├── ios ├── Runner │ ├── Runner-Bridging-Header.h │ ├── Assets.xcassets │ │ ├── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-83.5x83.5@2x.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 ├── manifest.json └── index.html ├── assets ├── images │ ├── logo.png │ ├── taxi.png │ ├── car_ios.png │ ├── logo1.png │ ├── offline.png │ ├── desticon.png │ ├── desticon1.png │ ├── pickicon.png │ ├── redmarker.png │ ├── user_icon.png │ ├── car_android.png │ └── posimarker.png ├── logo │ ├── 512@2x.png │ ├── cover.png │ ├── default.png │ ├── profile.png │ ├── apple_1024_1x.png │ ├── ic_launcher_android.png │ ├── info.txt │ └── vector │ │ ├── isolated-monochrome-black.svg │ │ ├── isolated-monochrome-white.svg │ │ ├── isolated-layout.svg │ │ ├── default-monochrome-black.svg │ │ ├── default-monochrome-white.svg │ │ ├── default-monochrome.svg │ │ └── default.svg ├── sounds │ └── alert.mp3 └── fonts │ ├── Viga │ ├── Viga-Regular.ttf │ └── OFL.txt │ └── Open_Sans │ ├── OpenSans-Bold.ttf │ ├── OpenSans-Italic.ttf │ ├── OpenSans-Light.ttf │ ├── OpenSans-ExtraBold.ttf │ ├── OpenSans-Regular.ttf │ ├── OpenSans-SemiBold.ttf │ ├── OpenSans-BoldItalic.ttf │ ├── OpenSans-LightItalic.ttf │ ├── OpenSans-SemiBoldItalic.ttf │ ├── OpenSans-ExtraBoldItalic.ttf │ └── LICENSE.txt ├── android ├── gradle.properties ├── app │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── 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 │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable-v21 │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── values │ │ │ │ │ └── styles.xml │ │ │ │ └── values-night │ │ │ │ │ └── styles.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── boomer │ │ │ │ │ └── driver │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── .gitignore ├── settings.gradle └── build.gradle ├── screenshots ├── Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.22.32.png ├── Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.22.56.png ├── Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.24.17.png ├── Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.24.24.png ├── Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.24.33.png ├── Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.24.45.png ├── Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.24.55.png ├── Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.24.57.png ├── Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.26.08.png ├── Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.26.17.png └── Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-31 at 06.50.01.png ├── lib ├── helpers │ └── http_exception.dart ├── models │ ├── driver.dart │ └── car.dart ├── screens │ ├── ratings_screen.dart │ ├── earnings_screen.dart │ ├── splash_screen.dart │ ├── navigation_bar.dart │ ├── home_screen.dart │ ├── about_screen.dart │ ├── profile_screen.dart │ ├── car_info_screen.dart │ ├── all_cars_screen.dart │ └── auth_screen.dart ├── widgets │ ├── splashed_flex.dart │ ├── decorated_wrapper.dart │ ├── side_tabbed_title.dart │ ├── custom_button.dart │ ├── floating_appbar_wrapper.dart │ ├── tap_to_action.dart │ └── custom_textfield.dart ├── providers │ ├── maps_provider.dart │ ├── driver_provider.dart │ └── auth.dart └── main.dart ├── .metadata ├── .gitignore ├── test └── widget_test.dart ├── README.md ├── pubspec.yaml └── pubspec.lock /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/web/favicon.png -------------------------------------------------------------------------------- /assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/images/logo.png -------------------------------------------------------------------------------- /assets/images/taxi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/images/taxi.png -------------------------------------------------------------------------------- /assets/logo/512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/logo/512@2x.png -------------------------------------------------------------------------------- /assets/logo/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/logo/cover.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/web/icons/Icon-512.png -------------------------------------------------------------------------------- /assets/images/car_ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/images/car_ios.png -------------------------------------------------------------------------------- /assets/images/logo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/images/logo1.png -------------------------------------------------------------------------------- /assets/images/offline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/images/offline.png -------------------------------------------------------------------------------- /assets/logo/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/logo/default.png -------------------------------------------------------------------------------- /assets/logo/profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/logo/profile.png -------------------------------------------------------------------------------- /assets/sounds/alert.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/sounds/alert.mp3 -------------------------------------------------------------------------------- /assets/images/desticon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/images/desticon.png -------------------------------------------------------------------------------- /assets/images/desticon1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/images/desticon1.png -------------------------------------------------------------------------------- /assets/images/pickicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/images/pickicon.png -------------------------------------------------------------------------------- /assets/images/redmarker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/images/redmarker.png -------------------------------------------------------------------------------- /assets/images/user_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/images/user_icon.png -------------------------------------------------------------------------------- /assets/images/car_android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/images/car_android.png -------------------------------------------------------------------------------- /assets/images/posimarker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/images/posimarker.png -------------------------------------------------------------------------------- /assets/logo/apple_1024_1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/logo/apple_1024_1x.png -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /assets/fonts/Viga/Viga-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/fonts/Viga/Viga-Regular.ttf -------------------------------------------------------------------------------- /assets/logo/ic_launcher_android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/logo/ic_launcher_android.png -------------------------------------------------------------------------------- /assets/fonts/Open_Sans/OpenSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/fonts/Open_Sans/OpenSans-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/Open_Sans/OpenSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/fonts/Open_Sans/OpenSans-Italic.ttf -------------------------------------------------------------------------------- /assets/fonts/Open_Sans/OpenSans-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/fonts/Open_Sans/OpenSans-Light.ttf -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /assets/fonts/Open_Sans/OpenSans-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/fonts/Open_Sans/OpenSans-ExtraBold.ttf -------------------------------------------------------------------------------- /assets/fonts/Open_Sans/OpenSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/fonts/Open_Sans/OpenSans-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/Open_Sans/OpenSans-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/fonts/Open_Sans/OpenSans-SemiBold.ttf -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /assets/fonts/Open_Sans/OpenSans-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/fonts/Open_Sans/OpenSans-BoldItalic.ttf -------------------------------------------------------------------------------- /assets/fonts/Open_Sans/OpenSans-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/fonts/Open_Sans/OpenSans-LightItalic.ttf -------------------------------------------------------------------------------- /assets/fonts/Open_Sans/OpenSans-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/fonts/Open_Sans/OpenSans-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/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/gsbakshi/boomer-driver/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /assets/fonts/Open_Sans/OpenSans-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/assets/fonts/Open_Sans/OpenSans-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/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/gsbakshi/boomer-driver/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/boomer/driver/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.boomer.driver 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /screenshots/Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.22.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/screenshots/Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.22.32.png -------------------------------------------------------------------------------- /screenshots/Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.22.56.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/screenshots/Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.22.56.png -------------------------------------------------------------------------------- /screenshots/Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.24.17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/screenshots/Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.24.17.png -------------------------------------------------------------------------------- /screenshots/Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.24.24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/screenshots/Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.24.24.png -------------------------------------------------------------------------------- /screenshots/Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.24.33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/screenshots/Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.24.33.png -------------------------------------------------------------------------------- /screenshots/Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.24.45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/screenshots/Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.24.45.png -------------------------------------------------------------------------------- /screenshots/Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.24.55.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/screenshots/Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.24.55.png -------------------------------------------------------------------------------- /screenshots/Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.24.57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/screenshots/Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.24.57.png -------------------------------------------------------------------------------- /screenshots/Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.26.08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/screenshots/Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.26.08.png -------------------------------------------------------------------------------- /screenshots/Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.26.17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/screenshots/Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-29 at 11.26.17.png -------------------------------------------------------------------------------- /screenshots/Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-31 at 06.50.01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsbakshi/boomer-driver/HEAD/screenshots/Simulator Screen Shot - iPhone 12 Pro Max - 2021-08-31 at 06.50.01.png -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/helpers/http_exception.dart: -------------------------------------------------------------------------------- 1 | class HttpException implements Exception { 2 | final String message; 3 | 4 | HttpException(this.message); 5 | 6 | @override 7 | String toString() { 8 | return message; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /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: f4abaa0735eba4dfd8f33f73363911d63931fe03 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 | #secret keys 10 | **/*/res/values/secrets.xml 11 | 12 | # Remember to never publicly share your keystore. 13 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 14 | key.properties 15 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /assets/logo/info.txt: -------------------------------------------------------------------------------- 1 | 2 | Hope you enjoy your new logo, here are the people that 3 | made your beautiful logo happen :) 4 | font name: Viga-Regular 5 | font link: https://fonts.google.com/specimen/Viga 6 | font author: Fontstage 7 | font author site: http://www.fontstage.com/ 8 | 9 | 10 | icon designer: Erika Jasso 11 | icon designer link: /kikajasso 12 | 13 | {"bg":"#423833","icon":"#D1793F","font":"#ffffff","slogan":"#ffffff"} 14 | -------------------------------------------------------------------------------- /lib/models/driver.dart: -------------------------------------------------------------------------------- 1 | import 'car.dart'; 2 | 3 | class Driver { 4 | Driver({ 5 | this.cars, 6 | this.email, 7 | this.mobile, 8 | this.name, 9 | this.id, 10 | }); 11 | 12 | final List? cars; 13 | final String? email; 14 | final int? mobile; 15 | final String? name; 16 | final String? id; 17 | 18 | 19 | @override 20 | String toString() { 21 | return 'Driver(cars: $cars, email: $email, mobile: $mobile, name: $name, id: $id)'; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/models/car.dart: -------------------------------------------------------------------------------- 1 | class Car { 2 | Car({ 3 | this.id, 4 | this.carMake, 5 | this.carModel, 6 | this.carNumber, 7 | this.carColor, 8 | }); 9 | 10 | final String? id; 11 | final String? carMake; 12 | final String? carModel; 13 | final String? carNumber; 14 | final String? carColor; 15 | 16 | @override 17 | String toString() { 18 | return 'Car(id: $id, carMake: $carMake, carModel: $carModel, carNumber: $carNumber, carColor: $carColor)'; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/screens/ratings_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class RatingsScreen extends StatelessWidget { 4 | const RatingsScreen({Key? key}) : super(key: key); 5 | static const routeName = '/ratings-screen'; 6 | @override 7 | Widget build(BuildContext context) { 8 | return Scaffold( 9 | body: Center( 10 | child: Container( 11 | child: Text( 12 | 'Ratings Screen', 13 | style: Theme.of(context).textTheme.headline3, 14 | ), 15 | ), 16 | ), 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/screens/earnings_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class EarningsScreen extends StatelessWidget { 4 | const EarningsScreen({Key? key}) : super(key: key); 5 | static const routeName = '/earnings-screen'; 6 | @override 7 | Widget build(BuildContext context) { 8 | return Scaffold( 9 | body: Center( 10 | child: Container( 11 | child: Text( 12 | 'Earnings Screen', 13 | style: Theme.of(context).textTheme.headline3, 14 | ), 15 | ), 16 | ), 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "driver", 3 | "short_name": "driver", 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 | } 24 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/ephemeral/ 22 | Flutter/app.flx 23 | Flutter/app.zip 24 | Flutter/flutter_assets/ 25 | Flutter/flutter_export_environment.sh 26 | ServiceDefinitions.json 27 | Runner/GeneratedPluginRegistrant.* 28 | 29 | # Exceptions to above rules. 30 | !default.mode1v3 31 | !default.mode2v3 32 | !default.pbxuser 33 | !default.perspectivev3 34 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 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 | classpath 'com.google.gms:google-services:4.3.8' 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | google() 18 | jcenter() 19 | } 20 | } 21 | 22 | rootProject.buildDir = '../build' 23 | subprojects { 24 | project.buildDir = "${rootProject.buildDir}/${project.name}" 25 | project.evaluationDependsOn(':app') 26 | } 27 | 28 | task clean(type: Delete) { 29 | delete rootProject.buildDir 30 | } 31 | -------------------------------------------------------------------------------- /lib/widgets/splashed_flex.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class SplashedFlex extends StatelessWidget { 4 | const SplashedFlex({ 5 | Key? key, 6 | }) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Column( 11 | crossAxisAlignment: CrossAxisAlignment.center, 12 | mainAxisAlignment: MainAxisAlignment.center, 13 | mainAxisSize: MainAxisSize.min, 14 | children: [ 15 | Image(image: AssetImage('assets/logo/cover.png')), 16 | Text( 17 | 'Driver App', 18 | style: TextStyle( 19 | color: Colors.white, 20 | fontSize: 32, 21 | fontFamily: 'Viga', 22 | ), 23 | ), 24 | ], 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/widgets/decorated_wrapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class DecoratedWrapper extends StatelessWidget { 4 | const DecoratedWrapper({ 5 | Key? key, 6 | this.child, 7 | }) : super(key: key); 8 | 9 | final Widget? child; 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Container( 14 | decoration: BoxDecoration( 15 | color: Theme.of(context).primaryColor, 16 | borderRadius: BorderRadius.circular(16), 17 | boxShadow: [ 18 | BoxShadow( 19 | color: Theme.of(context).primaryColorDark, 20 | blurRadius: 3, 21 | spreadRadius: 0.5, 22 | offset: Offset(0.7, 0.7), 23 | ), 24 | ], 25 | ), 26 | child: child, 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /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 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/widgets/side_tabbed_title.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class SideTabbedTitle extends StatelessWidget { 4 | const SideTabbedTitle( 5 | this.title, { 6 | Key? key, 7 | }) : super(key: key); 8 | 9 | final String title; 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Row( 14 | children: [ 15 | Container( 16 | padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24), 17 | decoration: BoxDecoration( 18 | color: Color(0xffD1793F), 19 | borderRadius: BorderRadius.only( 20 | topRight: Radius.circular(8), 21 | bottomRight: Radius.circular(8), 22 | ), 23 | ), 24 | child: Text( 25 | title, 26 | style: Theme.of(context).textTheme.headline5, 27 | ), 28 | ), 29 | ], 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/widgets/custom_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class CustomButton extends StatelessWidget { 4 | const CustomButton({ 5 | Key? key, 6 | required this.label, 7 | required this.onTap, 8 | }) : super(key: key); 9 | 10 | final String label; 11 | final void Function()? onTap; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Container( 16 | width: double.infinity, 17 | child: ElevatedButton( 18 | onPressed: onTap, 19 | child: Text(label), 20 | style: ElevatedButton.styleFrom( 21 | primary: Theme.of(context).accentColor, 22 | onPrimary: Color(0xffF8EBE2), 23 | textStyle: TextStyle( 24 | fontSize: 18, 25 | fontWeight: FontWeight.w500, 26 | ), 27 | padding: const EdgeInsets.all(14), 28 | shape: RoundedRectangleBorder( 29 | borderRadius: BorderRadius.circular(12), 30 | ), 31 | ), 32 | ), 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/screens/splash_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../widgets/splashed_flex.dart'; 4 | 5 | class SplashScreen extends StatelessWidget { 6 | const SplashScreen({Key? key}) : super(key: key); 7 | 8 | static const routeName = '/splash'; 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | var query = MediaQuery.of(context).size; 13 | return Scaffold( 14 | body: Center( 15 | child: Stack( 16 | fit: StackFit.expand, 17 | children: [ 18 | SplashedFlex(), 19 | Positioned( 20 | bottom: 0, 21 | child: Container( 22 | height: query.height * 0.2, 23 | width: query.width, 24 | child: Center( 25 | child: CircularProgressIndicator( 26 | color: Theme.of(context).accentColor, 27 | ), 28 | ), 29 | ), 30 | ) 31 | ], 32 | ), 33 | ), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/widgets/floating_appbar_wrapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'decorated_wrapper.dart'; 4 | 5 | class FloatingAppBarWrapper extends StatelessWidget { 6 | const FloatingAppBarWrapper({ 7 | Key? key, 8 | this.children = const [], 9 | required this.height, 10 | required this.width, 11 | }) : super(key: key); 12 | 13 | final List children; 14 | final double height; 15 | final double width; 16 | @override 17 | Widget build(BuildContext context) { 18 | return SafeArea( 19 | child: Container( 20 | height: height, 21 | width: width, 22 | padding: const EdgeInsets.symmetric(horizontal: 16), 23 | child: DecoratedWrapper( 24 | child: Padding( 25 | padding: const EdgeInsets.symmetric(horizontal: 8), 26 | child: Row( 27 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 28 | children: children, 29 | ), 30 | ), 31 | ), 32 | ), 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 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Firebase 3 | import GoogleMaps 4 | import Flutter 5 | 6 | @UIApplicationMain 7 | @objc class AppDelegate: FlutterAppDelegate { 8 | private let mapsKey: String 9 | 10 | override init() { 11 | guard let filePath = Bundle.main.path(forResource: "Api-Keys-Info", ofType: "plist") else { 12 | fatalError("Couldn't find file 'Api-Keys-Info.plist'.") 13 | } 14 | let plist = NSDictionary(contentsOfFile: filePath) 15 | guard let key = plist?.object(forKey: "GOOGLE_MAPS_API") as? String else { 16 | fatalError("Couldn't find key 'GOOGLE_MAPS_API' in 'Api-Keys-Info.plist'.") 17 | } 18 | mapsKey = key 19 | super.init() 20 | } 21 | 22 | override func application( 23 | _ application: UIApplication, 24 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 25 | ) -> Bool { 26 | FirebaseApp.configure() 27 | GMSServices.provideAPIKey(mapsKey) 28 | GeneratedPluginRegistrant.register(with: self) 29 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | .store 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | 35 | # Web related 36 | lib/generated_plugin_registrant.dart 37 | 38 | # Symbolication related 39 | app.*.symbols 40 | 41 | # Obfuscation related 42 | app.*.map.json 43 | key.jks 44 | 45 | # Android Studio will place build artifacts here 46 | /android/app/debug 47 | /android/app/profile 48 | /android/app/release 49 | 50 | #Firebase related files 51 | firebase_utils.dart 52 | 53 | #GCP related files 54 | platform_keys.dart 55 | 56 | #Secrets 57 | google-services.json 58 | GoogleService-Info.plist 59 | Api-Keys-Info.plist -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:boomer_driver/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /lib/widgets/tap_to_action.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/gestures.dart'; 3 | 4 | class TapToActionText extends StatelessWidget { 5 | const TapToActionText({ 6 | Key? key, 7 | this.label, 8 | this.tapLabel, 9 | this.onTap, 10 | this.padding = const EdgeInsets.only(top: 20), 11 | }) : super(key: key); 12 | 13 | final String? label; 14 | final String? tapLabel; 15 | final void Function()? onTap; 16 | final EdgeInsetsGeometry? padding; 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Container( 21 | padding: padding, 22 | width: double.infinity, 23 | child: Center( 24 | child: RichText( 25 | text: TextSpan( 26 | text: label, 27 | style: TextStyle( 28 | fontSize: 14, 29 | color: Color(0xffB8AAA3), 30 | ), 31 | children: [ 32 | TextSpan( 33 | text: tapLabel, 34 | style: TextStyle( 35 | color: Theme.of(context).accentColor, 36 | fontSize: 14, 37 | fontWeight: FontWeight.w600, 38 | ), 39 | recognizer: TapGestureRecognizer()..onTap = onTap, 40 | ), 41 | ], 42 | ), 43 | ), 44 | ), 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '11.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 | pod 'Firebase/Analytics' 35 | pod 'GoogleMaps' 36 | 37 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 38 | end 39 | 40 | post_install do |installer| 41 | installer.pods_project.targets.each do |target| 42 | flutter_additional_ios_build_settings(target) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![image](https://github.com/gsbakshi/boomer-driver/blob/main/assets/logo/cover.png) 2 | # Boomer Driver App 3 | 4 | An Uber-like application for Drivers made using Flutter. This app is meant to be a supporting application for [Rider App](https://github.com/gsbakshi/boomer-rider). It's only purpose at the moment is to register a driver and mark them as available, if online, and display on the map to the riders looking to hail a ride from nearby, on the [Rider App](https://github.com/gsbakshi/boomer-rider). 5 | 6 | `Supports both iOS and Android.` 7 | 8 | View [screenshots](https://github.com/gsbakshi/boomer-driver/tree/main/screenshots) 9 | 10 | ## Table of contents 11 | 12 | 1. [External Services that app uses](#external-services-that-app-uses) 13 | 2. [Features](#features) 14 | 3. [Issues](#issues) 15 | 16 | 17 | ### External Services that app uses 18 | - [Google Maps SDK for Android](https://developers.google.com/maps/documentation/android-sdk/overview) for maps on Android; 19 | - [Google Maps SDK for iOS](https://developers.google.com/maps/documentation/ios-sdk/overview) for maps on iOS; 20 | - [Firebase](https://firebase.google.com/) for authentication and database; 21 | 22 | ### Features 23 | 24 | - [x] Driver is able to register in the system 25 | - [x] Driver is able to login into the system 26 | - [x] Driver is able to view his profile info 27 | - [x] Driver is able to view and add cars 28 | - [x] Driver is able to go online/offline depending on their availability 29 | - [x] Driver is get go online/offline depending on their availability 30 | - [x] Driver sends a stream of current location updates when online to find new nearby ride requests 31 | - [x] Driver is able to logout 32 | 33 | ### Issues 34 | 35 | - [x] Driver is not able to edit their details 36 | - [x] Driver is not able to delete their account 37 | 38 | ``` The issues have not been resovled due to laziness and non-requirement. ``` 39 | 40 | 41 | 42 | *This application is only for showcasing purposes.* -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Boomer Driver 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | driver 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | NSLocationAlwaysUsageDescription 28 | This app needs access to location when in the background. 29 | NSLocationWhenInUseUsageDescription 30 | This app needs access to location when open. 31 | UILaunchStoryboardName 32 | LaunchScreen 33 | UIMainStoryboardFile 34 | Main 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | UIViewControllerBasedStatusBarAppearance 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /assets/logo/vector/isolated-monochrome-black.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/logo/vector/isolated-monochrome-white.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/logo/vector/isolated-layout.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.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 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /lib/screens/navigation_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'home_screen.dart'; 4 | import 'profile_screen.dart'; 5 | import 'ratings_screen.dart'; 6 | import 'earnings_screen.dart'; 7 | 8 | class NavigationBar extends StatefulWidget { 9 | const NavigationBar({Key? key}) : super(key: key); 10 | static const routeName = '/nav-bar'; 11 | 12 | @override 13 | _NavigationBarState createState() => _NavigationBarState(); 14 | } 15 | 16 | class _NavigationBarState extends State 17 | with SingleTickerProviderStateMixin { 18 | PageController _pageController = PageController(); 19 | 20 | int selectedIndex = 0; 21 | 22 | List _pages = [ 23 | HomeScreen(), 24 | EarningsScreen(), 25 | RatingsScreen(), 26 | ProfileScreen(), 27 | ]; 28 | 29 | void onItemClicked(int index) { 30 | setState(() { 31 | selectedIndex = index; 32 | _pageController.animateToPage( 33 | selectedIndex, 34 | duration: Duration(microseconds: 160), 35 | curve: Curves.bounceIn, 36 | ); 37 | }); 38 | } 39 | 40 | @override 41 | void initState() { 42 | super.initState(); 43 | } 44 | 45 | @override 46 | void dispose() { 47 | _pageController.dispose(); 48 | super.dispose(); 49 | } 50 | 51 | @override 52 | Widget build(BuildContext context) { 53 | return Scaffold( 54 | body: PageView( 55 | controller: _pageController, 56 | physics: NeverScrollableScrollPhysics(), 57 | onPageChanged: (int index) => onItemClicked(index), 58 | children: _pages.map((page) => page).toList(), 59 | ), 60 | bottomNavigationBar: BottomNavigationBar( 61 | items: [ 62 | BottomNavigationBarItem( 63 | icon: Icon(Icons.home), 64 | label: 'Home', 65 | ), 66 | BottomNavigationBarItem( 67 | icon: Icon(Icons.credit_card), 68 | label: 'Earnings', 69 | ), 70 | BottomNavigationBarItem( 71 | icon: Icon(Icons.star), 72 | label: 'Ratings', 73 | ), 74 | BottomNavigationBarItem( 75 | icon: Icon(Icons.person), 76 | label: 'Account', 77 | ), 78 | ], 79 | unselectedItemColor: 80 | Theme.of(context).primaryColorLight.withOpacity(0.5), 81 | selectedItemColor: Theme.of(context).accentColor, 82 | type: BottomNavigationBarType.fixed, 83 | showUnselectedLabels: true, 84 | currentIndex: selectedIndex, 85 | onTap: (int index) => onItemClicked(index), 86 | ), 87 | ); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def keystoreProperties = new Properties() 2 | def keystorePropertiesFile = rootProject.file('key.properties') 3 | if (keystorePropertiesFile.exists()) { 4 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 5 | } 6 | 7 | def localProperties = new Properties() 8 | def localPropertiesFile = rootProject.file('local.properties') 9 | if (localPropertiesFile.exists()) { 10 | localPropertiesFile.withReader('UTF-8') { reader -> 11 | localProperties.load(reader) 12 | } 13 | } 14 | 15 | def flutterRoot = localProperties.getProperty('flutter.sdk') 16 | if (flutterRoot == null) { 17 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 18 | } 19 | 20 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 21 | if (flutterVersionCode == null) { 22 | flutterVersionCode = '1' 23 | } 24 | 25 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 26 | if (flutterVersionName == null) { 27 | flutterVersionName = '1.0' 28 | } 29 | 30 | apply plugin: 'com.android.application' 31 | apply plugin: 'kotlin-android' 32 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 33 | apply plugin: 'com.google.gms.google-services' 34 | 35 | android { 36 | compileSdkVersion 30 37 | 38 | sourceSets { 39 | main.java.srcDirs += 'src/main/kotlin' 40 | } 41 | 42 | defaultConfig { 43 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 44 | applicationId "com.boomer.driver" 45 | minSdkVersion 20 46 | targetSdkVersion 30 47 | versionCode flutterVersionCode.toInteger() 48 | versionName flutterVersionName 49 | } 50 | 51 | signingConfigs { 52 | release { 53 | keyAlias keystoreProperties['keyAlias'] 54 | keyPassword keystoreProperties['keyPassword'] 55 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null 56 | storePassword keystoreProperties['storePassword'] 57 | } 58 | } 59 | 60 | buildTypes { 61 | debug { 62 | minifyEnabled true 63 | useProguard false 64 | } 65 | release { 66 | signingConfig signingConfigs.release 67 | } 68 | } 69 | } 70 | 71 | flutter { 72 | source '../..' 73 | } 74 | 75 | dependencies { 76 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 77 | implementation platform('com.google.firebase:firebase-bom:28.3.0') 78 | implementation 'com.google.firebase:firebase-analytics-ktx' 79 | } 80 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 13 | 17 | 21 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /assets/logo/vector/default-monochrome-black.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/logo/vector/default-monochrome-white.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/logo/vector/default-monochrome.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /assets/logo/vector/default.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/widgets/custom_textfield.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class CustomTextField extends StatelessWidget { 4 | CustomTextField({ 5 | Key? key, 6 | required this.label, 7 | this.keyboardType, 8 | this.controller, 9 | this.textInputAction, 10 | this.prefixIcon, 11 | this.suffixIcon, 12 | this.onFieldSubmitted, 13 | this.validator, 14 | this.onSaved, 15 | this.focusNode, 16 | this.obscure = false, 17 | }) : enabledBorder = OutlineInputBorder( 18 | borderRadius: BorderRadius.circular(12), 19 | borderSide: BorderSide(color: Colors.grey), 20 | ), 21 | errorBorder = OutlineInputBorder( 22 | borderRadius: BorderRadius.circular(12), 23 | borderSide: BorderSide(color: Colors.red), 24 | ), 25 | focusedErrorBorder = OutlineInputBorder( 26 | borderRadius: BorderRadius.circular(12), 27 | borderSide: BorderSide(color: Color(0xffD1793F)), 28 | ), 29 | focusedBorder = OutlineInputBorder( 30 | borderRadius: BorderRadius.circular(12), 31 | borderSide: BorderSide(color: Color(0xffD1793F)), 32 | ), 33 | super(key: key); 34 | 35 | CustomTextField.underline({ 36 | Key? key, 37 | required this.label, 38 | this.keyboardType, 39 | this.controller, 40 | this.textInputAction, 41 | this.prefixIcon, 42 | this.suffixIcon, 43 | this.onFieldSubmitted, 44 | this.validator, 45 | this.onSaved, 46 | this.focusNode, 47 | this.obscure = false, 48 | }) : enabledBorder = UnderlineInputBorder( 49 | borderSide: BorderSide(color: Colors.grey), 50 | ), 51 | errorBorder = UnderlineInputBorder( 52 | borderSide: BorderSide(color: Colors.red), 53 | ), 54 | focusedErrorBorder = UnderlineInputBorder( 55 | borderSide: BorderSide(color: Color(0xffD1793F)), 56 | ), 57 | focusedBorder = UnderlineInputBorder( 58 | borderSide: BorderSide(color: Color(0xffD1793F)), 59 | ), 60 | super(key: key); 61 | 62 | final String label; 63 | final TextEditingController? controller; 64 | final TextInputType? keyboardType; 65 | final TextInputAction? textInputAction; 66 | final Widget? prefixIcon; 67 | final Widget? suffixIcon; 68 | final void Function(String)? onFieldSubmitted; 69 | final String? Function(String?)? validator; 70 | final void Function(String?)? onSaved; 71 | final FocusNode? focusNode; 72 | final bool obscure; 73 | 74 | late final InputBorder enabledBorder; 75 | late final InputBorder errorBorder; 76 | late final InputBorder focusedErrorBorder; 77 | late final InputBorder focusedBorder; 78 | 79 | @override 80 | Widget build(BuildContext context) { 81 | return TextFormField( 82 | keyboardType: keyboardType, 83 | controller: controller, 84 | obscureText: obscure, 85 | style: TextStyle(color: Colors.white), 86 | textInputAction: textInputAction, 87 | decoration: InputDecoration( 88 | labelText: label, 89 | labelStyle: TextStyle( 90 | fontSize: 16, 91 | color: Colors.grey, 92 | ), 93 | hintStyle: TextStyle( 94 | color: Theme.of(context).accentColor, 95 | ), 96 | enabledBorder: enabledBorder, 97 | errorBorder: errorBorder, 98 | focusedErrorBorder: focusedErrorBorder, 99 | focusedBorder: focusedBorder, 100 | prefixIcon: prefixIcon, 101 | suffixIcon: suffixIcon, 102 | ), 103 | onFieldSubmitted: onFieldSubmitted, 104 | validator: validator, 105 | onSaved: onSaved, 106 | focusNode: focusNode, 107 | ); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | driver 27 | 28 | 29 | 30 | 33 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /lib/providers/maps_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:geolocator/geolocator.dart'; 5 | import 'package:flutter_geofire/flutter_geofire.dart'; 6 | import 'package:google_maps_flutter/google_maps_flutter.dart'; 7 | 8 | import 'driver_provider.dart'; 9 | 10 | class MapsProvider with ChangeNotifier { 11 | void update(DriverProvider driver) { 12 | driverId = driver.driverId; 13 | status = driver.status; 14 | } 15 | 16 | late String? driverId; 17 | 18 | late bool status; 19 | 20 | late Position _currentPosition; 21 | 22 | Position get currentPosition => _currentPosition; 23 | 24 | late GoogleMapController _newMapController; 25 | 26 | GoogleMapController get newMapController => _newMapController; 27 | 28 | Future setMapController(GoogleMapController controller) async { 29 | try { 30 | Completer _controller = Completer(); 31 | _controller.complete(controller); 32 | _newMapController = await _controller.future; 33 | notifyListeners(); 34 | await locatePosition(); 35 | } catch (error) { 36 | rethrow; 37 | } 38 | } 39 | 40 | // ignore: cancel_subscriptions 41 | StreamSubscription? liveLocationStream; 42 | 43 | bool isPermissionsInit = true; 44 | 45 | Future checkPermissions() async { 46 | LocationPermission permission; 47 | permission = await Geolocator.checkPermission(); 48 | if (permission == LocationPermission.denied) { 49 | permission = await Geolocator.requestPermission(); 50 | if (permission == LocationPermission.denied || 51 | permission == LocationPermission.deniedForever) { 52 | return false; 53 | } 54 | isPermissionsInit = false; 55 | notifyListeners(); 56 | return true; 57 | } else { 58 | isPermissionsInit = false; 59 | notifyListeners(); 60 | return true; 61 | } 62 | } 63 | 64 | Future geolocate() async { 65 | final check = await checkPermissions(); 66 | if (check) { 67 | Position position = await Geolocator.getCurrentPosition( 68 | desiredAccuracy: LocationAccuracy.high, 69 | ); 70 | _currentPosition = position; 71 | } 72 | notifyListeners(); 73 | } 74 | 75 | Future locatePosition() async { 76 | try { 77 | await geolocate(); 78 | LatLng latLngPosition = 79 | LatLng(currentPosition.latitude, currentPosition.longitude); 80 | CameraPosition cameraPosition = new CameraPosition( 81 | target: latLngPosition, 82 | zoom: 14, 83 | ); 84 | _newMapController.animateCamera( 85 | CameraUpdate.newCameraPosition(cameraPosition), 86 | ); 87 | await goOnline(); 88 | await getLiveLocationUpdates(); 89 | } catch (error) { 90 | print(error); 91 | throw error; 92 | } 93 | } 94 | 95 | Future goOffline() async { 96 | try { 97 | await Geofire.removeLocation(driverId!); 98 | // _controller = null; 99 | // liveLocationStream = null; 100 | } catch (error) { 101 | print(error); 102 | throw error; 103 | } 104 | } 105 | 106 | Future goOnline() async { 107 | try { 108 | await geolocate(); 109 | await Geofire.initialize('boomer/available-drivers'); 110 | await Geofire.setLocation( 111 | driverId!, 112 | currentPosition.latitude, 113 | currentPosition.longitude, 114 | ); 115 | } catch (error) { 116 | print(error); 117 | throw error; 118 | } 119 | } 120 | 121 | Future getLiveLocationUpdates() async { 122 | liveLocationStream = Geolocator.getPositionStream().listen( 123 | (Position position) { 124 | _currentPosition = position; 125 | if (status) 126 | Geofire.setLocation(driverId!, position.latitude, position.longitude); 127 | LatLng latLng = LatLng(position.latitude, position.longitude); 128 | CameraPosition cameraPosition = new CameraPosition( 129 | target: latLng, 130 | zoom: 14, 131 | ); 132 | _newMapController 133 | .animateCamera(CameraUpdate.newCameraPosition(cameraPosition)); 134 | }, 135 | ); 136 | notifyListeners(); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: boomer_driver 2 | description: A new Flutter project. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 1.0.2+1 19 | 20 | environment: 21 | sdk: ">=2.12.0 <3.0.0" 22 | 23 | dependencies: 24 | flutter: 25 | sdk: flutter 26 | 27 | 28 | # The following adds the Cupertino Icons font to your application. 29 | # Use with the CupertinoIcons class for iOS style icons. 30 | cupertino_icons: ^1.0.2 31 | firebase_auth: ^3.0.1 32 | firebase_core: ^1.4.0 33 | provider: ^5.0.0 34 | http: ^0.13.3 35 | shared_preferences: ^2.0.6 36 | google_maps_flutter: ^2.0.6 37 | geolocator: ^7.4.0 38 | flutter_polyline_points: ^1.0.0 39 | animated_text_kit: ^4.2.1 40 | flutter_geofire: ^2.0.0 41 | 42 | dev_dependencies: 43 | flutter_test: 44 | sdk: flutter 45 | flutter_launcher_icons: ^0.9.2 46 | 47 | flutter_icons: 48 | android: true 49 | ios: true 50 | image_path_android: "assets/logo/ic_launcher_android.png" 51 | image_path_ios: "assets/logo/apple_1024_1x.png" 52 | 53 | # For information on the generic Dart part of this file, see the 54 | # following page: https://dart.dev/tools/pub/pubspec 55 | 56 | # The following section is specific to Flutter. 57 | flutter: 58 | 59 | # The following line ensures that the Material Icons font is 60 | # included with your application, so that you can use the icons in 61 | # the material Icons class. 62 | uses-material-design: true 63 | 64 | # To add assets to your application, add an assets section, like this: 65 | assets: 66 | - assets/images/ 67 | - assets/logo/ 68 | - assets/sounds/ 69 | 70 | # An image asset can refer to one or more resolution-specific "variants", see 71 | # https://flutter.dev/assets-and-images/#resolution-aware. 72 | 73 | # For details regarding adding assets from package dependencies, see 74 | # https://flutter.dev/assets-and-images/#from-packages 75 | 76 | # To add custom fonts to your application, add a fonts section here, 77 | # in this "flutter" section. Each entry in this list should have a 78 | # "family" key with the font family name, and a "fonts" key with a 79 | # list giving the asset and other descriptors for the font. For 80 | # example: 81 | fonts: 82 | - family: Viga 83 | fonts: 84 | - asset: assets/fonts/Viga/Viga-Regular.ttf 85 | - family: Open Sans 86 | fonts: 87 | - asset: assets/fonts/Open_Sans/OpenSans-Light.ttf 88 | weight: 300 89 | - asset: assets/fonts/Open_Sans/OpenSans-LightItalic.ttf 90 | weight: 300 91 | style: italic 92 | - asset: assets/fonts/Open_Sans/OpenSans-Regular.ttf 93 | weight: 400 94 | - asset: assets/fonts/Open_Sans/OpenSans-Italic.ttf 95 | weight: 400 96 | style: italic 97 | - asset: assets/fonts/Open_Sans/OpenSans-SemiBold.ttf 98 | weight: 600 99 | - asset: assets/fonts/Open_Sans/OpenSans-SemiBoldItalic.ttf 100 | weight: 600 101 | style: italic 102 | - asset: assets/fonts/Open_Sans/OpenSans-Bold.ttf 103 | weight: 700 104 | - asset: assets/fonts/Open_Sans/OpenSans-BoldItalic.ttf 105 | weight: 700 106 | style: italic 107 | - asset: assets/fonts/Open_Sans/OpenSans-ExtraBold.ttf 108 | weight: 800 109 | - asset: assets/fonts/Open_Sans/OpenSans-ExtraBoldItalic.ttf 110 | weight: 800 111 | style: italic 112 | # 113 | # For details regarding fonts from package dependencies, 114 | # see https://flutter.dev/custom-fonts/#from-packages 115 | -------------------------------------------------------------------------------- /lib/providers/driver_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:http/http.dart' as http; 5 | import 'package:shared_preferences/shared_preferences.dart'; 6 | 7 | import '../helpers/firebase_utils.dart'; 8 | import '../helpers/http_exception.dart'; 9 | 10 | import '../models/driver.dart'; 11 | import '../models/car.dart'; 12 | 13 | import 'auth.dart'; 14 | 15 | class DriverProvider with ChangeNotifier { 16 | void update(Auth auth) { 17 | authToken = auth.token; 18 | driverId = auth.driverId; 19 | fetchDriverDetails(); 20 | cars; 21 | } 22 | 23 | late String? authToken; 24 | late String? driverId; 25 | 26 | late String _name; 27 | late String _email; 28 | late String _mobile; 29 | 30 | String get name => _name; 31 | String get email => _email; 32 | String get mobile => _mobile; 33 | 34 | List _cars = []; 35 | 36 | List get cars { 37 | return [..._cars]; 38 | } 39 | 40 | Driver _driver = Driver(); 41 | 42 | Driver get driver => _driver; 43 | 44 | bool _status = true; 45 | 46 | bool get status => _status; 47 | 48 | Future fetchDriverDetails() async { 49 | try { 50 | final url = '${DBUrls.drivers}/$driverId.json?auth=$authToken'; 51 | final response = await http.get(Uri.parse(url)); 52 | final data = json.decode(response.body); 53 | if (data == null) { 54 | return; 55 | } 56 | _name = data['name']; 57 | _email = data['email']; 58 | _mobile = data['mobile']; 59 | final carsData = data['cars']; 60 | final List loadedCars = []; 61 | if (carsData != null) { 62 | carsData.forEach((carId, carData) { 63 | loadedCars.insert( 64 | 0, 65 | Car( 66 | id: carId, 67 | carMake: carData['car_make'], 68 | carModel: carData['car_model'], 69 | carNumber: carData['car_number'], 70 | carColor: carData['car_color'], 71 | ), 72 | ); 73 | }); 74 | } 75 | _cars = loadedCars; 76 | _driver = Driver( 77 | id: driverId, 78 | name: name, 79 | email: email, 80 | mobile: int.tryParse(mobile), 81 | cars: cars, 82 | ); 83 | notifyListeners(); 84 | } catch (error) { 85 | print(error); 86 | } 87 | } 88 | 89 | Car findAddressById(String id) { 90 | return _cars.firstWhere((car) => car.id == id); 91 | } 92 | 93 | Future addCar({ 94 | String? carMake, 95 | String? carModel, 96 | String? carNumber, 97 | String? carColor, 98 | }) async { 99 | try { 100 | final url = '${DBUrls.drivers}/$driverId/cars.json?auth=$authToken'; 101 | final response = await http.post( 102 | Uri.parse(url), 103 | body: json.encode({ 104 | 'car_make': carMake, 105 | 'car_model': carModel, 106 | 'car_number': carNumber, 107 | 'car_color': carColor, 108 | }), 109 | ); 110 | final newCar = Car( 111 | id: json.decode(response.body)['name'], 112 | carMake: carMake, 113 | carModel: carModel, 114 | carNumber: carNumber, 115 | carColor: carColor, 116 | ); 117 | _cars.insert(0, newCar); 118 | notifyListeners(); 119 | } catch (error) { 120 | print(error); 121 | } 122 | } 123 | 124 | Future deleteCar(String id) async { 125 | final url = '${DBUrls.drivers}/$driverId/cars/$id.json?auth=$authToken'; 126 | final existingCarIndex = _cars.indexWhere((car) => car.id == id); 127 | Car? existingCar = _cars[existingCarIndex]; 128 | _cars.removeAt(existingCarIndex); 129 | notifyListeners(); 130 | final response = await http.delete(Uri.parse(url)); 131 | if (response.statusCode >= 400) { 132 | _cars.insert(existingCarIndex, existingCar); 133 | notifyListeners(); 134 | throw HttpException('Could not delete car.'); 135 | } 136 | existingCar = null; 137 | } 138 | 139 | void changeWorkMode(bool value) async { 140 | final prefs = await SharedPreferences.getInstance(); 141 | _status = value; 142 | await prefs.setBool('$driverId-status', _status); 143 | notifyListeners(); 144 | } 145 | 146 | Future tryStatus() async { 147 | final prefs = await SharedPreferences.getInstance(); 148 | if (!prefs.containsKey('$driverId-status')) { 149 | _status = true; 150 | } 151 | final extractedValue = prefs.getBool('$driverId-status')!; 152 | _status = extractedValue; 153 | notifyListeners(); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /assets/fonts/Viga/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Fontstage (info@fontstage.com), 2 | with Reserved Font Names, "Viga" 3 | 4 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 5 | This license is copied below, and is also available with a FAQ at: 6 | http://scripts.sil.org/OFL 7 | 8 | 9 | ----------------------------------------------------------- 10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 11 | ----------------------------------------------------------- 12 | 13 | PREAMBLE 14 | The goals of the Open Font License (OFL) are to stimulate worldwide 15 | development of collaborative font projects, to support the font creation 16 | efforts of academic and linguistic communities, and to provide a free and 17 | open framework in which fonts may be shared and improved in partnership 18 | with others. 19 | 20 | The OFL allows the licensed fonts to be used, studied, modified and 21 | redistributed freely as long as they are not sold by themselves. The 22 | fonts, including any derivative works, can be bundled, embedded, 23 | redistributed and/or sold with any software provided that any reserved 24 | names are not used by derivative works. The fonts and derivatives, 25 | however, cannot be released under any other type of license. The 26 | requirement for fonts to remain under this license does not apply 27 | to any document created using the fonts or their derivatives. 28 | 29 | DEFINITIONS 30 | "Font Software" refers to the set of files released by the Copyright 31 | Holder(s) under this license and clearly marked as such. This may 32 | include source files, build scripts and documentation. 33 | 34 | "Reserved Font Name" refers to any names specified as such after the 35 | copyright statement(s). 36 | 37 | "Original Version" refers to the collection of Font Software components as 38 | distributed by the Copyright Holder(s). 39 | 40 | "Modified Version" refers to any derivative made by adding to, deleting, 41 | or substituting -- in part or in whole -- any of the components of the 42 | Original Version, by changing formats or by porting the Font Software to a 43 | new environment. 44 | 45 | "Author" refers to any designer, engineer, programmer, technical 46 | writer or other person who contributed to the Font Software. 47 | 48 | PERMISSION & CONDITIONS 49 | Permission is hereby granted, free of charge, to any person obtaining 50 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 51 | redistribute, and sell modified and unmodified copies of the Font 52 | Software, subject to the following conditions: 53 | 54 | 1) Neither the Font Software nor any of its individual components, 55 | in Original or Modified Versions, may be sold by itself. 56 | 57 | 2) Original or Modified Versions of the Font Software may be bundled, 58 | redistributed and/or sold with any software, provided that each copy 59 | contains the above copyright notice and this license. These can be 60 | included either as stand-alone text files, human-readable headers or 61 | in the appropriate machine-readable metadata fields within text or 62 | binary files as long as those fields can be easily viewed by the user. 63 | 64 | 3) No Modified Version of the Font Software may use the Reserved Font 65 | Name(s) unless explicit written permission is granted by the corresponding 66 | Copyright Holder. This restriction only applies to the primary font name as 67 | presented to the users. 68 | 69 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 70 | Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except to acknowledge the contribution(s) of the 72 | Copyright Holder(s) and the Author(s) or with their explicit written 73 | permission. 74 | 75 | 5) The Font Software, modified or unmodified, in part or in whole, 76 | must be distributed entirely under this license, and must not be 77 | distributed under any other license. The requirement for fonts to 78 | remain under this license does not apply to any document created 79 | using the Font Software. 80 | 81 | TERMINATION 82 | This license becomes null and void if any of the above conditions are 83 | not met. 84 | 85 | DISCLAIMER 86 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 87 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 88 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 89 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 90 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 91 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 92 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 93 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 94 | OTHER DEALINGS IN THE FONT SOFTWARE. 95 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:provider/provider.dart'; 4 | import 'package:firebase_core/firebase_core.dart'; 5 | 6 | import 'providers/auth.dart'; 7 | import 'providers/maps_provider.dart'; 8 | import 'providers/driver_provider.dart'; 9 | 10 | import 'screens/auth_screen.dart'; 11 | import 'screens/home_screen.dart'; 12 | import 'screens/about_screen.dart'; 13 | import 'screens/splash_screen.dart'; 14 | import 'screens/navigation_bar.dart'; 15 | import 'screens/profile_screen.dart'; 16 | import 'screens/ratings_screen.dart'; 17 | import 'screens/all_cars_screen.dart'; 18 | import 'screens/earnings_screen.dart'; 19 | import 'screens/car_info_screen.dart'; 20 | 21 | Future main() async { 22 | WidgetsFlutterBinding.ensureInitialized(); 23 | SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); 24 | await Firebase.initializeApp(); 25 | runApp(MyApp()); 26 | } 27 | 28 | class MyApp extends StatelessWidget { 29 | @override 30 | Widget build(BuildContext context) { 31 | return MultiProvider( 32 | providers: [ 33 | ChangeNotifierProvider.value( 34 | value: Auth(), 35 | ), 36 | ChangeNotifierProxyProvider( 37 | create: (_) => DriverProvider(), 38 | update: (_, auth, driverData) => driverData!..update(auth), 39 | ), 40 | ChangeNotifierProxyProvider( 41 | create: (_) => MapsProvider(), 42 | update: (_, driver, mapsData) => mapsData!..update(driver), 43 | ), 44 | ], 45 | child: Consumer( 46 | builder: (ctx, auth, _) => Consumer( 47 | builder: (ctx, driver, _) => MaterialApp( 48 | title: 'Boomer Driver App', 49 | debugShowCheckedModeBanner: false, 50 | theme: themeData, 51 | home: auth.isAuth 52 | ? FutureBuilder( 53 | future: driver.fetchDriverDetails(), 54 | builder: (ctx, snapshot) => driver.cars.isNotEmpty 55 | ? NavigationBar() 56 | : CarInfoScreen(), 57 | ) 58 | : FutureBuilder( 59 | future: auth.tryAutoLogin(), 60 | builder: (ctx, snapshot) => 61 | snapshot.connectionState == ConnectionState.waiting 62 | ? SplashScreen() 63 | : AuthScreen(), 64 | ), 65 | routes: routes, 66 | ), 67 | ), 68 | ), 69 | ); 70 | } 71 | 72 | Map get routes { 73 | return { 74 | SplashScreen.routeName: (ctx) => SplashScreen(), 75 | AuthScreen.routeName: (ctx) => AuthScreen(), 76 | CarInfoScreen.routeName: (ctx) => CarInfoScreen(), 77 | NavigationBar.routeName: (ctx) => NavigationBar(), 78 | HomeScreen.routeName: (ctx) => HomeScreen(), 79 | EarningsScreen.routeName: (ctx) => EarningsScreen(), 80 | RatingsScreen.routeName: (ctx) => RatingsScreen(), 81 | ProfileScreen.routeName: (ctx) => ProfileScreen(), 82 | AboutScreen.routeName: (ctx) => AboutScreen(), 83 | AllCarsScreen.routeName: (ctx) => AllCarsScreen(), 84 | }; 85 | } 86 | } 87 | 88 | final themeData = ThemeData( 89 | brightness: Brightness.dark, 90 | primaryColor: Color(0xff423833), 91 | primaryColorLight: Color(0xffA28F86), 92 | primaryColorDark: Color(0xff342C28), 93 | accentColor: Color(0xffD1793F), 94 | fontFamily: 'Open Sans', 95 | focusColor: Color(0xffD1793F), 96 | scaffoldBackgroundColor: Color(0xff423833), 97 | canvasColor: Color(0xff342C28), 98 | // canvasColor: Color(0xffB8AAA3), 99 | textTheme: TextTheme( 100 | headline1: TextStyle( 101 | fontSize: 14, 102 | color: Color(0xff8A756B), 103 | fontWeight: FontWeight.w700, 104 | ), 105 | headline2: TextStyle( 106 | fontSize: 14, 107 | color: Color(0xff6D5D54), 108 | fontWeight: FontWeight.w500, 109 | ), 110 | headline3: TextStyle( 111 | color: Color(0xffB8AAA3), 112 | fontSize: 20, 113 | fontWeight: FontWeight.w600, 114 | ), 115 | headline4: TextStyle( 116 | fontSize: 20, 117 | color: Color(0xffD1793F), 118 | fontWeight: FontWeight.w700, 119 | ), 120 | headline5: TextStyle( 121 | fontSize: 20, 122 | color: Color(0xffFBFAF9), 123 | fontWeight: FontWeight.w700, 124 | ), 125 | headline6: TextStyle( 126 | color: Color(0xffA28F86), 127 | fontSize: 20, 128 | ), 129 | bodyText2: TextStyle( 130 | color: Color(0xffB8AAA3), 131 | fontSize: 14, 132 | ), 133 | ), 134 | appBarTheme: AppBarTheme( 135 | backgroundColor: Color(0xff423833), 136 | iconTheme: IconThemeData( 137 | color: Color(0xffD1793F), 138 | ), 139 | textTheme: TextTheme( 140 | headline6: TextStyle( 141 | color: Color(0xffA28F86), 142 | fontSize: 20, 143 | ), 144 | ), 145 | ), 146 | ); 147 | -------------------------------------------------------------------------------- /lib/screens/home_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | import 'package:google_maps_flutter/google_maps_flutter.dart'; 4 | 5 | import '../providers/maps_provider.dart'; 6 | import '../providers/driver_provider.dart'; 7 | 8 | import '../widgets/floating_appbar_wrapper.dart'; 9 | 10 | class HomeScreen extends StatefulWidget { 11 | const HomeScreen({Key? key}) : super(key: key); 12 | static const routeName = '/home-screen'; 13 | 14 | @override 15 | _HomeScreenState createState() => _HomeScreenState(); 16 | } 17 | 18 | class _HomeScreenState extends State { 19 | void _snackbar(String message) { 20 | ScaffoldMessenger.of(context).clearSnackBars(); 21 | ScaffoldMessenger.of(context).showSnackBar( 22 | SnackBar( 23 | content: Text( 24 | message, 25 | style: TextStyle( 26 | color: Theme.of(context).primaryColorDark, 27 | ), 28 | ), 29 | behavior: SnackBarBehavior.floating, 30 | margin: const EdgeInsets.fromLTRB(10.0, 5.0, 10.0, 10.0), 31 | backgroundColor: Theme.of(context).accentColor, 32 | ), 33 | ); 34 | } 35 | 36 | static final CameraPosition _kGooglePlex = CameraPosition( 37 | target: LatLng(37.42796133580664, -122.085749655962), 38 | zoom: 14.4746, 39 | ); 40 | 41 | @override 42 | void initState() { 43 | super.initState(); 44 | } 45 | 46 | @override 47 | void dispose() { 48 | super.dispose(); 49 | } 50 | 51 | @override 52 | Widget build(BuildContext context) { 53 | var query = MediaQuery.of(context).size; 54 | return Scaffold( 55 | body: Consumer( 56 | builder: (ctx, driver, _) => Consumer( 57 | builder: (ctx, maps, _) => FutureBuilder( 58 | future: driver.tryStatus(), 59 | builder: (ctx, snapshot) => Stack( 60 | children: [ 61 | driver.status 62 | ? FutureBuilder( 63 | future: maps.checkPermissions(), 64 | builder: (ctx, snapshot) => maps.isPermissionsInit 65 | ? CircularProgressIndicator( 66 | color: Theme.of(context).accentColor, 67 | ) 68 | : GoogleMap( 69 | myLocationEnabled: true, 70 | padding: EdgeInsets.all(12), 71 | initialCameraPosition: _kGooglePlex, 72 | onMapCreated: 73 | (GoogleMapController controller) async { 74 | try { 75 | await maps.setMapController(controller); 76 | } catch (error) { 77 | const errorMessage = 78 | 'Could not locate you. Please try again later.'; 79 | print(error); 80 | _snackbar(errorMessage + 81 | ' ' + 82 | error.toString()); 83 | } 84 | }, 85 | ), 86 | ) 87 | : Center( 88 | child: Icon( 89 | Icons.offline_bolt_rounded, 90 | size: query.width * 0.6, 91 | color: Theme.of(context).accentColor, 92 | ), 93 | ), 94 | Positioned( 95 | top: 0, 96 | child: FloatingAppBarWrapper( 97 | height: query.height * 0.072, 98 | width: query.width, 99 | children: [ 100 | Padding( 101 | padding: const EdgeInsets.only(left: 20), 102 | child: Text( 103 | driver.status ? 'Online Now' : 'Offline', 104 | style: Theme.of(context).textTheme.headline4, 105 | ), 106 | ), 107 | Padding( 108 | padding: const EdgeInsets.symmetric(horizontal: 12), 109 | child: Switch.adaptive( 110 | value: driver.status, 111 | onChanged: (value) async { 112 | driver.changeWorkMode(value); 113 | value 114 | ? await maps.goOnline() 115 | : await maps.goOffline(); 116 | }, 117 | ), 118 | ), 119 | ], 120 | ), 121 | ), 122 | ], 123 | ), 124 | ), 125 | ), 126 | ), 127 | ); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /lib/providers/auth.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:async'; 3 | 4 | import 'package:flutter/material.dart'; 5 | import 'package:http/http.dart' as http; 6 | import 'package:firebase_auth/firebase_auth.dart'; 7 | import 'package:shared_preferences/shared_preferences.dart'; 8 | 9 | import '../helpers/http_exception.dart'; 10 | import '../helpers/firebase_utils.dart'; 11 | 12 | class Auth with ChangeNotifier { 13 | FirebaseAuth _auth = FirebaseAuth.instance; 14 | 15 | String? _token; 16 | DateTime? _expiryDate; 17 | String? _driverId; 18 | Timer? _authTimer; 19 | 20 | bool get isAuth { 21 | return token != null; 22 | } 23 | 24 | String? get token { 25 | if (_expiryDate != null && 26 | _expiryDate!.isAfter(DateTime.now()) && 27 | _token != null) { 28 | return _token; 29 | } 30 | return null; 31 | } 32 | 33 | String? get driverId { 34 | if (isAuth) { 35 | return _driverId; 36 | } 37 | return null; 38 | } 39 | 40 | Future signupWithEmail({ 41 | required String email, 42 | required String password, 43 | required String name, 44 | required String mobile, 45 | }) async { 46 | try { 47 | UserCredential userCredential = 48 | await _auth.createUserWithEmailAndPassword( 49 | email: email, 50 | password: password, 51 | ); 52 | User? user = userCredential.user; 53 | if (user != null) { 54 | IdTokenResult tokenRes = await user.getIdTokenResult(); 55 | _driverId = user.uid; 56 | _token = tokenRes.token; 57 | _expiryDate = tokenRes.expirationTime; 58 | _autoLogout(); 59 | notifyListeners(); 60 | final url = '${DBUrls.drivers}/$_driverId.json'; 61 | await http.put( 62 | Uri.parse(url), 63 | body: json.encode( 64 | { 65 | 'name': name, 66 | 'email': email, 67 | 'mobile': mobile, 68 | }, 69 | ), 70 | ); 71 | final prefs = await SharedPreferences.getInstance(); 72 | final userData = json.encode( 73 | { 74 | 'token': _token, 75 | 'userId': _driverId, 76 | 'expiryDate': _expiryDate!.toIso8601String(), 77 | }, 78 | ); 79 | await prefs.setString('userData', userData); 80 | } 81 | } catch (error) { 82 | print(error); 83 | throw error; 84 | } 85 | } 86 | 87 | Future login({ 88 | required String email, 89 | required String password, 90 | }) async { 91 | try { 92 | UserCredential userCredential = await _auth.signInWithEmailAndPassword( 93 | email: email, 94 | password: password, 95 | ); 96 | User? user = userCredential.user; 97 | if (user != null) { 98 | final url = '${DBUrls.drivers}/${user.uid}.json'; 99 | final response = await http.get(Uri.parse(url)); 100 | final checkUser = json.decode(response.body); 101 | if (checkUser == null) { 102 | throw HttpException('User does not exist'); 103 | } 104 | IdTokenResult tokenRes = await user.getIdTokenResult(); 105 | _driverId = user.uid; 106 | _token = tokenRes.token; 107 | _expiryDate = tokenRes.expirationTime; 108 | _autoLogout(); 109 | notifyListeners(); 110 | final prefs = await SharedPreferences.getInstance(); 111 | final userData = json.encode( 112 | { 113 | 'token': _token, 114 | 'userId': _driverId, 115 | 'expiryDate': _expiryDate!.toIso8601String(), 116 | }, 117 | ); 118 | await prefs.setString('userData', userData); 119 | } 120 | } catch (error) { 121 | print(error); 122 | throw error; 123 | } 124 | } 125 | 126 | Future tryAutoLogin() async { 127 | final prefs = await SharedPreferences.getInstance(); 128 | if (!prefs.containsKey('userData')) { 129 | return false; 130 | } 131 | final extractedData = json.decode(prefs.getString('userData')!); 132 | final expiryDate = DateTime.parse(extractedData['expiryDate'] as String); 133 | if (expiryDate.isBefore(DateTime.now())) { 134 | return false; 135 | } 136 | _token = extractedData['token'] as String; 137 | _driverId = extractedData['userId'] as String; 138 | _expiryDate = expiryDate; 139 | notifyListeners(); 140 | _autoLogout(); 141 | return true; 142 | } 143 | 144 | Future logout() async { 145 | await _auth.signOut(); 146 | _token = null; 147 | _driverId = null; 148 | _expiryDate = null; 149 | if (_authTimer != null) { 150 | _authTimer!.cancel(); 151 | _authTimer = null; 152 | } 153 | notifyListeners(); 154 | final prefs = await SharedPreferences.getInstance(); 155 | prefs.remove('userData'); 156 | prefs.remove('$driverId-status'); 157 | } 158 | 159 | void _autoLogout() { 160 | if (_authTimer != null) { 161 | _authTimer!.cancel(); 162 | } 163 | final expiry = _expiryDate!.difference(DateTime.now()).inSeconds; 164 | _authTimer = Timer( 165 | Duration(seconds: expiry), 166 | logout, 167 | ); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /lib/screens/about_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../widgets/tap_to_action.dart'; 4 | import '../widgets/side_tabbed_title.dart'; 5 | 6 | class AboutScreen extends StatelessWidget { 7 | const AboutScreen({Key? key}) : super(key: key); 8 | static const routeName = '/about'; 9 | @override 10 | Widget build(BuildContext context) { 11 | return Scaffold( 12 | appBar: AppBar( 13 | title: Text('About'), 14 | centerTitle: true, 15 | ), 16 | body: SingleChildScrollView( 17 | child: Column( 18 | children: [ 19 | SizedBox(height: 40), 20 | SideTabbedTitle('Boomer Driver App'), 21 | SizedBox(height: 20), 22 | ListTile( 23 | title: Text( 24 | 'An Uber-like application for Drivers made using Flutter.', 25 | style: Theme.of(context).textTheme.headline1), 26 | ), 27 | SizedBox(height: 40), 28 | SideTabbedTitle('External Services that app uses'), 29 | SizedBox(height: 20), 30 | ListTile( 31 | leading: Icon( 32 | Icons.circle, 33 | color: Color(0xff6D5D54), 34 | ), 35 | title: Text( 36 | 'Google Maps SDK for Android', 37 | style: Theme.of(context).textTheme.bodyText2, 38 | ), 39 | ), 40 | Divider(color: Color(0xff6D5D54)), 41 | ListTile( 42 | leading: Icon( 43 | Icons.circle, 44 | color: Color(0xff6D5D54), 45 | ), 46 | title: Text( 47 | 'Google Maps SDK for iOS', 48 | style: Theme.of(context).textTheme.bodyText2, 49 | ), 50 | ), 51 | Divider(color: Color(0xff6D5D54)), 52 | ListTile( 53 | leading: Icon( 54 | Icons.circle, 55 | color: Color(0xff6D5D54), 56 | ), 57 | title: Text( 58 | 'Firebase for authentication and database', 59 | style: Theme.of(context).textTheme.bodyText2, 60 | ), 61 | ), 62 | Divider(color: Color(0xff6D5D54)), 63 | ListTile( 64 | leading: Icon( 65 | Icons.circle, 66 | color: Color(0xff6D5D54), 67 | ), 68 | title: Text( 69 | 'Provider', 70 | style: Theme.of(context).textTheme.bodyText2, 71 | ), 72 | ), 73 | Divider(color: Color(0xff6D5D54)), 74 | ListTile( 75 | leading: Icon( 76 | Icons.circle, 77 | color: Color(0xff6D5D54), 78 | ), 79 | title: Text( 80 | 'Google Maps Flutter', 81 | style: Theme.of(context).textTheme.bodyText2, 82 | ), 83 | ), 84 | Divider(color: Color(0xff6D5D54)), 85 | ListTile( 86 | leading: Icon( 87 | Icons.circle, 88 | color: Color(0xff6D5D54), 89 | ), 90 | title: Text( 91 | 'Geolocator', 92 | style: Theme.of(context).textTheme.bodyText2, 93 | ), 94 | ), 95 | Divider(color: Color(0xff6D5D54)), 96 | ListTile( 97 | leading: Icon( 98 | Icons.circle, 99 | color: Color(0xff6D5D54), 100 | ), 101 | title: Text( 102 | 'Animated Text Kit', 103 | style: Theme.of(context).textTheme.bodyText2, 104 | ), 105 | ), 106 | Divider(color: Color(0xff6D5D54)), 107 | ListTile( 108 | leading: Icon( 109 | Icons.circle, 110 | color: Color(0xff6D5D54), 111 | ), 112 | title: Text( 113 | 'Flutter Geofire', 114 | style: Theme.of(context).textTheme.bodyText2, 115 | ), 116 | ), 117 | SizedBox(height: 20), 118 | Container( 119 | margin: const EdgeInsets.all(10), 120 | padding: const EdgeInsets.all(20), 121 | decoration: BoxDecoration( 122 | color: Theme.of(context).primaryColorDark.withOpacity(0.6), 123 | borderRadius: BorderRadius.circular(12), 124 | ), 125 | child: Text( 126 | 'This application is only for showcasing purposes.', 127 | style: Theme.of(context).textTheme.bodyText2, 128 | ), 129 | ), 130 | SizedBox(height: 20), 131 | ListTile( 132 | tileColor: Theme.of(context).primaryColorDark, 133 | contentPadding: const EdgeInsets.all(10), 134 | title: TapToActionText( 135 | label: 'Created By ', 136 | tapLabel: 'Gurmehar Bakshi', 137 | padding: const EdgeInsets.all(0), 138 | ), 139 | ), 140 | SizedBox(height: 70), 141 | ], 142 | ), 143 | ), 144 | ); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /lib/screens/profile_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | import '../providers/auth.dart'; 5 | import '../providers/maps_provider.dart'; 6 | import '../providers/driver_provider.dart'; 7 | 8 | import 'about_screen.dart'; 9 | import 'all_cars_screen.dart'; 10 | 11 | class ProfileScreen extends StatelessWidget { 12 | const ProfileScreen({Key? key}) : super(key: key); 13 | static const routeName = '/profile-screen'; 14 | @override 15 | Widget build(BuildContext context) { 16 | final driverData = Provider.of(context, listen: false); 17 | final driver = driverData.driver; 18 | return Scaffold( 19 | body: SafeArea( 20 | child: Padding( 21 | padding: const EdgeInsets.all(16.0), 22 | child: Column( 23 | children: [ 24 | ListTile( 25 | onTap: () { 26 | print('View Profile'); 27 | }, 28 | shape: RoundedRectangleBorder( 29 | borderRadius: BorderRadius.circular(12), 30 | ), 31 | tileColor: Theme.of(context).primaryColorDark.withOpacity(0.6), 32 | contentPadding: const EdgeInsets.symmetric( 33 | vertical: 8, 34 | horizontal: 16, 35 | ), 36 | leading: Image.asset('assets/images/user_icon.png'), 37 | title: Text( 38 | driver.name!, 39 | style: Theme.of(context).textTheme.headline3, 40 | ), 41 | subtitle: Text( 42 | driver.mobile.toString(), 43 | style: Theme.of(context).textTheme.headline1, 44 | ), 45 | trailing: Icon( 46 | Icons.edit, 47 | color: Color(0xffB8AAA3), 48 | ), 49 | ), 50 | SizedBox(height: 40), 51 | ListTile( 52 | shape: RoundedRectangleBorder( 53 | borderRadius: BorderRadius.circular(12), 54 | ), 55 | leading: Icon( 56 | Icons.money, 57 | color: Color(0xff6D5D54), 58 | ), 59 | title: Text( 60 | 'View Cars', 61 | style: Theme.of(context).textTheme.bodyText2, 62 | ), 63 | onTap: () { 64 | Navigator.of(context).pushNamed(AllCarsScreen.routeName); 65 | }, 66 | ), 67 | Divider(color: Color(0xff6D5D54)), 68 | ListTile( 69 | shape: RoundedRectangleBorder( 70 | borderRadius: BorderRadius.circular(12), 71 | ), 72 | leading: Icon( 73 | Icons.money, 74 | color: Color(0xff6D5D54), 75 | ), 76 | title: Text( 77 | 'Payments Methods', 78 | style: Theme.of(context).textTheme.bodyText2, 79 | ), 80 | onTap: () { 81 | // Navigator.of(context).pushNamed(PaymentMethodsScreen.routeName); 82 | }, 83 | ), 84 | Divider(color: Color(0xff6D5D54)), 85 | ListTile( 86 | shape: RoundedRectangleBorder( 87 | borderRadius: BorderRadius.circular(12), 88 | ), 89 | leading: Icon( 90 | Icons.history, 91 | color: Color(0xff6D5D54), 92 | ), 93 | title: Text( 94 | 'History', 95 | style: Theme.of(context).textTheme.bodyText2, 96 | ), 97 | onTap: () { 98 | // Navigator.of(context).pushNamed(HistoryScreen.routeName); 99 | }, 100 | ), 101 | Divider(color: Color(0xff6D5D54)), 102 | ListTile( 103 | shape: RoundedRectangleBorder( 104 | borderRadius: BorderRadius.circular(12), 105 | ), 106 | leading: Icon( 107 | Icons.info, 108 | color: Color(0xff6D5D54), 109 | ), 110 | title: Text( 111 | 'About', 112 | style: Theme.of(context).textTheme.bodyText2, 113 | ), 114 | onTap: () { 115 | Navigator.of(context).pushNamed(AboutScreen.routeName); 116 | }, 117 | ), 118 | Divider(color: Color(0xff6D5D54)), 119 | Expanded(child: Container()), 120 | ListTile( 121 | shape: RoundedRectangleBorder( 122 | borderRadius: BorderRadius.circular(12), 123 | ), 124 | leading: Icon( 125 | Icons.logout, 126 | color: Color(0xff6D5D54), 127 | ), 128 | title: Text( 129 | 'Logout', 130 | style: Theme.of(context).textTheme.bodyText2, 131 | ), 132 | onTap: () async { 133 | await Provider.of( 134 | context, 135 | listen: false, 136 | ).logout(); 137 | await Provider.of( 138 | context, 139 | listen: false, 140 | ).goOffline(); 141 | }, 142 | ), 143 | ], 144 | ), 145 | ), 146 | ), 147 | ); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /lib/screens/car_info_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | import '../providers/driver_provider.dart'; 5 | 6 | import '../helpers/http_exception.dart'; 7 | 8 | import '../widgets/custom_button.dart'; 9 | import '../widgets/custom_textfield.dart'; 10 | 11 | class CarInfoScreen extends StatefulWidget { 12 | CarInfoScreen({Key? key}) : super(key: key); 13 | 14 | static const routeName = '/car-info'; 15 | 16 | @override 17 | _CarInfoScreenState createState() => _CarInfoScreenState(); 18 | } 19 | 20 | class _CarInfoScreenState extends State { 21 | final GlobalKey _formKey = GlobalKey(); 22 | 23 | bool _isLoading = false; 24 | 25 | Map _carInfoData = { 26 | 'carMake': '', 27 | 'carModel': '', 28 | 'carNumber': '', 29 | 'carColor': '', 30 | }; 31 | 32 | void _showErrorDialog(String message) { 33 | showDialog( 34 | context: context, 35 | builder: (ctx) => AlertDialog( 36 | title: Text('An Error Occurred!'), 37 | content: Text(message), 38 | actions: [ 39 | TextButton( 40 | child: Text('Close'), 41 | onPressed: () { 42 | Navigator.of(ctx).pop(); 43 | }, 44 | ) 45 | ], 46 | ), 47 | ); 48 | } 49 | 50 | Future _submit() async { 51 | if (!_formKey.currentState!.validate()) { 52 | return; 53 | } 54 | _formKey.currentState!.save(); 55 | setState(() { 56 | _isLoading = true; 57 | }); 58 | try { 59 | await Provider.of( 60 | context, 61 | listen: false, 62 | ).addCar( 63 | carMake: (_carInfoData['carMake'] as String).trim(), 64 | carModel: _carInfoData['carModel'] as String, 65 | carNumber: (_carInfoData['carNumber'] as String).trim(), 66 | carColor: (_carInfoData['carColor'] as String).trim(), 67 | ); 68 | Navigator.of(context).pop(); 69 | } on HttpException catch (error) { 70 | var errorMessage = 'Request failed'; 71 | if (error.toString().contains('NETWORK_ERROR')) { 72 | errorMessage = 'Could not save information.'; 73 | } else if (error.toString().contains('CAR_NUMBER_EXISTS')) { 74 | errorMessage = 'This car number already exists in our database.'; 75 | } 76 | _showErrorDialog(errorMessage); 77 | } catch (error) { 78 | const errorMessage = 79 | 'Could not save car information. Please try again later.'; 80 | _showErrorDialog(errorMessage); 81 | print(error); 82 | } 83 | if (mounted) 84 | setState(() { 85 | _isLoading = false; 86 | }); 87 | } 88 | 89 | @override 90 | void initState() { 91 | super.initState(); 92 | } 93 | 94 | @override 95 | void dispose() { 96 | super.dispose(); 97 | } 98 | 99 | @override 100 | Widget build(BuildContext context) { 101 | return Scaffold( 102 | resizeToAvoidBottomInset: true, 103 | appBar: AppBar( 104 | title: Text('Enter Car Details'), 105 | elevation: 0, 106 | ), 107 | body: SingleChildScrollView( 108 | child: Padding( 109 | padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 40), 110 | child: Form( 111 | key: _formKey, 112 | child: Column( 113 | crossAxisAlignment: CrossAxisAlignment.center, 114 | mainAxisAlignment: MainAxisAlignment.start, 115 | mainAxisSize: MainAxisSize.min, 116 | children: [ 117 | CustomTextField( 118 | label: 'Car Make', 119 | textInputAction: TextInputAction.next, 120 | validator: (value) { 121 | if (value!.isEmpty) { 122 | return 'Field is empty'; 123 | } 124 | }, 125 | onSaved: (value) { 126 | _carInfoData['carMake'] = value!; 127 | }, 128 | ), 129 | SizedBox(height: 24), 130 | CustomTextField( 131 | label: 'Car Model', 132 | textInputAction: TextInputAction.next, 133 | validator: (value) { 134 | if (value!.isEmpty) { 135 | return 'Field is empty'; 136 | } 137 | }, 138 | onSaved: (value) { 139 | _carInfoData['carModel'] = value!; 140 | }, 141 | ), 142 | SizedBox(height: 24), 143 | CustomTextField( 144 | label: 'Car Number', 145 | textInputAction: TextInputAction.next, 146 | validator: (value) { 147 | if (value!.isEmpty) { 148 | return 'Field is empty'; 149 | } 150 | }, 151 | onSaved: (value) { 152 | _carInfoData['carNumber'] = value!; 153 | }, 154 | ), 155 | SizedBox(height: 24), 156 | CustomTextField( 157 | label: 'Car Color', 158 | textInputAction: TextInputAction.done, 159 | validator: (value) { 160 | if (value!.isEmpty) { 161 | return 'Field is empty'; 162 | } 163 | }, 164 | onSaved: (value) { 165 | _carInfoData['carColor'] = value!; 166 | }, 167 | ), 168 | SizedBox(height: 52), 169 | if (_isLoading) 170 | CircularProgressIndicator() 171 | else 172 | CustomButton( 173 | label: 'Continue', 174 | onTap: _submit, 175 | ), 176 | ], 177 | ), 178 | ), 179 | ), 180 | ), 181 | ); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /lib/screens/all_cars_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | import '../providers/driver_provider.dart'; 5 | 6 | import 'car_info_screen.dart'; 7 | 8 | class AllCarsScreen extends StatelessWidget { 9 | const AllCarsScreen({Key? key}) : super(key: key); 10 | static const routeName = '/all-cars-screen'; 11 | 12 | static const color = const Color(0xffB8AAA3); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Scaffold( 17 | appBar: AppBar( 18 | title: Text('All Cars'), 19 | actions: [ 20 | IconButton( 21 | icon: Icon(Icons.add), 22 | onPressed: () { 23 | Navigator.of(context).pushNamed(CarInfoScreen.routeName); 24 | }, 25 | ), 26 | ], 27 | ), 28 | body: Consumer( 29 | builder: (ctx, driver, _) => SingleChildScrollView( 30 | child: Padding( 31 | padding: const EdgeInsets.fromLTRB(16, 20, 16, 0), 32 | child: Column( 33 | children: driver.cars 34 | .map( 35 | (car) => Container( 36 | margin: const EdgeInsets.only(bottom: 12.0), 37 | decoration: BoxDecoration( 38 | color: Theme.of(context).primaryColorDark, 39 | borderRadius: BorderRadius.circular(12), 40 | ), 41 | child: ListTile( 42 | shape: RoundedRectangleBorder( 43 | borderRadius: BorderRadius.circular(12), 44 | ), 45 | leading: Image.asset('assets/images/car_ios.png'), 46 | title: Text( 47 | '${car.carMake!} ${car.carModel!}', 48 | style: TextStyle( 49 | color: color, 50 | fontWeight: FontWeight.w600, 51 | ), 52 | ), 53 | subtitle: Text( 54 | car.carNumber!, 55 | style: TextStyle(color: color), 56 | ), 57 | trailing: driver.cars.length <= 1 58 | ? null 59 | : IconButton( 60 | icon: Icon(Icons.delete), 61 | color: color, 62 | onPressed: () async { 63 | try { 64 | bool confirm = await showDialog( 65 | context: context, 66 | builder: (ctx) => AlertDialog( 67 | shape: RoundedRectangleBorder( 68 | borderRadius: 69 | BorderRadius.circular(12), 70 | ), 71 | title: Text('Are you sure?'), 72 | content: Text( 73 | 'Do you want to delete this car?', 74 | ), 75 | actions: [ 76 | TextButton( 77 | child: Text( 78 | 'No', 79 | style: TextStyle( 80 | color: Theme.of(context) 81 | .accentColor), 82 | ), 83 | onPressed: () { 84 | Navigator.of(ctx).pop(false); 85 | }, 86 | ), 87 | TextButton( 88 | child: Text( 89 | 'Yes', 90 | style: TextStyle( 91 | color: Theme.of(context) 92 | .accentColor), 93 | ), 94 | onPressed: () { 95 | Navigator.of(ctx).pop(true); 96 | }, 97 | ), 98 | ], 99 | ), 100 | ); 101 | if (confirm) { 102 | await driver.deleteCar(car.id!); 103 | } else { 104 | return; 105 | } 106 | } catch (error) { 107 | ScaffoldMessenger.of(context).showSnackBar( 108 | SnackBar( 109 | content: Text( 110 | error.toString(), 111 | style: TextStyle( 112 | color: Theme.of(context) 113 | .primaryColorDark, 114 | ), 115 | ), 116 | behavior: SnackBarBehavior.floating, 117 | margin: const EdgeInsets.fromLTRB( 118 | 10.0, 119 | 5.0, 120 | 10.0, 121 | 10.0, 122 | ), 123 | backgroundColor: 124 | Theme.of(context).accentColor, 125 | ), 126 | ); 127 | } 128 | }, 129 | ), 130 | ), 131 | ), 132 | ) 133 | .toList(), 134 | ), 135 | ), 136 | ), 137 | ), 138 | ); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Firebase/Analytics (8.5.0): 3 | - Firebase/Core 4 | - Firebase/Auth (8.5.0): 5 | - Firebase/CoreOnly 6 | - FirebaseAuth (~> 8.5.0) 7 | - Firebase/Core (8.5.0): 8 | - Firebase/CoreOnly 9 | - FirebaseAnalytics (~> 8.5.0) 10 | - Firebase/CoreOnly (8.5.0): 11 | - FirebaseCore (= 8.5.0) 12 | - Firebase/Database (8.5.0): 13 | - Firebase/CoreOnly 14 | - FirebaseDatabase (~> 8.5.0) 15 | - firebase_auth (3.0.2): 16 | - Firebase/Auth (= 8.5.0) 17 | - firebase_core 18 | - Flutter 19 | - firebase_core (1.5.0): 20 | - Firebase/CoreOnly (= 8.5.0) 21 | - Flutter 22 | - FirebaseAnalytics (8.5.0): 23 | - FirebaseAnalytics/AdIdSupport (= 8.5.0) 24 | - FirebaseCore (~> 8.0) 25 | - FirebaseInstallations (~> 8.0) 26 | - GoogleUtilities/AppDelegateSwizzler (~> 7.4) 27 | - GoogleUtilities/MethodSwizzler (~> 7.4) 28 | - GoogleUtilities/Network (~> 7.4) 29 | - "GoogleUtilities/NSData+zlib (~> 7.4)" 30 | - nanopb (~> 2.30908.0) 31 | - FirebaseAnalytics/AdIdSupport (8.5.0): 32 | - FirebaseCore (~> 8.0) 33 | - FirebaseInstallations (~> 8.0) 34 | - GoogleAppMeasurement (= 8.5.0) 35 | - GoogleUtilities/AppDelegateSwizzler (~> 7.4) 36 | - GoogleUtilities/MethodSwizzler (~> 7.4) 37 | - GoogleUtilities/Network (~> 7.4) 38 | - "GoogleUtilities/NSData+zlib (~> 7.4)" 39 | - nanopb (~> 2.30908.0) 40 | - FirebaseAuth (8.5.0): 41 | - FirebaseCore (~> 8.0) 42 | - GoogleUtilities/AppDelegateSwizzler (~> 7.4) 43 | - GoogleUtilities/Environment (~> 7.4) 44 | - GTMSessionFetcher/Core (~> 1.5) 45 | - FirebaseCore (8.5.0): 46 | - FirebaseCoreDiagnostics (~> 8.0) 47 | - GoogleUtilities/Environment (~> 7.4) 48 | - GoogleUtilities/Logger (~> 7.4) 49 | - FirebaseCoreDiagnostics (8.5.0): 50 | - GoogleDataTransport (~> 9.0) 51 | - GoogleUtilities/Environment (~> 7.4) 52 | - GoogleUtilities/Logger (~> 7.4) 53 | - nanopb (~> 2.30908.0) 54 | - FirebaseDatabase (8.5.0): 55 | - FirebaseCore (~> 8.0) 56 | - leveldb-library (~> 1.22) 57 | - FirebaseInstallations (8.5.0): 58 | - FirebaseCore (~> 8.0) 59 | - GoogleUtilities/Environment (~> 7.4) 60 | - GoogleUtilities/UserDefaults (~> 7.4) 61 | - PromisesObjC (< 3.0, >= 1.2) 62 | - Flutter (1.0.0) 63 | - flutter_geofire (0.0.1): 64 | - Flutter 65 | - GeoFire (~> 4.0) 66 | - GeoFire (4.3.0): 67 | - GeoFire/Database (= 4.3.0) 68 | - GeoFire/Database (4.3.0): 69 | - Firebase/Database (< 9.0.0, > 7.0.0) 70 | - GeoFire/Utils 71 | - GeoFire/Utils (4.3.0) 72 | - geolocator_apple (1.0.0): 73 | - Flutter 74 | - google_maps_flutter (0.0.1): 75 | - Flutter 76 | - GoogleMaps 77 | - GoogleAppMeasurement (8.5.0): 78 | - GoogleAppMeasurement/AdIdSupport (= 8.5.0) 79 | - GoogleUtilities/AppDelegateSwizzler (~> 7.4) 80 | - GoogleUtilities/MethodSwizzler (~> 7.4) 81 | - GoogleUtilities/Network (~> 7.4) 82 | - "GoogleUtilities/NSData+zlib (~> 7.4)" 83 | - nanopb (~> 2.30908.0) 84 | - GoogleAppMeasurement/AdIdSupport (8.5.0): 85 | - GoogleUtilities/AppDelegateSwizzler (~> 7.4) 86 | - GoogleUtilities/MethodSwizzler (~> 7.4) 87 | - GoogleUtilities/Network (~> 7.4) 88 | - "GoogleUtilities/NSData+zlib (~> 7.4)" 89 | - nanopb (~> 2.30908.0) 90 | - GoogleDataTransport (9.1.0): 91 | - GoogleUtilities/Environment (~> 7.2) 92 | - nanopb (~> 2.30908.0) 93 | - PromisesObjC (< 3.0, >= 1.2) 94 | - GoogleMaps (5.1.0): 95 | - GoogleMaps/Maps (= 5.1.0) 96 | - GoogleMaps/Base (5.1.0) 97 | - GoogleMaps/Maps (5.1.0): 98 | - GoogleMaps/Base 99 | - GoogleUtilities/AppDelegateSwizzler (7.5.0): 100 | - GoogleUtilities/Environment 101 | - GoogleUtilities/Logger 102 | - GoogleUtilities/Network 103 | - GoogleUtilities/Environment (7.5.0): 104 | - PromisesObjC (< 3.0, >= 1.2) 105 | - GoogleUtilities/Logger (7.5.0): 106 | - GoogleUtilities/Environment 107 | - GoogleUtilities/MethodSwizzler (7.5.0): 108 | - GoogleUtilities/Logger 109 | - GoogleUtilities/Network (7.5.0): 110 | - GoogleUtilities/Logger 111 | - "GoogleUtilities/NSData+zlib" 112 | - GoogleUtilities/Reachability 113 | - "GoogleUtilities/NSData+zlib (7.5.0)" 114 | - GoogleUtilities/Reachability (7.5.0): 115 | - GoogleUtilities/Logger 116 | - GoogleUtilities/UserDefaults (7.5.0): 117 | - GoogleUtilities/Logger 118 | - GTMSessionFetcher/Core (1.6.1) 119 | - leveldb-library (1.22.1) 120 | - nanopb (2.30908.0): 121 | - nanopb/decode (= 2.30908.0) 122 | - nanopb/encode (= 2.30908.0) 123 | - nanopb/decode (2.30908.0) 124 | - nanopb/encode (2.30908.0) 125 | - PromisesObjC (2.0.0) 126 | - shared_preferences (0.0.1): 127 | - Flutter 128 | 129 | DEPENDENCIES: 130 | - Firebase/Analytics 131 | - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) 132 | - firebase_core (from `.symlinks/plugins/firebase_core/ios`) 133 | - Flutter (from `Flutter`) 134 | - flutter_geofire (from `.symlinks/plugins/flutter_geofire/ios`) 135 | - geolocator_apple (from `.symlinks/plugins/geolocator_apple/ios`) 136 | - google_maps_flutter (from `.symlinks/plugins/google_maps_flutter/ios`) 137 | - GoogleMaps 138 | - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) 139 | 140 | SPEC REPOS: 141 | trunk: 142 | - Firebase 143 | - FirebaseAnalytics 144 | - FirebaseAuth 145 | - FirebaseCore 146 | - FirebaseCoreDiagnostics 147 | - FirebaseDatabase 148 | - FirebaseInstallations 149 | - GeoFire 150 | - GoogleAppMeasurement 151 | - GoogleDataTransport 152 | - GoogleMaps 153 | - GoogleUtilities 154 | - GTMSessionFetcher 155 | - leveldb-library 156 | - nanopb 157 | - PromisesObjC 158 | 159 | EXTERNAL SOURCES: 160 | firebase_auth: 161 | :path: ".symlinks/plugins/firebase_auth/ios" 162 | firebase_core: 163 | :path: ".symlinks/plugins/firebase_core/ios" 164 | Flutter: 165 | :path: Flutter 166 | flutter_geofire: 167 | :path: ".symlinks/plugins/flutter_geofire/ios" 168 | geolocator_apple: 169 | :path: ".symlinks/plugins/geolocator_apple/ios" 170 | google_maps_flutter: 171 | :path: ".symlinks/plugins/google_maps_flutter/ios" 172 | shared_preferences: 173 | :path: ".symlinks/plugins/shared_preferences/ios" 174 | 175 | SPEC CHECKSUMS: 176 | Firebase: ff8c73105b90e33e1dc6c8e5445d7adc2ccdc7c1 177 | firebase_auth: 214ff86facd807bbb0ccff32f4b2d3865e3bc4f3 178 | firebase_core: 82d486a6231b636aea229bd471bceca82cbe00a6 179 | FirebaseAnalytics: 96325c1e0acbd2bb805c6a613028b1fe599d6a37 180 | FirebaseAuth: b152ea261b60eeb9419ae7e5bf34761382b33277 181 | FirebaseCore: 1c1ca72483b59b17050f5b4cec4fb748425a3901 182 | FirebaseCoreDiagnostics: 7bf55d386f9fc690d971b70a582142321a390eb8 183 | FirebaseDatabase: 65c3742ed355f9b9db222036fd154e699ab7d672 184 | FirebaseInstallations: f2bc590b291d25fb40a9a05b8281c02a881b5117 185 | Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c 186 | flutter_geofire: 1fcdd7f26157b6fbc99bdf4bb41b957bef9e33d5 187 | GeoFire: c34927b5c81ab614f611c79aa8d074cfa9780f07 188 | geolocator_apple: 60a1c3f5930c090d2f5164ba8bf25f4f89121e3c 189 | google_maps_flutter: df4e7de95264aa0a2f11aac0fc7e313acb8ffc7e 190 | GoogleAppMeasurement: 8d10c1c470fcb0e5143ed74fddd164f0a0384800 191 | GoogleDataTransport: 85fd18ff3019bb85d3f2c551d04c481dedf71fc9 192 | GoogleMaps: 086ae1dab659eaa7339bdb30fc88e2f44e7c597c 193 | GoogleUtilities: eea970f4a389963963bffe8d8fabe43540678b9c 194 | GTMSessionFetcher: 36689134877faeb055b27dfa4ccc9ceaa42e029e 195 | leveldb-library: 50c7b45cbd7bf543c81a468fe557a16ae3db8729 196 | nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 197 | PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58 198 | shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d 199 | 200 | PODFILE CHECKSUM: 920e8c1fb9095c7cf80f566d7748b4c285a551c4 201 | 202 | COCOAPODS: 1.10.1 203 | -------------------------------------------------------------------------------- /assets/fonts/Open_Sans/LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | animated_text_kit: 5 | dependency: "direct main" 6 | description: 7 | name: animated_text_kit 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "4.2.1" 11 | archive: 12 | dependency: transitive 13 | description: 14 | name: archive 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "3.1.2" 18 | args: 19 | dependency: transitive 20 | description: 21 | name: args 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "2.2.0" 25 | async: 26 | dependency: transitive 27 | description: 28 | name: async 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "2.6.1" 32 | boolean_selector: 33 | dependency: transitive 34 | description: 35 | name: boolean_selector 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "2.1.0" 39 | characters: 40 | dependency: transitive 41 | description: 42 | name: characters 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.1.0" 46 | charcode: 47 | dependency: transitive 48 | description: 49 | name: charcode 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.2.0" 53 | clock: 54 | dependency: transitive 55 | description: 56 | name: clock 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.1.0" 60 | collection: 61 | dependency: transitive 62 | description: 63 | name: collection 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "1.15.0" 67 | crypto: 68 | dependency: transitive 69 | description: 70 | name: crypto 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "3.0.1" 74 | cupertino_icons: 75 | dependency: "direct main" 76 | description: 77 | name: cupertino_icons 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "1.0.3" 81 | fake_async: 82 | dependency: transitive 83 | description: 84 | name: fake_async 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "1.2.0" 88 | ffi: 89 | dependency: transitive 90 | description: 91 | name: ffi 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "1.1.2" 95 | file: 96 | dependency: transitive 97 | description: 98 | name: file 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "6.1.2" 102 | firebase_auth: 103 | dependency: "direct main" 104 | description: 105 | name: firebase_auth 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "3.0.2" 109 | firebase_auth_platform_interface: 110 | dependency: transitive 111 | description: 112 | name: firebase_auth_platform_interface 113 | url: "https://pub.dartlang.org" 114 | source: hosted 115 | version: "6.0.1" 116 | firebase_auth_web: 117 | dependency: transitive 118 | description: 119 | name: firebase_auth_web 120 | url: "https://pub.dartlang.org" 121 | source: hosted 122 | version: "3.0.1" 123 | firebase_core: 124 | dependency: "direct main" 125 | description: 126 | name: firebase_core 127 | url: "https://pub.dartlang.org" 128 | source: hosted 129 | version: "1.5.0" 130 | firebase_core_platform_interface: 131 | dependency: transitive 132 | description: 133 | name: firebase_core_platform_interface 134 | url: "https://pub.dartlang.org" 135 | source: hosted 136 | version: "4.0.1" 137 | firebase_core_web: 138 | dependency: transitive 139 | description: 140 | name: firebase_core_web 141 | url: "https://pub.dartlang.org" 142 | source: hosted 143 | version: "1.1.0" 144 | flutter: 145 | dependency: "direct main" 146 | description: flutter 147 | source: sdk 148 | version: "0.0.0" 149 | flutter_geofire: 150 | dependency: "direct main" 151 | description: 152 | name: flutter_geofire 153 | url: "https://pub.dartlang.org" 154 | source: hosted 155 | version: "2.0.0" 156 | flutter_launcher_icons: 157 | dependency: "direct dev" 158 | description: 159 | name: flutter_launcher_icons 160 | url: "https://pub.dartlang.org" 161 | source: hosted 162 | version: "0.9.2" 163 | flutter_plugin_android_lifecycle: 164 | dependency: transitive 165 | description: 166 | name: flutter_plugin_android_lifecycle 167 | url: "https://pub.dartlang.org" 168 | source: hosted 169 | version: "2.0.2" 170 | flutter_polyline_points: 171 | dependency: "direct main" 172 | description: 173 | name: flutter_polyline_points 174 | url: "https://pub.dartlang.org" 175 | source: hosted 176 | version: "1.0.0" 177 | flutter_test: 178 | dependency: "direct dev" 179 | description: flutter 180 | source: sdk 181 | version: "0.0.0" 182 | flutter_web_plugins: 183 | dependency: transitive 184 | description: flutter 185 | source: sdk 186 | version: "0.0.0" 187 | geolocator: 188 | dependency: "direct main" 189 | description: 190 | name: geolocator 191 | url: "https://pub.dartlang.org" 192 | source: hosted 193 | version: "7.4.0" 194 | geolocator_android: 195 | dependency: transitive 196 | description: 197 | name: geolocator_android 198 | url: "https://pub.dartlang.org" 199 | source: hosted 200 | version: "1.0.0" 201 | geolocator_apple: 202 | dependency: transitive 203 | description: 204 | name: geolocator_apple 205 | url: "https://pub.dartlang.org" 206 | source: hosted 207 | version: "1.0.0" 208 | geolocator_platform_interface: 209 | dependency: transitive 210 | description: 211 | name: geolocator_platform_interface 212 | url: "https://pub.dartlang.org" 213 | source: hosted 214 | version: "2.3.2" 215 | geolocator_web: 216 | dependency: transitive 217 | description: 218 | name: geolocator_web 219 | url: "https://pub.dartlang.org" 220 | source: hosted 221 | version: "2.0.6" 222 | google_maps_flutter: 223 | dependency: "direct main" 224 | description: 225 | name: google_maps_flutter 226 | url: "https://pub.dartlang.org" 227 | source: hosted 228 | version: "2.0.6" 229 | google_maps_flutter_platform_interface: 230 | dependency: transitive 231 | description: 232 | name: google_maps_flutter_platform_interface 233 | url: "https://pub.dartlang.org" 234 | source: hosted 235 | version: "2.1.1" 236 | http: 237 | dependency: "direct main" 238 | description: 239 | name: http 240 | url: "https://pub.dartlang.org" 241 | source: hosted 242 | version: "0.13.3" 243 | http_parser: 244 | dependency: transitive 245 | description: 246 | name: http_parser 247 | url: "https://pub.dartlang.org" 248 | source: hosted 249 | version: "4.0.0" 250 | image: 251 | dependency: transitive 252 | description: 253 | name: image 254 | url: "https://pub.dartlang.org" 255 | source: hosted 256 | version: "3.0.2" 257 | intl: 258 | dependency: transitive 259 | description: 260 | name: intl 261 | url: "https://pub.dartlang.org" 262 | source: hosted 263 | version: "0.17.0" 264 | js: 265 | dependency: transitive 266 | description: 267 | name: js 268 | url: "https://pub.dartlang.org" 269 | source: hosted 270 | version: "0.6.3" 271 | matcher: 272 | dependency: transitive 273 | description: 274 | name: matcher 275 | url: "https://pub.dartlang.org" 276 | source: hosted 277 | version: "0.12.10" 278 | meta: 279 | dependency: transitive 280 | description: 281 | name: meta 282 | url: "https://pub.dartlang.org" 283 | source: hosted 284 | version: "1.3.0" 285 | nested: 286 | dependency: transitive 287 | description: 288 | name: nested 289 | url: "https://pub.dartlang.org" 290 | source: hosted 291 | version: "1.0.0" 292 | path: 293 | dependency: transitive 294 | description: 295 | name: path 296 | url: "https://pub.dartlang.org" 297 | source: hosted 298 | version: "1.8.0" 299 | path_provider_linux: 300 | dependency: transitive 301 | description: 302 | name: path_provider_linux 303 | url: "https://pub.dartlang.org" 304 | source: hosted 305 | version: "2.0.2" 306 | path_provider_platform_interface: 307 | dependency: transitive 308 | description: 309 | name: path_provider_platform_interface 310 | url: "https://pub.dartlang.org" 311 | source: hosted 312 | version: "2.0.1" 313 | path_provider_windows: 314 | dependency: transitive 315 | description: 316 | name: path_provider_windows 317 | url: "https://pub.dartlang.org" 318 | source: hosted 319 | version: "2.0.3" 320 | pedantic: 321 | dependency: transitive 322 | description: 323 | name: pedantic 324 | url: "https://pub.dartlang.org" 325 | source: hosted 326 | version: "1.11.1" 327 | petitparser: 328 | dependency: transitive 329 | description: 330 | name: petitparser 331 | url: "https://pub.dartlang.org" 332 | source: hosted 333 | version: "4.1.0" 334 | platform: 335 | dependency: transitive 336 | description: 337 | name: platform 338 | url: "https://pub.dartlang.org" 339 | source: hosted 340 | version: "3.0.2" 341 | plugin_platform_interface: 342 | dependency: transitive 343 | description: 344 | name: plugin_platform_interface 345 | url: "https://pub.dartlang.org" 346 | source: hosted 347 | version: "2.0.1" 348 | process: 349 | dependency: transitive 350 | description: 351 | name: process 352 | url: "https://pub.dartlang.org" 353 | source: hosted 354 | version: "4.2.3" 355 | provider: 356 | dependency: "direct main" 357 | description: 358 | name: provider 359 | url: "https://pub.dartlang.org" 360 | source: hosted 361 | version: "5.0.0" 362 | shared_preferences: 363 | dependency: "direct main" 364 | description: 365 | name: shared_preferences 366 | url: "https://pub.dartlang.org" 367 | source: hosted 368 | version: "2.0.7" 369 | shared_preferences_linux: 370 | dependency: transitive 371 | description: 372 | name: shared_preferences_linux 373 | url: "https://pub.dartlang.org" 374 | source: hosted 375 | version: "2.0.2" 376 | shared_preferences_macos: 377 | dependency: transitive 378 | description: 379 | name: shared_preferences_macos 380 | url: "https://pub.dartlang.org" 381 | source: hosted 382 | version: "2.0.2" 383 | shared_preferences_platform_interface: 384 | dependency: transitive 385 | description: 386 | name: shared_preferences_platform_interface 387 | url: "https://pub.dartlang.org" 388 | source: hosted 389 | version: "2.0.0" 390 | shared_preferences_web: 391 | dependency: transitive 392 | description: 393 | name: shared_preferences_web 394 | url: "https://pub.dartlang.org" 395 | source: hosted 396 | version: "2.0.2" 397 | shared_preferences_windows: 398 | dependency: transitive 399 | description: 400 | name: shared_preferences_windows 401 | url: "https://pub.dartlang.org" 402 | source: hosted 403 | version: "2.0.2" 404 | sky_engine: 405 | dependency: transitive 406 | description: flutter 407 | source: sdk 408 | version: "0.0.99" 409 | source_span: 410 | dependency: transitive 411 | description: 412 | name: source_span 413 | url: "https://pub.dartlang.org" 414 | source: hosted 415 | version: "1.8.1" 416 | stack_trace: 417 | dependency: transitive 418 | description: 419 | name: stack_trace 420 | url: "https://pub.dartlang.org" 421 | source: hosted 422 | version: "1.10.0" 423 | stream_channel: 424 | dependency: transitive 425 | description: 426 | name: stream_channel 427 | url: "https://pub.dartlang.org" 428 | source: hosted 429 | version: "2.1.0" 430 | stream_transform: 431 | dependency: transitive 432 | description: 433 | name: stream_transform 434 | url: "https://pub.dartlang.org" 435 | source: hosted 436 | version: "2.0.0" 437 | string_scanner: 438 | dependency: transitive 439 | description: 440 | name: string_scanner 441 | url: "https://pub.dartlang.org" 442 | source: hosted 443 | version: "1.1.0" 444 | term_glyph: 445 | dependency: transitive 446 | description: 447 | name: term_glyph 448 | url: "https://pub.dartlang.org" 449 | source: hosted 450 | version: "1.2.0" 451 | test_api: 452 | dependency: transitive 453 | description: 454 | name: test_api 455 | url: "https://pub.dartlang.org" 456 | source: hosted 457 | version: "0.3.0" 458 | typed_data: 459 | dependency: transitive 460 | description: 461 | name: typed_data 462 | url: "https://pub.dartlang.org" 463 | source: hosted 464 | version: "1.3.0" 465 | vector_math: 466 | dependency: transitive 467 | description: 468 | name: vector_math 469 | url: "https://pub.dartlang.org" 470 | source: hosted 471 | version: "2.1.0" 472 | win32: 473 | dependency: transitive 474 | description: 475 | name: win32 476 | url: "https://pub.dartlang.org" 477 | source: hosted 478 | version: "2.2.6" 479 | xdg_directories: 480 | dependency: transitive 481 | description: 482 | name: xdg_directories 483 | url: "https://pub.dartlang.org" 484 | source: hosted 485 | version: "0.2.0" 486 | xml: 487 | dependency: transitive 488 | description: 489 | name: xml 490 | url: "https://pub.dartlang.org" 491 | source: hosted 492 | version: "5.1.2" 493 | yaml: 494 | dependency: transitive 495 | description: 496 | name: yaml 497 | url: "https://pub.dartlang.org" 498 | source: hosted 499 | version: "3.1.0" 500 | sdks: 501 | dart: ">=2.13.0 <3.0.0" 502 | flutter: ">=2.0.0" 503 | -------------------------------------------------------------------------------- /lib/screens/auth_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | import 'package:firebase_auth/firebase_auth.dart'; 4 | 5 | import '../helpers/http_exception.dart'; 6 | 7 | import '../providers/auth.dart'; 8 | 9 | import '../widgets/splashed_flex.dart'; 10 | import '../widgets/side_tabbed_title.dart'; 11 | import '../widgets/tap_to_action.dart'; 12 | import '../widgets/custom_button.dart'; 13 | import '../widgets/custom_textfield.dart'; 14 | 15 | import 'car_info_screen.dart'; 16 | 17 | enum AuthMode { Signup, Login } 18 | 19 | class AuthScreen extends StatefulWidget { 20 | const AuthScreen({Key? key}) : super(key: key); 21 | 22 | static const routeName = '/auth'; 23 | 24 | @override 25 | _AuthScreenState createState() => _AuthScreenState(); 26 | } 27 | 28 | class _AuthScreenState extends State 29 | with SingleTickerProviderStateMixin { 30 | final GlobalKey _formKey = GlobalKey(); 31 | 32 | final _passwordController = TextEditingController(); 33 | 34 | final _focusNode = FocusNode(); 35 | 36 | bool _obscure = true; 37 | 38 | bool _isLoading = false; 39 | 40 | AuthMode _authMode = AuthMode.Login; 41 | 42 | late AnimationController _controller; 43 | 44 | late Animation _slideAnimation; 45 | 46 | late Animation _opacityAnimation; 47 | 48 | Map _authData = { 49 | 'name': '', 50 | 'email': '', 51 | 'mobile': '', 52 | 'password': '', 53 | }; 54 | 55 | void _showErrorDialog(String message) { 56 | showDialog( 57 | context: context, 58 | builder: (ctx) => AlertDialog( 59 | title: Text('An Error Occurred!'), 60 | content: Text(message), 61 | actions: [ 62 | TextButton( 63 | child: Text('Close'), 64 | onPressed: () { 65 | Navigator.of(ctx).pop(); 66 | }, 67 | ) 68 | ], 69 | ), 70 | ); 71 | } 72 | 73 | Future _submit() async { 74 | if (!_formKey.currentState!.validate()) { 75 | return; 76 | } 77 | _formKey.currentState!.save(); 78 | setState(() { 79 | _isLoading = true; 80 | }); 81 | try { 82 | if (_authMode == AuthMode.Login) { 83 | await Provider.of(context, listen: false).login( 84 | email: (_authData['email'] as String).trim(), 85 | password: _authData['password'] as String, 86 | ); 87 | } else { 88 | Navigator.of(context).pushNamed(CarInfoScreen.routeName); 89 | await Provider.of(context, listen: false).signupWithEmail( 90 | email: (_authData['email'] as String).trim(), 91 | password: _authData['password'] as String, 92 | name: (_authData['name'] as String).trim(), 93 | mobile: (_authData['mobile'] as String).trim(), 94 | ); 95 | } 96 | } on FirebaseAuthException catch (e) { 97 | var errorMessage = 'Firebase Authentication failed'; 98 | if (e.code == 'weak-password') { 99 | errorMessage = 'The password provided is too weak.'; 100 | } else if (e.code == 'email-already-in-use') { 101 | errorMessage = 'The account already exists for that email.'; 102 | } else if (e.code == 'user-not-found') { 103 | errorMessage = 'No user found for that email.'; 104 | } else if (e.code == 'wrong-password') { 105 | errorMessage = 'Wrong password provided for that user.'; 106 | } 107 | _showErrorDialog(errorMessage); 108 | } on HttpException catch (error) { 109 | var errorMessage = 'Authentication failed'; 110 | if (error.toString().contains('EMAIL_EXISTS')) { 111 | errorMessage = 'This email address is already in use.'; 112 | } else if (error.toString().contains('INVALID_EMAIL')) { 113 | errorMessage = 'This is not a valid email address'; 114 | } else if (error.toString().contains('WEAK_PASSWORD')) { 115 | errorMessage = 'This password is too weak.'; 116 | } else if (error.toString().contains('EMAIL_NOT_FOUND')) { 117 | errorMessage = 'Could not find a user with that email.'; 118 | } else if (error.toString().contains('INVALID_PASSWORD')) { 119 | errorMessage = 'Invalid password.'; 120 | } else if (error.toString().contains('User does not exist')) { 121 | errorMessage = 'User does not exist.'; 122 | } 123 | _showErrorDialog(errorMessage); 124 | } catch (error) { 125 | const errorMessage = 126 | 'Could not authenticate you. Please try again later.'; 127 | _showErrorDialog(errorMessage); 128 | print(error); 129 | } 130 | if (mounted) 131 | setState(() { 132 | _isLoading = false; 133 | }); 134 | } 135 | 136 | void _switchAuthMode() { 137 | if (_authMode == AuthMode.Login) { 138 | setState(() { 139 | _authMode = AuthMode.Signup; 140 | }); 141 | _controller.forward(); 142 | } else { 143 | setState(() { 144 | _authMode = AuthMode.Login; 145 | }); 146 | _controller.reverse(); 147 | } 148 | _formKey.currentState!.reset(); 149 | _passwordController.clear(); 150 | } 151 | 152 | @override 153 | void initState() { 154 | super.initState(); 155 | _controller = AnimationController( 156 | vsync: this, 157 | duration: Duration( 158 | milliseconds: 300, 159 | ), 160 | ); 161 | _slideAnimation = Tween( 162 | begin: Offset(-1.5, 0), 163 | end: Offset(0, 0), 164 | ).animate( 165 | CurvedAnimation( 166 | parent: _controller, 167 | curve: Curves.decelerate, 168 | ), 169 | ); 170 | _opacityAnimation = Tween(begin: 0.0, end: 1.0).animate( 171 | CurvedAnimation( 172 | parent: _controller, 173 | curve: Curves.slowMiddle, 174 | ), 175 | ); 176 | } 177 | 178 | @override 179 | void dispose() { 180 | _passwordController.dispose(); 181 | _focusNode.dispose(); 182 | _controller.dispose(); 183 | super.dispose(); 184 | } 185 | 186 | @override 187 | Widget build(BuildContext context) { 188 | return Scaffold( 189 | resizeToAvoidBottomInset: true, 190 | body: SingleChildScrollView( 191 | keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag, 192 | child: Column( 193 | crossAxisAlignment: CrossAxisAlignment.center, 194 | mainAxisAlignment: MainAxisAlignment.start, 195 | children: [ 196 | SizedBox(height: 70), 197 | SplashedFlex(), 198 | SizedBox(height: 40), 199 | SideTabbedTitle( 200 | _authMode == AuthMode.Login ? 'Login' : 'Sign Up', 201 | ), 202 | AnimatedContainer( 203 | duration: Duration(milliseconds: 200), 204 | curve: Curves.easeIn, 205 | width: double.infinity, 206 | padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 40), 207 | child: Form( 208 | key: _formKey, 209 | child: Column( 210 | crossAxisAlignment: CrossAxisAlignment.center, 211 | mainAxisAlignment: MainAxisAlignment.start, 212 | mainAxisSize: MainAxisSize.min, 213 | children: [ 214 | if (_authMode == AuthMode.Signup) 215 | AnimatedContainer( 216 | duration: Duration(milliseconds: 300), 217 | curve: Curves.easeIn, 218 | child: FadeTransition( 219 | opacity: _opacityAnimation, 220 | child: SlideTransition( 221 | position: _slideAnimation, 222 | child: CustomTextField( 223 | label: 'Name', 224 | textInputAction: TextInputAction.next, 225 | validator: (value) { 226 | if (value!.isEmpty) { 227 | return 'Field is empty'; 228 | } 229 | }, 230 | onSaved: (value) { 231 | _authData['name'] = value!; 232 | }, 233 | ), 234 | ), 235 | ), 236 | ), 237 | if (_authMode == AuthMode.Signup) 238 | AnimatedContainer( 239 | duration: Duration(milliseconds: 500), 240 | constraints: BoxConstraints( 241 | minHeight: _authMode == AuthMode.Signup ? 24 : 0, 242 | maxHeight: _authMode == AuthMode.Signup ? 24 : 0, 243 | ), 244 | curve: Curves.easeIn, 245 | ), 246 | CustomTextField( 247 | label: 'E-mail', 248 | textInputAction: TextInputAction.next, 249 | keyboardType: TextInputType.emailAddress, 250 | validator: (value) { 251 | if (value!.isEmpty) { 252 | return 'Field is empty'; 253 | } 254 | if (!value.contains('@')) { 255 | return 'Invalid email address'; 256 | } 257 | }, 258 | onSaved: (value) { 259 | _authData['email'] = value!; 260 | }, 261 | ), 262 | SizedBox(height: 24), 263 | if (_authMode == AuthMode.Signup) 264 | AnimatedContainer( 265 | duration: Duration(milliseconds: 300), 266 | curve: Curves.easeIn, 267 | child: FadeTransition( 268 | opacity: _opacityAnimation, 269 | child: SlideTransition( 270 | position: _slideAnimation, 271 | child: CustomTextField( 272 | label: 'Mobile', 273 | textInputAction: TextInputAction.next, 274 | keyboardType: TextInputType.phone, 275 | validator: (value) { 276 | if (value!.isEmpty) { 277 | return 'Field is empty'; 278 | } 279 | if (value.length < 9) { 280 | return 'Invalid mobile number'; 281 | } 282 | }, 283 | onSaved: (value) { 284 | _authData['mobile'] = value!; 285 | }, 286 | ), 287 | ), 288 | ), 289 | ), 290 | if (_authMode == AuthMode.Signup) 291 | AnimatedContainer( 292 | duration: Duration(milliseconds: 500), 293 | constraints: BoxConstraints( 294 | minHeight: _authMode == AuthMode.Signup ? 24 : 0, 295 | maxHeight: _authMode == AuthMode.Signup ? 24 : 0, 296 | ), 297 | curve: Curves.easeIn, 298 | ), 299 | CustomTextField( 300 | label: 'Password', 301 | obscure: _obscure, 302 | textInputAction: _authMode == AuthMode.Signup 303 | ? TextInputAction.next 304 | : TextInputAction.done, 305 | controller: _passwordController, 306 | validator: (value) { 307 | if (value!.isEmpty) { 308 | return 'Field is empty'; 309 | } 310 | if (value.length < 5) { 311 | return 'Password is too short'; 312 | } 313 | }, 314 | onSaved: (value) { 315 | _authData['password'] = value!; 316 | }, 317 | onFieldSubmitted: _authMode == AuthMode.Signup 318 | ? (_) { 319 | FocusScope.of(context).requestFocus(_focusNode); 320 | } 321 | : null, 322 | suffixIcon: IconButton( 323 | onPressed: () { 324 | setState(() { 325 | _obscure = !_obscure; 326 | }); 327 | }, 328 | icon: Icon( 329 | _obscure ? Icons.visibility : Icons.visibility_off, 330 | ), 331 | color: Color(0xffB8AAA3), 332 | ), 333 | ), 334 | if (_authMode == AuthMode.Signup) 335 | AnimatedContainer( 336 | duration: Duration(milliseconds: 500), 337 | constraints: BoxConstraints( 338 | minHeight: _authMode == AuthMode.Signup ? 24 : 0, 339 | maxHeight: _authMode == AuthMode.Signup ? 24 : 0, 340 | ), 341 | curve: Curves.easeIn, 342 | ), 343 | if (_authMode == AuthMode.Signup) 344 | AnimatedContainer( 345 | duration: Duration(milliseconds: 300), 346 | curve: Curves.easeIn, 347 | child: FadeTransition( 348 | opacity: _opacityAnimation, 349 | child: SlideTransition( 350 | position: _slideAnimation, 351 | child: CustomTextField( 352 | label: 'Confirm Password', 353 | obscure: true, 354 | textInputAction: TextInputAction.done, 355 | focusNode: _focusNode, 356 | validator: (value) { 357 | if (value != _passwordController.text) { 358 | return 'Passwords do not match'; 359 | } 360 | if (value!.isEmpty) { 361 | return 'Field is empty'; 362 | } 363 | }, 364 | ), 365 | ), 366 | ), 367 | ), 368 | SizedBox(height: 36), 369 | if (_isLoading) 370 | CircularProgressIndicator() 371 | else 372 | CustomButton( 373 | label: _authMode == AuthMode.Login 374 | ? 'Enter' 375 | : 'Create Account', 376 | onTap: _submit, 377 | ), 378 | SizedBox(height: 16), 379 | TapToActionText( 380 | label: 381 | '${_authMode == AuthMode.Login ? 'Don\'t' : 'Already'} have an account? ', 382 | tapLabel: 383 | _authMode == AuthMode.Login ? 'Sign up' : 'Login', 384 | onTap: _switchAuthMode, 385 | ), 386 | if (_authMode == AuthMode.Signup) 387 | AnimatedContainer( 388 | duration: Duration(milliseconds: 500), 389 | constraints: BoxConstraints( 390 | minHeight: _authMode == AuthMode.Signup ? 70 : 0, 391 | maxHeight: _authMode == AuthMode.Signup ? 70 : 0, 392 | ), 393 | curve: Curves.easeIn, 394 | ), 395 | ], 396 | ), 397 | ), 398 | ), 399 | ], 400 | ), 401 | ), 402 | ); 403 | } 404 | } 405 | --------------------------------------------------------------------------------