├── ios
├── Runner
│ ├── Runner-Bridging-Header.h
│ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ ├── 114.png
│ │ │ ├── 120.png
│ │ │ ├── 180.png
│ │ │ ├── 29.png
│ │ │ ├── 40.png
│ │ │ ├── 57.png
│ │ │ ├── 58.png
│ │ │ ├── 60.png
│ │ │ ├── 80.png
│ │ │ ├── 87.png
│ │ │ ├── 1024.png
│ │ │ └── Contents.json
│ ├── AppDelegate.swift
│ ├── Base.lproj
│ │ ├── Main.storyboard
│ │ └── LaunchScreen.storyboard
│ └── Info.plist
├── Flutter
│ ├── Debug.xcconfig
│ ├── Release.xcconfig
│ └── AppFrameworkInfo.plist
├── Runner.xcodeproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── WorkspaceSettings.xcsettings
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── WorkspaceSettings.xcsettings
│ │ └── IDEWorkspaceChecks.plist
├── .gitignore
├── Podfile
└── Podfile.lock
├── web
├── favicon.png
├── icons
│ ├── Icon-192.png
│ ├── Icon-512.png
│ ├── Icon-maskable-192.png
│ └── Icon-maskable-512.png
├── manifest.json
└── index.html
├── samples
├── screen_1.png
└── screen_2.png
├── android
├── gradle.properties
├── app
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── drawable
│ │ │ │ │ ├── app_icon.png
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── drawable-v21
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── values
│ │ │ │ │ └── styles.xml
│ │ │ │ └── values-night
│ │ │ │ │ └── styles.xml
│ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── example
│ │ │ │ │ └── food_rating_app
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── AndroidManifest.xml
│ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ └── profile
│ │ │ └── AndroidManifest.xml
│ └── build.gradle
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── .gitignore
├── settings.gradle
└── build.gradle
├── .metadata
├── lib
├── common
│ ├── navigation.dart
│ └── styles.dart
├── data
│ ├── api
│ │ ├── urls.dart
│ │ └── api_service.dart
│ ├── database
│ │ └── database_helper.dart
│ └── models
│ │ └── restaurant.dart
├── utilities
│ ├── date_time_helper.dart
│ ├── background_service.dart
│ └── notification_helper.dart
├── modules
│ ├── screens
│ │ ├── preferences
│ │ │ ├── preferences_helper.dart
│ │ │ └── settings_screen.dart
│ │ ├── restaurant
│ │ │ ├── widgets
│ │ │ │ ├── animation_placeholder.dart
│ │ │ │ └── restaurant_card.dart
│ │ │ ├── favorite_screen.dart
│ │ │ ├── list_screen.dart
│ │ │ ├── review_screen.dart
│ │ │ ├── search_screen.dart
│ │ │ └── detail_screen.dart
│ │ └── home_screen.dart
│ └── providers
│ │ ├── scheduling_provider.dart
│ │ ├── preferences_provider.dart
│ │ ├── database_provider.dart
│ │ └── restaurant_provider.dart
├── routes.dart
└── main.dart
├── .gitignore
├── pubspec.yaml
├── LICENSE
├── .github
└── workflows
│ └── dart.yml
├── analysis_options.yaml
├── README.md
├── test
└── provider
│ ├── restaurant_provider_test.mocks.dart
│ └── restaurant_provider_test.dart
└── assets
├── empty.json
├── done.json
├── not-found.json
└── no-internet.json
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/web/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/web/favicon.png
--------------------------------------------------------------------------------
/samples/screen_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/samples/screen_1.png
--------------------------------------------------------------------------------
/samples/screen_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/samples/screen_2.png
--------------------------------------------------------------------------------
/web/icons/Icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/web/icons/Icon-192.png
--------------------------------------------------------------------------------
/web/icons/Icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/web/icons/Icon-512.png
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/web/icons/Icon-maskable-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/web/icons/Icon-maskable-192.png
--------------------------------------------------------------------------------
/web/icons/Icon-maskable-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/web/icons/Icon-maskable-512.png
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/app_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/android/app/src/main/res/drawable/app_icon.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/114.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/120.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/180.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/29.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/40.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/57.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/58.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/60.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/80.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/87.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/87.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codestronaut/flutter-food-review-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/1024.png
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/example/food_rating_app/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.food_rating_app
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
7 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 18116933e77adc82f80866c928266a5b4f1ed645
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 | **/*.keystore
13 | **/*.jks
14 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/lib/common/navigation.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | final GlobalKey navigatorKey = GlobalKey();
4 |
5 | class Navigation {
6 | static intentWithData(String routeName, {Object? arguments}) {
7 | navigatorKey.currentState?.pushNamed(routeName, arguments: arguments);
8 | }
9 |
10 | static back() => navigatorKey.currentState?.pop();
11 | }
12 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/lib/data/api/urls.dart:
--------------------------------------------------------------------------------
1 | class Urls {
2 | static const String baseUrl = 'https://restaurant-api.dicoding.dev';
3 | static const String restaurantList = '/list';
4 | static String restaurantDetail(String id) => '/detail/$id';
5 | static const String searchRestaurant = '/search';
6 | static const String restraurantReview = '/review';
7 | static String restaurantImage(String pictureId) =>
8 | 'https://restaurant-api.dicoding.dev/images/small/$pictureId';
9 | static String largeRestaurantImage(String pictureId) =>
10 | 'https://restaurant-api.dicoding.dev/images/large/$pictureId';
11 | }
12 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.3.50'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:4.1.0'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | project.evaluationDependsOn(':app')
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | **/dgph
2 | *.mode1v3
3 | *.mode2v3
4 | *.moved-aside
5 | *.pbxuser
6 | *.perspectivev3
7 | **/*sync/
8 | .sconsign.dblite
9 | .tags*
10 | **/.vagrant/
11 | **/DerivedData/
12 | Icon?
13 | **/Pods/
14 | **/.symlinks/
15 | profile
16 | xcuserdata
17 | **/.generated/
18 | Flutter/App.framework
19 | Flutter/Flutter.framework
20 | Flutter/Flutter.podspec
21 | Flutter/Generated.xcconfig
22 | Flutter/ephemeral/
23 | Flutter/app.flx
24 | Flutter/app.zip
25 | Flutter/flutter_assets/
26 | Flutter/flutter_export_environment.sh
27 | ServiceDefinitions.json
28 | Runner/GeneratedPluginRegistrant.*
29 |
30 | # Exceptions to above rules.
31 | !default.mode1v3
32 | !default.mode2v3
33 | !default.pbxuser
34 | !default.perspectivev3
35 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 9.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Web related
35 | lib/generated_plugin_registrant.dart
36 |
37 | # Symbolication related
38 | app.*.symbols
39 |
40 | # Obfuscation related
41 | app.*.map.json
42 |
43 | # Android Studio will place build artifacts here
44 | /android/app/debug
45 | /android/app/profile
46 | /android/app/release
47 |
--------------------------------------------------------------------------------
/lib/utilities/date_time_helper.dart:
--------------------------------------------------------------------------------
1 | import 'package:intl/intl.dart';
2 |
3 | class DateTimeHelper {
4 | static DateTime format() {
5 | /// Date and time format
6 | final now = DateTime.now();
7 | final dateFormat = DateFormat('y/M/d');
8 | const timeSpecific = '11:00:00';
9 | final completeFormat = DateFormat('y/M/d H:m:s');
10 |
11 | /// Today format
12 | final todayDate = dateFormat.format(now);
13 | final todayDateAndTime = '$todayDate $timeSpecific';
14 | var resultToday = completeFormat.parseStrict(todayDateAndTime);
15 |
16 | /// Tomorrow format
17 | var formatted = resultToday.add(const Duration(days: 1));
18 | final tomorrowDate = dateFormat.format(formatted);
19 | final tomorrowDateAndTime = '$tomorrowDate $timeSpecific';
20 | var resultTomorrow = completeFormat.parseStrict(tomorrowDateAndTime);
21 |
22 | return now.isAfter(resultToday) ? resultTomorrow : resultToday;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: food_rating_app
2 | description: A new Flutter project.
3 |
4 | publish_to: "none"
5 |
6 | version: 1.0.0+1
7 |
8 | environment:
9 | sdk: ">=2.12.0 <3.0.0"
10 |
11 | dependencies:
12 | flutter:
13 | sdk: flutter
14 |
15 | android_alarm_manager: ^2.0.2
16 | animate_do: ^2.0.0
17 | cached_network_image: ^3.1.0+1
18 | cupertino_icons: ^1.0.2
19 | dio: ^4.0.4
20 | flutter_local_notifications: ^9.1.4
21 | flutter_spinkit: ^5.1.0
22 | provider: ^5.0.0
23 | google_fonts: ^2.1.0
24 | intl: ^0.17.0
25 | lottie: ^1.2.1
26 | persistent_bottom_nav_bar: ^4.0.2
27 | readmore: ^2.1.0
28 | rxdart: ^0.27.3
29 | shared_preferences: ^2.0.9
30 | styled_text: ^3.0.4+1
31 | sqflite: ^2.0.0+4
32 |
33 | dev_dependencies:
34 | flutter_test:
35 | sdk: flutter
36 |
37 | mockito: ^5.0.16
38 | http_mock_adapter: ^0.3.2
39 | flutter_lints: ^1.0.0
40 | build_runner: ^2.1.2
41 |
42 | flutter:
43 | uses-material-design: true
44 |
45 | assets:
46 | - assets/
47 |
--------------------------------------------------------------------------------
/lib/modules/screens/preferences/preferences_helper.dart:
--------------------------------------------------------------------------------
1 | import 'package:shared_preferences/shared_preferences.dart';
2 |
3 | class PreferencesHelper {
4 | final Future sharedPreferences;
5 | PreferencesHelper({required this.sharedPreferences});
6 |
7 | static const darkTheme = 'DARK_THEME';
8 |
9 | Future get isDarkTheme async {
10 | final prefs = await sharedPreferences;
11 | return prefs.getBool(darkTheme) ?? false;
12 | }
13 |
14 | void setDarkTheme(bool value) async {
15 | final prefs = await sharedPreferences;
16 | prefs.setBool(darkTheme, value);
17 | }
18 |
19 | static const dailyRestaurant = 'DAILY_RESTAURANT';
20 |
21 | Future get isDailyRestaurantActive async {
22 | final prefs = await sharedPreferences;
23 | return prefs.getBool(dailyRestaurant) ?? false;
24 | }
25 |
26 | void setDailyRestaurantActive(bool value) async {
27 | final prefs = await sharedPreferences;
28 | prefs.setBool(dailyRestaurant, value);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/modules/providers/scheduling_provider.dart:
--------------------------------------------------------------------------------
1 | import 'package:android_alarm_manager/android_alarm_manager.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:food_rating_app/utilities/background_service.dart';
4 | import 'package:food_rating_app/utilities/date_time_helper.dart';
5 |
6 | class SchedulingProvider extends ChangeNotifier {
7 | bool _isScheduled = false;
8 | bool get isScheduled => _isScheduled;
9 |
10 | Future scheduledDailyRestaurant(bool value) async {
11 | _isScheduled = value;
12 | if (_isScheduled) {
13 | print('Scheduling acitvated');
14 | notifyListeners();
15 | return await AndroidAlarmManager.periodic(
16 | const Duration(hours: 24),
17 | 1,
18 | BackgroundService.callback,
19 | startAt: DateTimeHelper.format(),
20 | exact: true,
21 | wakeup: true,
22 | );
23 | } else {
24 | print('Scheduling canceled');
25 | notifyListeners();
26 | return await AndroidAlarmManager.cancel(1);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/lib/routes.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:food_rating_app/modules/screens/home_screen.dart';
3 | import 'package:food_rating_app/modules/screens/restaurant/detail_screen.dart';
4 | import 'package:food_rating_app/modules/screens/restaurant/list_screen.dart';
5 | import 'package:food_rating_app/modules/screens/restaurant/review_screen.dart';
6 | import 'package:food_rating_app/modules/screens/restaurant/search_screen.dart';
7 |
8 | Map allRoute(BuildContext context) {
9 | return {
10 | HomeScreen.routeName: (context) => const HomeScreen(),
11 | ListScreen.routeName: (context) => const ListScreen(),
12 | DetailScreen.routeName: (context) => DetailScreen(
13 | id: ModalRoute.of(context)!.settings.arguments as String,
14 | ),
15 | ReviewScreen.routeName: (context) => ReviewScreen(
16 | id: ModalRoute.of(context)!.settings.arguments as String,
17 | ),
18 | SearchScreen.routeName: (context) => const SearchScreen()
19 |
20 | /// TODO: Add more later
21 | };
22 | }
23 |
--------------------------------------------------------------------------------
/web/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "food_rating_app",
3 | "short_name": "food_rating_app",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "background_color": "#0175C2",
7 | "theme_color": "#0175C2",
8 | "description": "A new Flutter project.",
9 | "orientation": "portrait-primary",
10 | "prefer_related_applications": false,
11 | "icons": [
12 | {
13 | "src": "icons/Icon-192.png",
14 | "sizes": "192x192",
15 | "type": "image/png"
16 | },
17 | {
18 | "src": "icons/Icon-512.png",
19 | "sizes": "512x512",
20 | "type": "image/png"
21 | },
22 | {
23 | "src": "icons/Icon-maskable-192.png",
24 | "sizes": "192x192",
25 | "type": "image/png",
26 | "purpose": "maskable"
27 | },
28 | {
29 | "src": "icons/Icon-maskable-512.png",
30 | "sizes": "512x512",
31 | "type": "image/png",
32 | "purpose": "maskable"
33 | }
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Aditya Rohman
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/lib/utilities/background_service.dart:
--------------------------------------------------------------------------------
1 | import 'dart:isolate';
2 |
3 | import 'dart:ui';
4 | import 'package:food_rating_app/main.dart';
5 | import 'package:food_rating_app/data/api/api_service.dart';
6 | import 'package:food_rating_app/utilities/notification_helper.dart';
7 |
8 | final ReceivePort port = ReceivePort();
9 |
10 | class BackgroundService {
11 | static BackgroundService? _instance;
12 | static const String _isolateName = 'isolate';
13 | static SendPort? _uiSendPort;
14 |
15 | BackgroundService._internal() {
16 | _instance = this;
17 | }
18 |
19 | factory BackgroundService() => _instance ?? BackgroundService._internal();
20 |
21 | void initializeIsolate() {
22 | IsolateNameServer.registerPortWithName(port.sendPort, _isolateName);
23 | }
24 |
25 | static Future callback() async {
26 | final NotificationHelper _notificationHelper = NotificationHelper();
27 | var result = await ApiService().getRestaurantList();
28 | await _notificationHelper.showNotification(
29 | flutterLocalNotificationsPlugin,
30 | result,
31 | );
32 |
33 | _uiSendPort ??= IsolateNameServer.lookupPortByName(_isolateName);
34 | _uiSendPort?.send(null);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lib/modules/providers/preferences_provider.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:food_rating_app/modules/screens/preferences/preferences_helper.dart';
3 | import 'package:food_rating_app/common/styles.dart';
4 |
5 | class PreferencesProvider extends ChangeNotifier {
6 | final PreferencesHelper preferencesHelper;
7 |
8 | bool _isDarkTheme = false;
9 | bool get isDarkTheme => _isDarkTheme;
10 |
11 | bool _isDailyRestaurantActive = false;
12 | bool get isDailyRestaurantActive => _isDailyRestaurantActive;
13 |
14 | PreferencesProvider({required this.preferencesHelper}) {
15 | _getTheme();
16 | _getDailyRestaurant();
17 | }
18 |
19 | void _getTheme() async {
20 | _isDarkTheme = await preferencesHelper.isDarkTheme;
21 | notifyListeners();
22 | }
23 |
24 | void _getDailyRestaurant() async {
25 | _isDailyRestaurantActive = await preferencesHelper.isDailyRestaurantActive;
26 | notifyListeners();
27 | }
28 |
29 | void enableDarkTheme(bool value) async {
30 | preferencesHelper.setDarkTheme(value);
31 | _getTheme();
32 | }
33 |
34 | void enableDailyRestaurant(bool value) async {
35 | preferencesHelper.setDailyRestaurantActive(value);
36 | _getDailyRestaurant();
37 | }
38 |
39 | ThemeData get themeData => _isDarkTheme ? darkTheme : lightTheme;
40 | }
41 |
--------------------------------------------------------------------------------
/.github/workflows/dart.yml:
--------------------------------------------------------------------------------
1 | # This workflow uses actions that are not certified by GitHub.
2 | # They are provided by a third-party and are governed by
3 | # separate terms of service, privacy policy, and support
4 | # documentation.
5 |
6 | name: Dart
7 |
8 | on:
9 | push:
10 | branches: [ main ]
11 | pull_request:
12 | branches: [ main ]
13 |
14 | jobs:
15 | build:
16 | runs-on: ubuntu-latest
17 |
18 | steps:
19 | - uses: actions/checkout@v2
20 |
21 | # Note: This workflow uses the latest stable version of the Dart SDK.
22 | # You can specify other versions if desired, see documentation here:
23 | # https://github.com/dart-lang/setup-dart/blob/main/README.md
24 | # - uses: dart-lang/setup-dart@v1
25 | - uses: dart-lang/setup-dart@9a04e6d73cca37bd455e0608d7e5092f881fd603
26 |
27 | - name: Install dependencies
28 | run: dart pub get
29 |
30 | # Uncomment this step to verify the use of 'dart format' on each commit.
31 | # - name: Verify formatting
32 | # run: dart format --output=none --set-exit-if-changed .
33 |
34 | # Consider passing '--fatal-infos' for slightly stricter analysis.
35 | - name: Analyze project source
36 | run: dart analyze
37 |
38 | # Your project will need to have tests in test/ and a dependency on
39 | # package:test for this step to succeed. Note that Flutter projects will
40 | # want to change this to 'flutter test'.
41 | - name: Run tests
42 | run: dart test
43 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '9.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | end
36 |
37 | post_install do |installer|
38 | installer.pods_project.targets.each do |target|
39 | flutter_additional_ios_build_settings(target)
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | linter:
13 | # The lint rules applied to this project can be customized in the
14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
15 | # included above or to enable additional rules. A list of all available lints
16 | # and their documentation is published at
17 | # https://dart-lang.github.io/linter/lints/index.html.
18 | #
19 | # Instead of disabling a lint rule for the entire project in the
20 | # section below, it can also be suppressed for a single line of code
21 | # or a specific dart file by using the `// ignore: name_of_lint` and
22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
23 | # producing the lint.
24 | rules:
25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
27 |
28 | # Additional information about this file can be found at
29 | # https://dart.dev/guides/language/analysis-options
30 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"}]}
--------------------------------------------------------------------------------
/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Flutter (1.0.0)
3 | - flutter_local_notifications (0.0.1):
4 | - Flutter
5 | - FMDB (2.7.5):
6 | - FMDB/standard (= 2.7.5)
7 | - FMDB/standard (2.7.5)
8 | - path_provider_ios (0.0.1):
9 | - Flutter
10 | - shared_preferences_ios (0.0.1):
11 | - Flutter
12 | - sqflite (0.0.2):
13 | - Flutter
14 | - FMDB (>= 2.7.5)
15 |
16 | DEPENDENCIES:
17 | - Flutter (from `Flutter`)
18 | - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
19 | - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
20 | - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`)
21 | - sqflite (from `.symlinks/plugins/sqflite/ios`)
22 |
23 | SPEC REPOS:
24 | trunk:
25 | - FMDB
26 |
27 | EXTERNAL SOURCES:
28 | Flutter:
29 | :path: Flutter
30 | flutter_local_notifications:
31 | :path: ".symlinks/plugins/flutter_local_notifications/ios"
32 | path_provider_ios:
33 | :path: ".symlinks/plugins/path_provider_ios/ios"
34 | shared_preferences_ios:
35 | :path: ".symlinks/plugins/shared_preferences_ios/ios"
36 | sqflite:
37 | :path: ".symlinks/plugins/sqflite/ios"
38 |
39 | SPEC CHECKSUMS:
40 | Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
41 | flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743
42 | FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
43 | path_provider_ios: 7d7ce634493af4477d156294792024ec3485acd5
44 | shared_preferences_ios: aef470a42dc4675a1cdd50e3158b42e3d1232b32
45 | sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
46 |
47 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
48 |
49 | COCOAPODS: 1.11.2
50 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | Food Review
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/lib/data/api/api_service.dart:
--------------------------------------------------------------------------------
1 | import 'package:dio/dio.dart';
2 | import 'package:food_rating_app/data/models/restaurant.dart';
3 | import 'package:food_rating_app/data/api/urls.dart';
4 |
5 | class ApiService {
6 | final Dio _client = Dio(
7 | BaseOptions(
8 | baseUrl: Urls.baseUrl,
9 | connectTimeout: 5000,
10 | receiveTimeout: 3000,
11 | contentType: Headers.formUrlEncodedContentType,
12 | ),
13 | );
14 |
15 | Future getRestaurantList() async {
16 | final response = await _client.get(Urls.restaurantList);
17 |
18 | if (response.statusCode == 200) {
19 | return Restaurants.fromMap(response.data);
20 | } else {
21 | throw Exception('Failed to load restaurant list!');
22 | }
23 | }
24 |
25 | Future searchRestaurant(String query) async {
26 | final response = await _client.get(
27 | Urls.searchRestaurant,
28 | queryParameters: {'q': query},
29 | );
30 |
31 | if (response.statusCode == 200) {
32 | return Restaurants.fromSearchMap(response.data);
33 | } else {
34 | throw Exception('Failed to load search result!');
35 | }
36 | }
37 |
38 | Future getRestaurantDetailById(String id) async {
39 | final response = await _client.get(Urls.restaurantDetail(id));
40 |
41 | if (response.statusCode == 200) {
42 | return Restaurant.fromMap(response.data);
43 | } else {
44 | throw Exception('Failed to load restaurant detail!');
45 | }
46 | }
47 |
48 | Future postReviewById({
49 | required String id,
50 | required String name,
51 | required String review,
52 | }) async {
53 | _client.interceptors.add(LogInterceptor(requestBody: true));
54 | final request = await _client.post(
55 | Urls.restraurantReview,
56 | data: {'id': id, 'name': name, 'review': review},
57 | );
58 |
59 | if (request.statusCode == 201) {
60 | return true;
61 | } else {
62 | throw Exception('Failed to post review!');
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion 30
30 |
31 | compileOptions {
32 | sourceCompatibility JavaVersion.VERSION_1_8
33 | targetCompatibility JavaVersion.VERSION_1_8
34 | }
35 |
36 | kotlinOptions {
37 | jvmTarget = '1.8'
38 | }
39 |
40 | sourceSets {
41 | main.java.srcDirs += 'src/main/kotlin'
42 | }
43 |
44 | defaultConfig {
45 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
46 | applicationId "co.rainn.food_rating_app"
47 | minSdkVersion 16
48 | targetSdkVersion 30
49 | versionCode flutterVersionCode.toInteger()
50 | versionName flutterVersionName
51 | }
52 |
53 | buildTypes {
54 | release {
55 | // TODO: Add your own signing config for the release build.
56 | // Signing with the debug keys for now, so `flutter run --release` works.
57 | signingConfig signingConfigs.debug
58 | }
59 | }
60 | }
61 |
62 | flutter {
63 | source '../..'
64 | }
65 |
66 | dependencies {
67 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
68 | }
69 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Food Rating App
2 |
3 | This app is called FoodReview. The main feature is we can discover some restaurants, get details, foods, drinks, and post a review for that restaurant.
4 |
5 |
6 |
7 |
8 |
9 | ### Languages and Frameworks:
10 |
11 | [
][flutter]
12 | [
][dart]
13 |
14 | ### Here are the features in detail:
15 |
16 | ⚡ List of restaurant
17 |
18 | ⚡ Detail restaurant
19 |
20 | ⚡ Write a review
21 |
22 | ⚡ Search a restaurant
23 |
24 | ⚡ Favorite restaurant list
25 |
26 | ⚡ Add favorite restaurant
27 |
28 | ⚡ Delete favorite restaurant
29 |
30 | ⚡ Dark mode
31 |
32 | ⚡ Daily reminder
33 |
34 | ⚡ Testing
35 |
36 | ### Plugin used:
37 |
38 | - android_alarm_manager: ^2.0.2
39 | - animate_do: ^2.0.0
40 | - cached_network_image: ^3.1.0+1
41 | - cupertino_icons: ^1.0.2
42 | - dio: ^4.0.4
43 | - flutter_local_notifications: ^9.1.4
44 | - flutter_spinkit: ^5.1.0
45 | - provider: ^5.0.0
46 | - google_fonts: ^2.1.0
47 | - intl: ^0.17.0
48 | - lottie: ^1.2.1
49 | - persistent_bottom_nav_bar: ^4.0.2
50 | - readmore: ^2.1.0
51 | - rxdart: ^0.27.3
52 | - shared_preferences: ^2.0.9
53 | - styled_text: ^3.0.4+1
54 | - sqflite: ^2.0.0+4
55 |
56 | ## Getting Started
57 |
58 | This project is a starting point for a Flutter application.
59 |
60 | A few resources to get you started if this is your first Flutter project:
61 |
62 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
63 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
64 |
65 | For help getting started with Flutter, view our
66 | [online documentation](https://flutter.dev/docs), which offers tutorials,
67 | samples, guidance on mobile development, and a full API reference.
68 |
69 | [flutter]: https://flutter.dev
70 | [dart]: https://dart.dev
71 |
--------------------------------------------------------------------------------
/lib/modules/providers/database_provider.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:food_rating_app/data/database/database_helper.dart';
3 | import 'package:food_rating_app/data/models/restaurant.dart';
4 |
5 | enum DatabaseResultState { loading, noData, hasData, error }
6 |
7 | class DatabaseProvider extends ChangeNotifier {
8 | final DatabaseHelper databaseHelper;
9 |
10 | List- _favorites = [];
11 | List
- get favorites => _favorites;
12 |
13 | String _message = '';
14 | String get message => _message;
15 |
16 | DatabaseResultState? _databaseResultState;
17 | DatabaseResultState? get databaseResultState => _databaseResultState;
18 |
19 | DatabaseProvider({required this.databaseHelper}) {
20 | _getFavorites();
21 | }
22 |
23 | void _getFavorites() async {
24 | _favorites = await databaseHelper.getFavorites();
25 |
26 | if (_favorites.isNotEmpty) {
27 | _databaseResultState = DatabaseResultState.hasData;
28 | } else {
29 | _databaseResultState = DatabaseResultState.noData;
30 | _message = 'Database is empty';
31 | }
32 |
33 | notifyListeners();
34 | }
35 |
36 | Future addFavorite(Item restaurant) async {
37 | try {
38 | await databaseHelper.insertFavorite(restaurant);
39 | _getFavorites();
40 | } catch (e) {
41 | _databaseResultState = DatabaseResultState.error;
42 | _message = 'Error: $e';
43 | notifyListeners();
44 | }
45 | }
46 |
47 | Future isFavorite(String id) async {
48 | final favorite = await databaseHelper.getFavoriteById(id);
49 | return favorite.isNotEmpty;
50 | }
51 |
52 | void deleteFavoriteById(String id) async {
53 | try {
54 | await databaseHelper.deleteFavorite(id);
55 | _getFavorites();
56 | } catch (e) {
57 | _databaseResultState = DatabaseResultState.error;
58 | _message = 'Error: $e';
59 | notifyListeners();
60 | }
61 | }
62 |
63 | void deleteAllFavorite() async {
64 | try {
65 | await databaseHelper.deleteAllFavorite();
66 | _getFavorites();
67 | } catch (e) {
68 | _databaseResultState = DatabaseResultState.error;
69 | _message = 'Error: $e';
70 | notifyListeners();
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/lib/modules/screens/restaurant/widgets/animation_placeholder.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:food_rating_app/common/styles.dart';
3 | import 'package:lottie/lottie.dart';
4 |
5 | class AnimationPlaceholder extends StatelessWidget {
6 | final String animation;
7 | final String text;
8 | final bool hasButton;
9 | final String? buttonText;
10 | final Function()? onButtonTap;
11 | const AnimationPlaceholder({
12 | Key? key,
13 | required this.animation,
14 | required this.text,
15 | this.hasButton = false,
16 | this.buttonText,
17 | this.onButtonTap,
18 | }) : super(key: key);
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 | return Center(
23 | child: Column(
24 | mainAxisAlignment: MainAxisAlignment.center,
25 | crossAxisAlignment: CrossAxisAlignment.center,
26 | children: [
27 | Lottie.asset(
28 | animation,
29 | width: 180.0,
30 | height: 180.0,
31 | ),
32 | const SizedBox(height: 24.0),
33 | SizedBox(
34 | width: 280.0,
35 | child: Text(
36 | text,
37 | textAlign: TextAlign.center,
38 | style: TextStyles.kRegularBody.copyWith(
39 | fontSize: 18.0,
40 | ),
41 | ),
42 | ),
43 | const SizedBox(height: 18.0),
44 | hasButton
45 | ? ElevatedButton(
46 | child: Text(
47 | buttonText ?? '',
48 | style: TextStyles.kRegularTitle.copyWith(
49 | color: Colors.white,
50 | ),
51 | ),
52 | style: ElevatedButton.styleFrom(
53 | primary: Colors.black,
54 | padding: const EdgeInsets.symmetric(
55 | horizontal: 24.0,
56 | vertical: 14.0,
57 | ),
58 | shape: RoundedRectangleBorder(
59 | borderRadius: BorderRadius.circular(10.0),
60 | ),
61 | ),
62 | onPressed: onButtonTap,
63 | )
64 | : const SizedBox(),
65 | ],
66 | ),
67 | );
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/lib/data/database/database_helper.dart:
--------------------------------------------------------------------------------
1 | import 'package:food_rating_app/data/models/restaurant.dart';
2 | import 'package:sqflite/sqflite.dart';
3 | import 'package:path/path.dart';
4 |
5 | class DatabaseHelper {
6 | static DatabaseHelper? _instance;
7 | static Database? _database;
8 |
9 | DatabaseHelper._internal() {
10 | _instance = this;
11 | }
12 |
13 | factory DatabaseHelper() => _instance ?? DatabaseHelper._internal();
14 |
15 | static const String _tableName = 'favorite';
16 |
17 | Future _initializeDb() async {
18 | final path = await getDatabasesPath();
19 | final db = openDatabase(
20 | join(path, 'favorite_db.db'),
21 | onCreate: (db, version) async {
22 | await db.execute('''CREATE TABLE $_tableName (
23 | id TEXT PRIMARY KEY,
24 | name TEXT,
25 | description TEXT,
26 | pictureId TEXT,
27 | city TEXT,
28 | rating DOUBLE
29 | )''');
30 | },
31 | version: 1,
32 | );
33 |
34 | return db;
35 | }
36 |
37 | Future get database async {
38 | _database ??= await _initializeDb();
39 | return _database;
40 | }
41 |
42 | /// Insert favorite
43 | Future insertFavorite(Item restaurant) async {
44 | final Database? db = await database;
45 | await db!.insert(_tableName, restaurant.toMap());
46 | }
47 |
48 | /// Get all favorite
49 | Future
> getFavorites() async {
50 | final Database? db = await database;
51 | List