diff --git a/.fvm/fvm_config.json b/.fvm/fvm_config.json
index d8abe1b9..1726d93d 100644
--- a/.fvm/fvm_config.json
+++ b/.fvm/fvm_config.json
@@ -1,4 +1,3 @@
{
- "flutterSdkVersion": "3.13.9",
- "flavors": {}
+ "flutterSdkVersion": "3.13.9"
}
\ No newline at end of file
diff --git a/.fvmrc b/.fvmrc
new file mode 100644
index 00000000..6108f14a
--- /dev/null
+++ b/.fvmrc
@@ -0,0 +1,4 @@
+{
+ "flutter": "3.13.9",
+ "flavors": {}
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 1be2d875..7040cb04 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,4 +46,6 @@ app.*.map.json
/android/app/release
# fvm
-.fvm/flutter_sdk
\ No newline at end of file
+
+# FVM Version Cache
+.fvm/
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index f285aa4a..2951c6d3 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,9 +1,9 @@
{
- "dart.flutterSdkPath": ".fvm/flutter_sdk",
- "search.exclude": {
- "**/.fvm": true
- },
- "files.watcherExclude": {
- "**/.fvm": true
- }
+ "dart.flutterSdkPath": ".fvm/versions/3.13.9",
+ "search.exclude": {
+ "**/.fvm": true
+ },
+ "files.watcherExclude": {
+ "**/.fvm": true
+ }
}
\ No newline at end of file
diff --git a/assets/svg/circle_green.svg b/assets/svg/circle_green.svg
new file mode 100644
index 00000000..5900787d
--- /dev/null
+++ b/assets/svg/circle_green.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/svg/circle_red.svg b/assets/svg/circle_red.svg
new file mode 100644
index 00000000..9b4f05dc
--- /dev/null
+++ b/assets/svg/circle_red.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/svg/star.svg b/assets/svg/star.svg
new file mode 100644
index 00000000..87f75959
--- /dev/null
+++ b/assets/svg/star.svg
@@ -0,0 +1,3 @@
+
diff --git a/fonts/Lora-Bold.ttf b/fonts/Lora-Bold.ttf
new file mode 100644
index 00000000..530c9e11
Binary files /dev/null and b/fonts/Lora-Bold.ttf differ
diff --git a/fonts/Lora-Medium.ttf b/fonts/Lora-Medium.ttf
new file mode 100644
index 00000000..85ca5a27
Binary files /dev/null and b/fonts/Lora-Medium.ttf differ
diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig
index 592ceee8..ec97fc6f 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 592ceee8..c4855bfe 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 00000000..c236dc0f
--- /dev/null
+++ b/ios/Podfile
@@ -0,0 +1,44 @@
+# Uncomment this line to define a global platform for your project
+platform :ios, '14.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__))
+ # target 'RunnerTests' do
+ # inherit! :search_paths
+ # end
+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 00000000..68c86a02
--- /dev/null
+++ b/ios/Podfile.lock
@@ -0,0 +1,23 @@
+PODS:
+ - Flutter (1.0.0)
+ - shared_preferences_foundation (0.0.1):
+ - Flutter
+ - FlutterMacOS
+
+DEPENDENCIES:
+ - Flutter (from `Flutter`)
+ - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
+
+EXTERNAL SOURCES:
+ Flutter:
+ :path: Flutter
+ shared_preferences_foundation:
+ :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
+
+SPEC CHECKSUMS:
+ Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
+ shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
+
+PODFILE CHECKSUM: e60e17f8bfffff789408fce3f968c37c5c63400e
+
+COCOAPODS: 1.13.0
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 73cf3f6d..f1eabcab 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
+ 00B07F0BFD93F3FE48E32742 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4F3E3C2A00DC08E71DE335F5 /* Pods_Runner.framework */; };
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
@@ -32,6 +33,8 @@
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 = ""; };
+ 4F3E3C2A00DC08E71DE335F5 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 68833FFF5C840B3F5CAEF635 /* 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 = ""; };
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 = ""; };
@@ -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 = ""; };
+ BE1F7C8AF7B78E15497D78F9 /* 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 = ""; };
+ EF7E9E4C13D86896204E5096 /* 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 = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -49,12 +54,24 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 00B07F0BFD93F3FE48E32742 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 73CA00C62D685E39639EBEBF /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ EF7E9E4C13D86896204E5096 /* Pods-Runner.debug.xcconfig */,
+ 68833FFF5C840B3F5CAEF635 /* Pods-Runner.release.xcconfig */,
+ BE1F7C8AF7B78E15497D78F9 /* Pods-Runner.profile.xcconfig */,
+ );
+ name = Pods;
+ path = Pods;
+ sourceTree = "";
+ };
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
@@ -72,6 +89,8 @@
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
+ 73CA00C62D685E39639EBEBF /* Pods */,
+ D8E6A30210B33D79B7F7D21C /* Frameworks */,
);
sourceTree = "";
};
@@ -98,6 +117,14 @@
path = Runner;
sourceTree = "";
};
+ D8E6A30210B33D79B7F7D21C /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 4F3E3C2A00DC08E71DE335F5 /* Pods_Runner.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -105,12 +132,14 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
+ 096D77EFA5C68EB72EFCEE97 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ 82260A138AA466E24D5D6B67 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -169,6 +198,28 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
+ 096D77EFA5C68EB72EFCEE97 /* [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;
alwaysOutOfDate = 1;
@@ -185,6 +236,23 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
+ 82260A138AA466E24D5D6B67 /* [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;
alwaysOutOfDate = 1;
diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata
index 1d526a16..21a3cc14 100644
--- a/ios/Runner.xcworkspace/contents.xcworkspacedata
+++ b/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -4,4 +4,7 @@
+
+
diff --git a/lib/data/constants.dart b/lib/data/constants.dart
new file mode 100644
index 00000000..4b7d77b1
--- /dev/null
+++ b/lib/data/constants.dart
@@ -0,0 +1,45 @@
+class Urls {
+ static const String baseUrl = 'https://api.yelp.com';
+ static const String apiKey =
+ 'wfYIpeyetAPJbQYg5ITUE4wxzqCvoEQM5FQyW9Xq4SGJG52vkefWY_Irq9yg_TKpXRYJUgTO48W_fVXReEABY919sT74bHoCAyNH4b0kTe94rmEWFWNo1GjFxUXjZXYx';
+ static const String ghrapQLRoute = '/v3/graphql';
+ static String getRestaurantsByCity({
+ required String city,
+ required int limit,
+ required int offset,
+ }) =>
+ '''
+query getRestaurants {
+ search(location: "$city", limit: $limit, offset: $offset) {
+ total
+ business {
+ id
+ name
+ price
+ rating
+ photos
+ reviews {
+ id
+ rating
+ user {
+ id
+ image_url
+ name
+ }
+ text
+ }
+ categories {
+ title
+ alias
+ }
+ hours {
+ is_open_now
+ }
+ location {
+ formatted_address
+ }
+ }
+ }
+}
+''';
+}
diff --git a/lib/data/datasources/local_data_source.dart b/lib/data/datasources/local_data_source.dart
new file mode 100644
index 00000000..d67340a8
--- /dev/null
+++ b/lib/data/datasources/local_data_source.dart
@@ -0,0 +1,35 @@
+import 'package:shared_preferences/shared_preferences.dart';
+
+abstract class LocalDataSource {
+ Future> getFavorites();
+ Future addFavorite(String id);
+ Future removeFavorite(String id);
+}
+
+const key = 'favorites';
+
+class LocalDataSourceImpl implements LocalDataSource {
+ @override
+ Future addFavorite(String id) async {
+ final sharedPreferences = await SharedPreferences.getInstance();
+ final favorites = await getFavorites();
+ if (favorites.contains(id)) return;
+ favorites.add(id);
+ sharedPreferences.setStringList(key, favorites);
+ }
+
+ @override
+ Future> getFavorites() async {
+ final sharedPreferences = await SharedPreferences.getInstance();
+ final favorites = sharedPreferences.getStringList(key);
+ return favorites ?? [];
+ }
+
+ @override
+ Future removeFavorite(String id) async {
+ final sharedPreferences = await SharedPreferences.getInstance();
+ final favorites = await getFavorites();
+ favorites.remove(id);
+ sharedPreferences.setStringList(key, favorites);
+ }
+}
diff --git a/lib/data/datasources/remote_data_source.dart b/lib/data/datasources/remote_data_source.dart
new file mode 100644
index 00000000..47e9cf2d
--- /dev/null
+++ b/lib/data/datasources/remote_data_source.dart
@@ -0,0 +1,45 @@
+import 'dart:convert';
+
+import 'package:dio/dio.dart';
+import 'package:logger/logger.dart';
+import 'package:restaurantour/data/constants.dart';
+import 'package:restaurantour/data/models/restaurant.dart';
+
+abstract class RemoteDataSource {
+ Future getRestaurants();
+}
+
+class RemoteDataSourceImpl implements RemoteDataSource {
+ RemoteDataSourceImpl()
+ : dio = Dio(
+ BaseOptions(
+ baseUrl: Urls.baseUrl,
+ headers: {
+ 'Authorization': 'Bearer ${Urls.apiKey}',
+ 'Content-Type': 'application/graphql',
+ },
+ ),
+ );
+
+ late Dio dio;
+
+ @override
+ Future getRestaurants({int offset = 0}) async {
+ try {
+ final response = await dio.post