diff --git a/Tajawal/OFL.txt b/Tajawal/OFL.txt
new file mode 100755
index 0000000..8cdc822
--- /dev/null
+++ b/Tajawal/OFL.txt
@@ -0,0 +1,93 @@
+Copyright 2018 Boutros International. (http://www.boutrosfonts.com)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/Tajawal/Tajawal-Black.ttf b/Tajawal/Tajawal-Black.ttf
new file mode 100755
index 0000000..f9a40b5
Binary files /dev/null and b/Tajawal/Tajawal-Black.ttf differ
diff --git a/Tajawal/Tajawal-Bold.ttf b/Tajawal/Tajawal-Bold.ttf
new file mode 100755
index 0000000..081ac58
Binary files /dev/null and b/Tajawal/Tajawal-Bold.ttf differ
diff --git a/Tajawal/Tajawal-ExtraBold.ttf b/Tajawal/Tajawal-ExtraBold.ttf
new file mode 100755
index 0000000..2904c3c
Binary files /dev/null and b/Tajawal/Tajawal-ExtraBold.ttf differ
diff --git a/Tajawal/Tajawal-ExtraLight.ttf b/Tajawal/Tajawal-ExtraLight.ttf
new file mode 100755
index 0000000..a3e2686
Binary files /dev/null and b/Tajawal/Tajawal-ExtraLight.ttf differ
diff --git a/Tajawal/Tajawal-Light.ttf b/Tajawal/Tajawal-Light.ttf
new file mode 100755
index 0000000..dfc9d93
Binary files /dev/null and b/Tajawal/Tajawal-Light.ttf differ
diff --git a/Tajawal/Tajawal-Medium.ttf b/Tajawal/Tajawal-Medium.ttf
new file mode 100755
index 0000000..4176ac1
Binary files /dev/null and b/Tajawal/Tajawal-Medium.ttf differ
diff --git a/Tajawal/Tajawal-Regular.ttf b/Tajawal/Tajawal-Regular.ttf
new file mode 100755
index 0000000..747f56c
Binary files /dev/null and b/Tajawal/Tajawal-Regular.ttf differ
diff --git a/fonts/Tajawal/OFL.txt b/fonts/Tajawal/OFL.txt
new file mode 100755
index 0000000..8cdc822
--- /dev/null
+++ b/fonts/Tajawal/OFL.txt
@@ -0,0 +1,93 @@
+Copyright 2018 Boutros International. (http://www.boutrosfonts.com)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/fonts/Tajawal/Tajawal-Black.ttf b/fonts/Tajawal/Tajawal-Black.ttf
new file mode 100755
index 0000000..f9a40b5
Binary files /dev/null and b/fonts/Tajawal/Tajawal-Black.ttf differ
diff --git a/fonts/Tajawal/Tajawal-Bold.ttf b/fonts/Tajawal/Tajawal-Bold.ttf
new file mode 100755
index 0000000..081ac58
Binary files /dev/null and b/fonts/Tajawal/Tajawal-Bold.ttf differ
diff --git a/fonts/Tajawal/Tajawal-ExtraBold.ttf b/fonts/Tajawal/Tajawal-ExtraBold.ttf
new file mode 100755
index 0000000..2904c3c
Binary files /dev/null and b/fonts/Tajawal/Tajawal-ExtraBold.ttf differ
diff --git a/fonts/Tajawal/Tajawal-ExtraLight.ttf b/fonts/Tajawal/Tajawal-ExtraLight.ttf
new file mode 100755
index 0000000..a3e2686
Binary files /dev/null and b/fonts/Tajawal/Tajawal-ExtraLight.ttf differ
diff --git a/fonts/Tajawal/Tajawal-Light.ttf b/fonts/Tajawal/Tajawal-Light.ttf
new file mode 100755
index 0000000..dfc9d93
Binary files /dev/null and b/fonts/Tajawal/Tajawal-Light.ttf differ
diff --git a/fonts/Tajawal/Tajawal-Medium.ttf b/fonts/Tajawal/Tajawal-Medium.ttf
new file mode 100755
index 0000000..4176ac1
Binary files /dev/null and b/fonts/Tajawal/Tajawal-Medium.ttf differ
diff --git a/fonts/Tajawal/Tajawal-Regular.ttf b/fonts/Tajawal/Tajawal-Regular.ttf
new file mode 100755
index 0000000..747f56c
Binary files /dev/null and b/fonts/Tajawal/Tajawal-Regular.ttf differ
diff --git a/images/pexels-photo-9519451-2.jpg b/images/pexels-photo-9519451-2.jpg
new file mode 100644
index 0000000..41f833b
Binary files /dev/null and b/images/pexels-photo-9519451-2.jpg differ
diff --git a/images/undraw_Login_re_4vu2.png b/images/undraw_Login_re_4vu2.png
new file mode 100644
index 0000000..dc8ca92
Binary files /dev/null and b/images/undraw_Login_re_4vu2.png differ
diff --git a/images/undraw_Login_re_4vu2.svg b/images/undraw_Login_re_4vu2.svg
new file mode 100644
index 0000000..7a10186
--- /dev/null
+++ b/images/undraw_Login_re_4vu2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig
index 592ceee..ec97fc6 100644
--- a/ios/Flutter/Debug.xcconfig
+++ b/ios/Flutter/Debug.xcconfig
@@ -1 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig
index 592ceee..c4855bf 100644
--- a/ios/Flutter/Release.xcconfig
+++ b/ios/Flutter/Release.xcconfig
@@ -1 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
diff --git a/ios/Podfile b/ios/Podfile
new file mode 100644
index 0000000..1e8c3c9
--- /dev/null
+++ b/ios/Podfile
@@ -0,0 +1,41 @@
+# Uncomment this line to define a global platform for your project
+# platform :ios, '9.0'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
+ end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
+ end
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_ios_podfile_setup
+
+target 'Runner' do
+ use_frameworks!
+ use_modular_headers!
+
+ flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ flutter_additional_ios_build_settings(target)
+ end
+end
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
new file mode 100644
index 0000000..886f78a
--- /dev/null
+++ b/ios/Podfile.lock
@@ -0,0 +1,22 @@
+PODS:
+ - Flutter (1.0.0)
+ - shared_preferences (0.0.1):
+ - Flutter
+
+DEPENDENCIES:
+ - Flutter (from `Flutter`)
+ - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
+
+EXTERNAL SOURCES:
+ Flutter:
+ :path: Flutter
+ shared_preferences:
+ :path: ".symlinks/plugins/shared_preferences/ios"
+
+SPEC CHECKSUMS:
+ Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
+ shared_preferences: 5033afbb22d372e15aff8ff766df9021b845f273
+
+PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
+
+COCOAPODS: 1.10.2
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 5d430f2..903d0f1 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 46;
+ objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
@@ -13,6 +13,7 @@
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+ DE49AD1A013D3C298B45A1B4 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6051573150344A39C7B7524F /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -32,9 +33,11 @@
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
+ 6051573150344A39C7B7524F /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
+ 8150EADE7D303C515FD17674 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -42,6 +45,8 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ B6E8D6F7F6EEA526A7D951AE /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
+ CD96E550EAE76FE76C208040 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -49,12 +54,21 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ DE49AD1A013D3C298B45A1B4 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 67CCA216A8AB9F87A559F141 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 6051573150344A39C7B7524F /* Pods_Runner.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
@@ -72,6 +86,8 @@
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
+ CD7695917E693457953DD8EE /* Pods */,
+ 67CCA216A8AB9F87A559F141 /* Frameworks */,
);
sourceTree = "";
};
@@ -98,6 +114,17 @@
path = Runner;
sourceTree = "";
};
+ CD7695917E693457953DD8EE /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ 8150EADE7D303C515FD17674 /* Pods-Runner.debug.xcconfig */,
+ B6E8D6F7F6EEA526A7D951AE /* Pods-Runner.release.xcconfig */,
+ CD96E550EAE76FE76C208040 /* Pods-Runner.profile.xcconfig */,
+ );
+ name = Pods;
+ path = Pods;
+ sourceTree = "";
+ };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -105,12 +132,14 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
+ 0AAC64BE24CA53DD76CD6685 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ 4F5BF263DC90EE1A09E0EE3A /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -127,7 +156,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 1020;
+ LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
@@ -169,6 +198,28 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
+ 0AAC64BE24CA53DD76CD6685 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -183,6 +234,23 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
+ 4F5BF263DC90EE1A09E0EE3A /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index a28140c..3db53b6 100644
--- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -1,6 +1,6 @@
+
+
diff --git a/ios/build/Pods.build/Release-iphonesimulator/Flutter.build/dgph b/ios/build/Pods.build/Release-iphonesimulator/Flutter.build/dgph
new file mode 100644
index 0000000..b39bcba
Binary files /dev/null and b/ios/build/Pods.build/Release-iphonesimulator/Flutter.build/dgph differ
diff --git a/ios/build/Pods.build/Release-iphonesimulator/Pods-Runner.build/dgph b/ios/build/Pods.build/Release-iphonesimulator/Pods-Runner.build/dgph
new file mode 100644
index 0000000..b39bcba
Binary files /dev/null and b/ios/build/Pods.build/Release-iphonesimulator/Pods-Runner.build/dgph differ
diff --git a/ios/build/Pods.build/Release-iphonesimulator/shared_preferences.build/dgph b/ios/build/Pods.build/Release-iphonesimulator/shared_preferences.build/dgph
new file mode 100644
index 0000000..b39bcba
Binary files /dev/null and b/ios/build/Pods.build/Release-iphonesimulator/shared_preferences.build/dgph differ
diff --git a/lib/const/data.dart b/lib/const/data.dart
new file mode 100644
index 0000000..1b8b954
--- /dev/null
+++ b/lib/const/data.dart
@@ -0,0 +1,12 @@
+import 'package:contacts/main.dart';
+
+var list = List.generate(
+ 20,
+ (index) => Contact(
+ image:
+ 'https://www.pixsy.com/wp-content/uploads/2021/04/ben-sweet-2LowviVHZ-E-unsplash-1.jpeg',
+ name: 'Noaman Monther',
+ number: '0780xxxxxxx',
+ date: '2021/10/1',
+ isIncome: index.isEven ? true : false),
+);
diff --git a/lib/data.dart b/lib/data.dart
new file mode 100644
index 0000000..9f78946
--- /dev/null
+++ b/lib/data.dart
@@ -0,0 +1,77 @@
+
+import 'models/contact.dart';
+
+var contacts = [
+ Contact(
+ 'https://i.pravatar.cc/300',
+ 'Ahmed',
+ '71766137347',
+ DateTime.now().add(
+ const Duration(seconds: 3),
+ ),
+ true,
+ ),
+ Contact(
+ 'https://i.pravatar.cc/301',
+ 'Ali',
+ '71766137347',
+ DateTime.now().add(
+ const Duration(days: 1),
+ ),
+ false,
+ ),
+ Contact(
+ 'https://i.pravatar.cc/302',
+ 'Kamal',
+ '71766137347',
+ DateTime.now().add(
+ const Duration(days: 3),
+ ),
+ true,
+ ),
+ Contact(
+ 'https://i.pravatar.cc/303',
+ 'Mohammad',
+ '71766137347',
+ DateTime.now().add(
+ const Duration(days: 5),
+ ),
+ true,
+ ),
+ Contact(
+ 'https://i.pravatar.cc/304',
+ 'Mohammad',
+ '71766137347',
+ DateTime.now().add(
+ const Duration(days: 5),
+ ),
+ false,
+ ),
+ Contact(
+ 'https://i.pravatar.cc/305',
+ 'Hussein',
+ '71766137347',
+ DateTime.now().add(
+ const Duration(days: 6),
+ ),
+ false,
+ ),
+ Contact(
+ 'https://i.pravatar.cc/306',
+ 'Aboud',
+ '71766137347',
+ DateTime.now().add(
+ const Duration(days: 7),
+ ),
+ false,
+ ),
+ Contact(
+ 'https://i.pravatar.cc/307',
+ 'Osama',
+ '71766137347',
+ DateTime.now().add(
+ const Duration(days: 6),
+ ),
+ false,
+ ),
+];
\ No newline at end of file
diff --git a/lib/login_page.dart b/lib/login_page.dart
new file mode 100644
index 0000000..192556d
--- /dev/null
+++ b/lib/login_page.dart
@@ -0,0 +1,77 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/svg.dart';
+
+class LoginPage extends StatefulWidget {
+ @override
+ State createState() => _LoginPageState();
+}
+
+class _LoginPageState extends State {
+ var emailTextController = TextEditingController();
+ var passwordTextController = TextEditingController();
+
+ @override
+ Widget build(BuildContext context) {
+
+ var headerImage = SvgPicture.asset(
+ 'images/undraw_Login_re_4vu2.svg',
+ height: 200,
+ semanticsLabel: 'Acme Logo'
+ );
+
+ return ListView(
+ children: [
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 30),
+ child: headerImage,
+ ),
+ Container(height: 30,),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 20),
+ child: Card(
+ shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ) ,
+ elevation: 0,
+ child: Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: TextField(
+ textAlign: TextAlign.center,
+ controller: emailTextController,
+ decoration: InputDecoration(
+ hintText: 'Email'
+ ),
+ ),
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 20),
+ child: Card(
+ elevation: 0,
+ shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ) ,
+ child: Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: TextField(
+ textAlign: TextAlign.center,
+ controller: passwordTextController,
+ decoration: InputDecoration(
+ hintText: 'Password'
+ ),
+ ),
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 20),
+ child: ElevatedButton(onPressed: () {
+ if (emailTextController.text.isEmpty) {
+ debugPrint('emailTextController.text is empty...');
+ }
+ debugPrint('emailTextController.text: ${emailTextController.text}');
+ debugPrint('passwordTextController.text: ${passwordTextController.text}');
+ }, child: Text('Login')),
+ )
+ ],
+ );
+ }
+}
diff --git a/lib/main.dart b/lib/main.dart
index 2c084ed..1c2d4d0 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,17 +1,16 @@
+import 'package:contacts_01/models/users_viewmodel.dart';
+import 'package:contacts_01/ui/favorites_page.dart';
import 'package:flutter/material.dart';
-import 'dart:math' as math;
-void main() {
- runApp(const MyApp());
-}
+import 'package:provider/provider.dart';
-class Contact {
- String image;
- String name;
- String mobileNumber;
- DateTime date;
- bool isIncoming;
+import 'models/loading_state.dart';
+import 'models/user.dart';
- Contact(this.image, this.name, this.mobileNumber, this.date, this.isIncoming);
+void main() {
+ runApp(ChangeNotifierProvider(
+ create: (context) => UsersViewModel(),
+ child: const MyApp(),
+ ));
}
class MyApp extends StatelessWidget {
@@ -20,12 +19,12 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
- title: 'Flutter Demo 2',
+ title: 'Notes App',
theme: ThemeData(
- primarySwatch: Colors.blue,
+ primarySwatch: Colors.amber,
),
debugShowCheckedModeBanner: false,
- home: const MyHomePage(title: 'Contacts App'),
+ home: const MyHomePage(title: 'Saving/Restoring Counter'),
);
}
}
@@ -40,216 +39,106 @@ class MyHomePage extends StatefulWidget {
}
class _MyHomePageState extends State {
- int _selectedIndex = 2;
- static const TextStyle optionStyle =
- TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
- static late List _pages;
+ @override
+ void initState() {
+ debugPrint('initState...');
+ debugPrint('getSavedCounter called...');
- _MyHomePageState() {
- _pages = [
- buildContactsList(),
- buildFavoritesGridView(),
- // Text('hello'),
- Text(
- 'Index 2: School',
- style: optionStyle,
- ),
- ];
- }
+ Future.microtask(() => context.read().fetchUsers());
- void _onItemTapped(int index) {
- setState(() {
- _selectedIndex = index;
- });
+ super.initState();
}
- var contacts = [
- Contact(
- 'https://i.pravatar.cc/300',
- 'Ahmed',
- '71766137347',
- DateTime.now().add(
- const Duration(seconds: 3),
- ),
- true,
- ),
- Contact(
- 'https://i.pravatar.cc/301',
- 'Ali',
- '71766137347',
- DateTime.now().add(
- const Duration(days: 1),
- ),
- false,
- ),
- Contact(
- 'https://i.pravatar.cc/302',
- 'Kamal',
- '71766137347',
- DateTime.now().add(
- const Duration(days: 3),
- ),
- true,
- ),
- Contact(
- 'https://i.pravatar.cc/303',
- 'Mohammad',
- '71766137347',
- DateTime.now().add(
- const Duration(days: 5),
- ),
- true,
- ),
- Contact(
- 'https://i.pravatar.cc/304',
- 'Mohammad',
- '71766137347',
- DateTime.now().add(
- const Duration(days: 5),
- ),
- false,
- ),
- Contact(
- 'https://i.pravatar.cc/305',
- 'Hussein',
- '71766137347',
- DateTime.now().add(
- const Duration(days: 6),
- ),
- false,
- ),
- Contact(
- 'https://i.pravatar.cc/306',
- 'Aboud',
- '71766137347',
- DateTime.now().add(
- const Duration(days: 7),
- ),
- false,
- ),
- Contact(
- 'https://i.pravatar.cc/307',
- 'Osama',
- '71766137347',
- DateTime.now().add(
- const Duration(days: 6),
- ),
- false,
- ),
- ];
-
- Widget buildFavoritesGridView() {
- return Column(
- children: [
- Text('Favorites'),
- Divider(thickness: 4,),
- Expanded(
- child: GridView.count(
- crossAxisCount: 3,
- children: List.generate(5, (index) {
- var personColor = Color((math.Random().nextDouble() * 0xFFFFFF).toInt())
- .withOpacity(1.0);
- return Center(
- child: Container(
- width: 120,
- height: 120,
- child: Text(
- contacts[index].name[0],
- style: TextStyle(fontSize: 40),
- ),
- alignment: Alignment.center,
- decoration:
- BoxDecoration(shape: BoxShape.circle, color: personColor),
+ Widget buildUserView(User user) {
+ return Consumer(
+ builder: (context, favoritesViewModel, child) {
+ return Card(
+ child: Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: Row(
+ children: [
+ Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: Text(user.name),
+ ),
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: Text(user.phone),
),
- );
- }),
- ),
- ),
- ],
- );
- }
-
- Widget buildContactItem(Contact _contact) {
- return Card(
- child: Padding(
- padding: const EdgeInsets.all(8.0),
- child: Row(
- children: [
- CircleAvatar(
- backgroundImage: NetworkImage(_contact.image),
- ),
- Padding(
- padding: const EdgeInsets.all(16),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.start,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- _contact.name,
- style: const TextStyle(fontWeight: FontWeight.bold),
- ),
- Text(_contact.mobileNumber),
- ],
),
- ),
- Text(_contact.date.toIso8601String().split('T').first),
- Expanded(
- child: Container(),
- ),
- if (_contact.isIncoming)
- Icon(
- Icons.arrow_downward,
- color: Colors.red,
- )
- else
- Icon(
- Icons.arrow_upward,
- color: Colors.green,
- )
- ],
+ !favoritesViewModel.favorites.contains(user)
+ ? IconButton(
+ icon: Icon(Icons.add),
+ onPressed: () {
+ // var favoritesViewModel = context.read();
+ var favoritesViewModel =
+ Provider.of(context, listen: false);
+ favoritesViewModel.insert(user);
+ },
+ )
+ : Icon(Icons.done),
+ ],
+ ),
),
- ),
- );
+ );
+ });
}
- Widget buildContactsList() {
+ Widget buildUsersListView(List users) {
+ if (users.isEmpty) {
+ return Center(child: Text('Empty!'));
+ }
return ListView.builder(
- itemBuilder: (_context, index) {
- return buildContactItem(contacts[index]);
- },
- itemCount: contacts.length,
+ itemBuilder: (_context, index) => buildUserView(
+ users[index],
+ ),
+ itemCount: users.length,
);
}
@override
Widget build(BuildContext context) {
+ debugPrint('build...');
+
return Scaffold(
appBar: AppBar(
- title: Text(widget.title),
- ),
- body: Center(
- child: _pages[_selectedIndex],
- ),
- bottomNavigationBar: BottomNavigationBar(
- items: const [
- BottomNavigationBarItem(
- icon: Icon(Icons.home),
- label: 'Recent',
- ),
- BottomNavigationBarItem(
- icon: Icon(Icons.favorite),
- label: 'Favorites',
- ),
- BottomNavigationBarItem(
- icon: Icon(Icons.access_time_outlined),
- label: 'School',
- activeIcon: Icon(Icons.access_time_filled)
+ title: Text(
+ widget.title,
+ ),
+ actions: [
+ IconButton(
+ onPressed: () => Navigator.of(context).push(
+ MaterialPageRoute(builder: (_context) => FavoritesPage())),
+ icon: Consumer(
+ builder: (context, favoritesViewModel, child) => Text(
+ '${favoritesViewModel.favorites.length}',
+ style: TextStyle(
+ fontSize: 12,
+ ),
+ ),
+ ),
),
],
- currentIndex: _selectedIndex,
- selectedItemColor: Colors.amber[800],
- onTap: _onItemTapped,
),
+ body: Center(child: Consumer(
+ builder: (context, usersViewModel, child) {
+ debugPrint('building users list..');
+ debugPrint(
+ 'usersViewModel.loadingState: ${usersViewModel.loadingState}');
+
+ if (usersViewModel.loadingState == LoadingState.loading) {
+ return const Center(child: CircularProgressIndicator());
+ } else if (usersViewModel.usersResponse.item1 != null) {
+ return Center(
+ child: Text(
+ '${usersViewModel.usersResponse.item1?.message}',
+ style: TextStyle(fontSize: 20),
+ ));
+ }
+ return buildUsersListView(usersViewModel.usersResponse.item2 ?? []);
+ },
+ )),
);
}
}
diff --git a/lib/models/contact.dart b/lib/models/contact.dart
new file mode 100644
index 0000000..31b50e2
--- /dev/null
+++ b/lib/models/contact.dart
@@ -0,0 +1,10 @@
+
+class Contact {
+ String image;
+ String name;
+ String mobileNumber;
+ DateTime date;
+ bool isIncoming;
+
+ Contact(this.image, this.name, this.mobileNumber, this.date, this.isIncoming);
+}
\ No newline at end of file
diff --git a/lib/models/error.dart b/lib/models/error.dart
new file mode 100644
index 0000000..d4eedcf
--- /dev/null
+++ b/lib/models/error.dart
@@ -0,0 +1,5 @@
+class ErrorResponse {
+ ErrorResponse(this.message, this.statusCode);
+ String message;
+ int statusCode;
+}
\ No newline at end of file
diff --git a/lib/models/loading_state.dart b/lib/models/loading_state.dart
new file mode 100644
index 0000000..7cb60b2
--- /dev/null
+++ b/lib/models/loading_state.dart
@@ -0,0 +1,5 @@
+enum LoadingState {
+ idle,
+ loading,
+ finished
+}
\ No newline at end of file
diff --git a/lib/models/note.dart b/lib/models/note.dart
new file mode 100644
index 0000000..4cac359
--- /dev/null
+++ b/lib/models/note.dart
@@ -0,0 +1,4 @@
+class NoteModel {
+ NoteModel(this.content);
+ String content;
+}
\ No newline at end of file
diff --git a/lib/models/user.dart b/lib/models/user.dart
new file mode 100644
index 0000000..3b5c2bc
--- /dev/null
+++ b/lib/models/user.dart
@@ -0,0 +1,130 @@
+// To parse this JSON data, do
+//
+// final user = userFromJson(jsonString);
+
+import 'package:meta/meta.dart';
+import 'dart:convert';
+
+User userFromJson(String str) => User.fromJson(json.decode(str));
+
+String userToJson(User data) => json.encode(data.toJson());
+
+class User {
+ User({
+ required this.id,
+ required this.name,
+ required this.username,
+ required this.email,
+ required this.address,
+ required this.phone,
+ required this.website,
+ required this.company,
+ });
+
+ int id;
+ String name;
+ String username;
+ String email;
+ Address address;
+ String phone;
+ String website;
+ Company company;
+
+ factory User.fromJson(Map json) => User(
+ id: json["id"],
+ name: json["name"],
+ username: json["username"],
+ email: json["email"],
+ address: Address.fromJson(json["address"]),
+ phone: json["phone"],
+ website: json["website"],
+ company: Company.fromJson(json["company"]),
+ );
+
+ Map toJson() => {
+ "id": id,
+ "name": name,
+ "username": username,
+ "email": email,
+ "address": address.toJson(),
+ "phone": phone,
+ "website": website,
+ "company": company.toJson(),
+ };
+}
+
+class Address {
+ Address({
+ required this.street,
+ required this.suite,
+ required this.city,
+ required this.zipcode,
+ required this.geo,
+ });
+
+ String street;
+ String suite;
+ String city;
+ String zipcode;
+ Geo geo;
+
+ factory Address.fromJson(Map json) => Address(
+ street: json["street"],
+ suite: json["suite"],
+ city: json["city"],
+ zipcode: json["zipcode"],
+ geo: Geo.fromJson(json["geo"]),
+ );
+
+ Map toJson() => {
+ "street": street,
+ "suite": suite,
+ "city": city,
+ "zipcode": zipcode,
+ "geo": geo.toJson(),
+ };
+}
+
+class Geo {
+ Geo({
+ required this.lat,
+ required this.lng,
+ });
+
+ String lat;
+ String lng;
+
+ factory Geo.fromJson(Map json) => Geo(
+ lat: json["lat"],
+ lng: json["lng"],
+ );
+
+ Map toJson() => {
+ "lat": lat,
+ "lng": lng,
+ };
+}
+
+class Company {
+ Company({
+ required this.name,
+ required this.catchPhrase,
+ required this.bs,
+ });
+
+ String name;
+ String catchPhrase;
+ String bs;
+
+ factory Company.fromJson(Map json) => Company(
+ name: json["name"],
+ catchPhrase: json["catchPhrase"],
+ bs: json["bs"],
+ );
+
+ Map toJson() => {
+ "name": name,
+ "catchPhrase": catchPhrase,
+ "bs": bs,
+ };
+}
diff --git a/lib/models/users_viewmodel.dart b/lib/models/users_viewmodel.dart
new file mode 100644
index 0000000..b5f2a73
--- /dev/null
+++ b/lib/models/users_viewmodel.dart
@@ -0,0 +1,59 @@
+import 'dart:convert';
+
+import 'package:contacts_01/models/error.dart';
+import 'package:contacts_01/models/loading_state.dart';
+import 'package:contacts_01/models/user.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:http/http.dart' as http;
+import 'package:tuple/tuple.dart';
+
+class UsersViewModel extends ChangeNotifier {
+ List favorites = [];
+ late Tuple2?> usersResponse;
+ LoadingState loadingState = LoadingState.idle;
+
+ UsersViewModel() {
+ usersResponse = const Tuple2(null, null);
+ }
+
+ void insert(User user) {
+ favorites.add(user);
+ notifyListeners();
+ }
+
+ void fetchUsers() async {
+
+
+ loadingState = LoadingState.loading;
+ notifyListeners();
+
+ var response;
+ try {
+ var url = Uri.parse('https://jsonplaceholder.typicode.com/users');
+ debugPrint('url: ${url}');
+ response = await http.get(
+ url,
+ );
+ debugPrint('response: ${response}');
+ debugPrint('response.statusCode: ${response.statusCode}');
+ var jsonUsers = jsonDecode(response.body);
+ // jsonUsers[0]['name'] = null;
+ var users = jsonUsers.map((_userJson) => User.fromJson(_userJson)).toList();
+
+ // debugPrint('users.length: ${users.length}');
+ usersResponse = Tuple2(null, users);
+ notifyListeners();
+ } catch (e) {
+ if (response?.statusCode == 200) {
+ usersResponse = Tuple2(ErrorResponse('توجد مشكلة في عرض المعلومات', response?.statusCode), null);
+ } else {
+ usersResponse = Tuple2(ErrorResponse('لا يمكن الإتصال. يرجى المحاولة فيما بعد...', -1), null);
+ }
+
+ debugPrint('e: $e');
+ }
+
+ loadingState = LoadingState.finished;
+ notifyListeners();
+ }
+}
diff --git a/lib/screens/favorite.dart b/lib/screens/favorite.dart
new file mode 100644
index 0000000..d313cf7
--- /dev/null
+++ b/lib/screens/favorite.dart
@@ -0,0 +1,52 @@
+import 'dart:math';
+
+import 'package:contacts/const/data.dart';
+import 'package:flutter/material.dart';
+
+class Favorite extends StatelessWidget {
+ const Favorite({Key? key}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ Size size = MediaQuery.of(context).size;
+ final List