├── .gitignore ├── .metadata ├── LICENSE ├── README.md ├── android ├── .gitignore ├── app │ ├── build.gradle │ ├── google-services.json │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ └── res │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets └── images │ ├── default.png │ ├── default.svg │ ├── defaultDark.png │ └── defaultold.png ├── ios ├── .gitignore ├── Flutter │ ├── .last_build_id │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ ├── Generated 2.xcconfig │ ├── Release.xcconfig │ └── flutter_export_environment 2.sh ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── 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-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── GoogleService-Info.plist │ ├── Info.plist │ ├── Runner-Bridging-Header.h │ └── Runner.entitlements ├── lib ├── constants │ ├── app_routes.dart │ ├── app_themes.dart │ ├── constants.dart │ └── globals.dart ├── controllers │ ├── auth_controller.dart │ ├── controllers.dart │ ├── language_controller.dart │ └── theme_controller.dart ├── helpers │ ├── gravatar.dart │ ├── helpers.dart │ ├── localization.g.dart │ ├── update_localizations.dart │ └── validator.dart ├── main.dart ├── models │ ├── menu_option_model.dart │ ├── models.dart │ └── user_model.dart └── ui │ ├── auth │ ├── auth.dart │ ├── reset_password_ui.dart │ ├── sign_in_ui.dart │ ├── sign_up_ui.dart │ └── update_profile_ui.dart │ ├── components │ ├── avatar.dart │ ├── components.dart │ ├── dropdown_picker.dart │ ├── dropdown_picker_with_icon.dart │ ├── form_input_field.dart │ ├── form_input_field_with_icon.dart │ ├── form_vertical_spacing.dart │ ├── label_button.dart │ ├── loading.dart │ ├── logo_graphic_header.dart │ ├── primary_button.dart │ └── segmented_selector.dart │ ├── home_ui.dart │ ├── settings_ui.dart │ ├── splash_ui.dart │ └── ui.dart ├── pubspec.lock ├── pubspec.yaml └── web ├── favicon.png ├── icons ├── Icon-192.png └── Icon-512.png ├── index.html └── manifest.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | /build/ 32 | 33 | # Web related 34 | lib/generated_plugin_registrant.dart 35 | 36 | # Symbolication related 37 | app.*.symbols 38 | 39 | # Obfuscation related 40 | app.*.map.json 41 | 42 | # Exceptions to above rules. 43 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 44 | 45 | # added by delay 46 | /ios/Flutter/ 47 | /ios/build/ 48 | -------------------------------------------------------------------------------- /.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: be3a4b37b3e9ab4e80d45b59bed53708b96d211f 8 | channel: dev 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jeff McMorris 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## GetX Flutter Firebase Auth Example 2 | 3 | ![](https://cdn-images-1.medium.com/max/4776/1*OKSIgkZpss30GYT9TwQcJg.png) 4 | 5 | UPDATE: Version 2.0.0 Changed to new language options and added null safety. 6 | 7 | GetX is a relatively new package for Flutter that provides the missing link in making Flutter development simpler. I recently converted a [firebase auth project](https://medium.com/@jeffmcmorris/flutter-firebase-auth-starter-project-b0f91a6503b7) I had created which used provider for state management. Switching to GetX simplified many of the pain points I have had with Flutter development. I no longer was having to pass context into functions. I could also better separate my logic from the UI. GetX also greatly simplifies routing, displaying snackbars and dialogs. 8 | 9 | There are some really good projects and tutorials that I made use of in making [this project](https://github.com/delay/flutter_starter). See these links for more help with GetX. [GetX Documentation](https://github.com/jonataslaw/getx), [Todo GetX example](https://medium.com/@loicgeek/flutter-how-to-create-a-todo-app-using-firebase-firestore-and-firebase-authentication-with-getx-89bdaacc6de6), [Amateur Coder GetX Videos](https://www.youtube.com/watch?v=CNpXbeI_slw) and [Loading Overlay](https://medium.com/@fayaz07/dont-kill-app-s-ui-thread-for-showing-loading-indicators-809e5a992230). 10 | 11 | So why would you want to use GetX? Let me give you an example of how it simplified my own code. When I used Provider there was a lot more boilerplate code. It felt much more esoteric to use. GetX made my code easier to understand. Here is my code for main.dart original vs. GetX. 12 | 13 | ![](https://cdn-images-1.medium.com/max/3932/1*Sg7dajwS-q-I_G4KLDx_ow.png) 14 | 15 | Once you understand the GetX way it becomes much easier to organize your code and separate your concerns between your UI and functions. For instance when calling a function in the UI I was having to handle the results from the function and display snackbars with the results. With GetX I was able to move the snackbars into the actual function which makes more sense since I no longer have to send success and fail messages in and out of functions. Below I show a button which signs in the user with the old way vs GetX way. I prefer the GetX way even though it isn’t a lot shorter, but I like having my logic all separated from the UI. 16 | 17 | ![](https://cdn-images-1.medium.com/max/3580/1*YWsqOuTY1xvqkVvGrt2BLQ.png) 18 | 19 | GetX also has a storage package called [get_storage](https://github.com/jonataslaw/get_storage). It can take the place of shared preferences. It simplifies the way to store data on the device. Here is another example of before and after code. 20 | 21 | ![](https://cdn-images-1.medium.com/max/2600/1*kyYboVrB1BYcMkeHsSNeSw.png) 22 | 23 | There are many other features of GetX to make things simpler, such as working with themes, getting various device settings, etc. This package simplifies a lot of the problems developers face daily when building an app. 24 | 25 | When building an auth project there are a lot of the features you need for a production flutter project. I wanted light and dark mode theming but also the ability to detect and switch themes automatically. I needed the ability to switch between languages easily and automatically detect the user’s language. I wanted a simple way to handle translating from english (the only language I know unfortunately). This is accomplished by running a commandline app to generate the GetX Localization class which pulls from a [google sheet](https://docs.google.com/spreadsheets/d/1oS7iJ6ocrZBA53SxRfKF0CG9HAaXeKtzvsTBhgG4Zzk/edit#gid=0) and easily translate into other languages. Also I needed a way to [do simple user roles](https://medium.com/firebase-developers/patterns-for-security-with-firebase-group-based-permissions-for-cloud-firestore-72859cdec8f6) and it needed to be secure. I see a lot of auth packages including roles in the user’s collection in firestore which is usually editable by that same user. This would make it trivial for the user to assign himself admin privileges. I also wanted to show how to put some basic rules in firestore to secure it. Finally I wanted to have a way the user could alter their profile and change their email or password. 26 | 27 | To handle the language translation you need to create a translation for your app in google sheets. Then open /helpers/update_localizations.dart and replace the docID and sheetId with your own documentId and sheetId. After doing that your will need to drop to the command line and go into the helpers directory. Then type: ```dart update_localizations.dart```. This will create or overwrite the localization.g.dart file with your custom translation. There should not be a need to edit this file directly. Every time you make changes to your translation you will need to re-run this generator. 28 | 29 | ![](https://cdn-images-1.medium.com/max/2000/0*9-A7El_nRDBz-ecK) 30 | 31 | You can copy my [sheet](https://docs.google.com/spreadsheets/d/1oS7iJ6ocrZBA53SxRfKF0CG9HAaXeKtzvsTBhgG4Zzk/edit#gid=0) as a starting point for your own app. The cool thing about using a google sheet is you can have google translate a field with a simple google formula: =GOOGLETRANSLATE(B4,en,fr) This says translate the phrase in field B4 from english to french. 32 | 33 | To handle user roles I created a separate admin collection and added a document with the same document id as the uid of my user. The reason to do this is to make it secure as explained in this [medium article](https://medium.com/firebase-developers/patterns-for-security-with-firebase-group-based-permissions-for-cloud-firestore-72859cdec8f6). I went with the second method explained in that article. If we had just put the roles as a field in the users collection any user could have upgraded themselves to an admin user. So by moving the admin role to a separate collection we can create some rules in firestore that allow the user to update fields about themselves without giving them access to change the role they were assigned. You can also generate other roles for the user by simply adding additional collections for the other roles.. 34 | 35 | The rules I have setup in firestore for this project are fairly simple. Here are the rules I have created. 36 | 37 | ![](https://cdn-images-1.medium.com/max/2000/0*_lmwiYDofWZd0Kn0) 38 | 39 | The first rule matches any user in the admin collection and allows you to read that document only. No one is allowed to write to this collection. I manually add my admin users through the firebase console. The second rule allows the user to read and write only if the user matches the currently logged in user. So a user can only change information about themselves. Here is how my collections are setup in firestore. 40 | 41 | ![](https://cdn-images-1.medium.com/max/2060/0*uFxZGvnPvviMebQ5) 42 | 43 | Finally I wanted to explain a little bit about my ui. I try to control as much as possible with the theme. You can change a lot about the look with the user interface by changing the theme. I am still learning about what all can be changed with just the theme. I also break out small ui components into a separate components folder. Then I make a custom widget instead of using the standard widget directly. This allows me to make changes in one spot if I decide to make ui changes to say a form field in my form_input_field.dart instead of changing a bunch of TextFormField widgets spread through a dozen files. 44 | 45 | ## Overview of project 46 | 47 | **main.dart** — contains info for maintaining the state of the app for the theme, language and user. It initializes language and theme settings. Sets up routing. 48 | 49 | 50 | 51 | ## /constants/ 52 | 53 | **app_themes.dart** — contains info related to our light and dark themes. 54 | 55 | **globals.dart** — contains some global app settings 56 | 57 | **app_routes.dart** — contains the app routes. 58 | 59 | ## /controllers/ 60 | 61 | **auth_controller.dart** — our user and authentication functions for creating, logging in and out our user and saving our user data. 62 | 63 | **language_controller.dart** — saves and loads our selected language. 64 | 65 | **theme_controller.dart** — saves and loads our selected theme. 66 | 67 | ## /helpers/ 68 | 69 | **validator.dart** — contains some validation functions for our form fields. 70 | 71 | **update_localizations.dart** — command line dart app that generates the localization.g.dart file. 72 | 73 | **localizations.g.dart** — this file is generated from our google sheet (do not manually edit this file). 74 | 75 | ## /models/ 76 | 77 | **user_model.dart** — contains the model for our user saved in firestore. 78 | 79 | **menu_option_model.dart** — contains our model for our language options and theme options in settings. 80 | 81 | ## /ui/ 82 | 83 | **home_ui.dart** — contains the ui for the home which shows info about the user. 84 | 85 | **settings_ui.dart** — contains the settings screen for setting the theme and language and some user settings. 86 | 87 | **splash_ui.dart** — contains the initial loading screen, currently just a circular progress indicator. 88 | 89 | ## /ui/auth/ 90 | 91 | **reset_password_ui.dart** — sends a password reset email to the user. 92 | 93 | **sign_in_ui.dart** — allows user to login with email and password. 94 | 95 | **sign_up_ui.dart** — allows user to create a new account. 96 | 97 | **update_profile_ui.dart** — allows user to change his email or name. 98 | 99 | ## /ui/components/ 100 | 101 | **avatar.dart** — displays a user avatar on the home_ui. 102 | 103 | **dropdown_picker.dart** — shows a dropdown list. 104 | 105 | **dropdown_picker_with_icon.dart** — shows a dropdown list with icons. 106 | 107 | **form_input_field.dart** — handles our form field elements. 108 | 109 | **form_input_field_with_icon.dart** — handles our form field elements but has an icon too. 110 | 111 | **form_vertical_spacing.dart** — just a space in the ui. 112 | 113 | **label_button.dart** — one type of button in ui. 114 | 115 | **loading.dart** — circular loading indicator overlay. 116 | 117 | **logo_graphic_header.dart** — a graphic displayed in our ui. 118 | 119 | **primary_button.dart** — another button in the ui. 120 | 121 | **segmented_selector.dart** — a control used to select the theme. 122 | 123 | Provider is also a great package and what I was using for Flutter development until I found GetX. Flutter is still new and evolving fast. It is fun to watch it progress so rapidly with the help of the Flutter community! 124 | 125 | Anyway hopefully this project will help someone. Feel free to use any of it, I didn’t create all of this code as parts of it came from watching the tutorials and reviewing the projects mentioned above. Make sure you [setup firebase](https://firebase.google.com/docs/flutter/setup?platform=android) with your project. 126 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'com.google.gms.google-services' 26 | apply plugin: 'kotlin-android' 27 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 28 | 29 | 30 | android { 31 | compileSdkVersion 28 32 | 33 | sourceSets { 34 | main.java.srcDirs += 'src/main/kotlin' 35 | } 36 | 37 | lintOptions { 38 | disable 'InvalidPackage' 39 | } 40 | 41 | defaultConfig { 42 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 43 | applicationId "com.tapmode.flutterstarter" 44 | minSdkVersion 16 45 | targetSdkVersion 28 46 | versionCode flutterVersionCode.toInteger() 47 | versionName flutterVersionName 48 | multiDexEnabled true 49 | } 50 | 51 | buildTypes { 52 | release { 53 | // TODO: Add your own signing config for the release build. 54 | // Signing with the debug keys for now, so `flutter run --release` works. 55 | signingConfig signingConfigs.debug 56 | } 57 | } 58 | } 59 | 60 | flutter { 61 | source '../..' 62 | } 63 | 64 | dependencies { 65 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 66 | implementation 'com.google.firebase:firebase-analytics:17.2.2' 67 | implementation 'com.android.support:multidex:1.0.3' 68 | } 69 | -------------------------------------------------------------------------------- /android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "524197078337", 4 | "firebase_url": "https://flutter-starter-delay.firebaseio.com", 5 | "project_id": "flutter-starter-delay", 6 | "storage_bucket": "flutter-starter-delay.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:524197078337:android:dee32de1a467249cfa2dc0", 12 | "android_client_info": { 13 | "package_name": "com.tapmode.flutterstarter" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "524197078337-5gq7dldnk4tt8q4cvtenbn08q5fn4vkj.apps.googleusercontent.com", 19 | "client_type": 3 20 | } 21 | ], 22 | "api_key": [ 23 | { 24 | "current_key": "AIzaSyCHe-BxbY8-_CWhZZRC5jqVY4c0ydUGw08" 25 | } 26 | ], 27 | "services": { 28 | "appinvite_service": { 29 | "other_platform_oauth_client": [ 30 | { 31 | "client_id": "524197078337-5gq7dldnk4tt8q4cvtenbn08q5fn4vkj.apps.googleusercontent.com", 32 | "client_type": 3 33 | }, 34 | { 35 | "client_id": "524197078337-96irqf3smo96s6f48mc1neh2vh7jber5.apps.googleusercontent.com", 36 | "client_type": 2, 37 | "ios_info": { 38 | "bundle_id": "com.tapmode.flutter-starter" 39 | } 40 | } 41 | ] 42 | } 43 | } 44 | } 45 | ], 46 | "configuration_version": "1" 47 | } -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 12 | 19 | 23 | 27 | 32 | 36 | 37 | 38 | 39 | 40 | 41 | 43 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /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:3.5.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | classpath 'com.google.gms:google-services:4.3.3' 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 | } 26 | subprojects { 27 | project.evaluationDependsOn(':app') 28 | } 29 | 30 | task clean(type: Delete) { 31 | delete rootProject.buildDir 32 | } 33 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /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-5.6.2-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /assets/images/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/assets/images/default.png -------------------------------------------------------------------------------- /assets/images/default.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Combined Shape 5 | Created with Sketch. 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /assets/images/defaultDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/assets/images/defaultDark.png -------------------------------------------------------------------------------- /assets/images/defaultold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/assets/images/defaultold.png -------------------------------------------------------------------------------- /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/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /ios/Flutter/.last_build_id: -------------------------------------------------------------------------------- 1 | c3df2818212c7215afb888c221a81ee3 -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 3 | #include "Generated.xcconfig" 4 | -------------------------------------------------------------------------------- /ios/Flutter/Generated 2.xcconfig: -------------------------------------------------------------------------------- 1 | // This is a generated file; do not edit or check into version control. 2 | FLUTTER_ROOT=/Users/delay/development/flutter 3 | FLUTTER_APPLICATION_PATH=/Users/delay/Desktop/Programming/flutter_starter 4 | FLUTTER_TARGET=/Users/delay/Desktop/Programming/flutter_starter/lib/main.dart 5 | FLUTTER_BUILD_DIR=build 6 | SYMROOT=${SOURCE_ROOT}/../build/ios 7 | OTHER_LDFLAGS=$(inherited) -framework Flutter 8 | FLUTTER_FRAMEWORK_DIR=/Users/delay/development/flutter/bin/cache/artifacts/engine/ios 9 | FLUTTER_BUILD_NAME=1.0.0 10 | FLUTTER_BUILD_NUMBER=1 11 | DART_OBFUSCATION=false 12 | TRACK_WIDGET_CREATION=true 13 | TREE_SHAKE_ICONS=false 14 | PACKAGE_CONFIG=.packages 15 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 3 | #include "Generated.xcconfig" 4 | -------------------------------------------------------------------------------- /ios/Flutter/flutter_export_environment 2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is a generated file; do not edit or check into version control. 3 | export "FLUTTER_ROOT=/Users/delay/development/flutter" 4 | export "FLUTTER_APPLICATION_PATH=/Users/delay/Desktop/Programming/flutter_starter" 5 | export "FLUTTER_TARGET=/Users/delay/Desktop/Programming/flutter_starter/lib/main.dart" 6 | export "FLUTTER_BUILD_DIR=build" 7 | export "SYMROOT=${SOURCE_ROOT}/../build/ios" 8 | export "OTHER_LDFLAGS=$(inherited) -framework Flutter" 9 | export "FLUTTER_FRAMEWORK_DIR=/Users/delay/development/flutter/bin/cache/artifacts/engine/ios" 10 | export "FLUTTER_BUILD_NAME=1.0.0" 11 | export "FLUTTER_BUILD_NUMBER=1" 12 | export "DART_OBFUSCATION=false" 13 | export "TRACK_WIDGET_CREATION=true" 14 | export "TREE_SHAKE_ICONS=false" 15 | export "PACKAGE_CONFIG=.packages" 16 | -------------------------------------------------------------------------------- /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 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - abseil/algorithm (0.20200225.0): 3 | - abseil/algorithm/algorithm (= 0.20200225.0) 4 | - abseil/algorithm/container (= 0.20200225.0) 5 | - abseil/algorithm/algorithm (0.20200225.0): 6 | - abseil/base/config 7 | - abseil/algorithm/container (0.20200225.0): 8 | - abseil/algorithm/algorithm 9 | - abseil/base/core_headers 10 | - abseil/meta/type_traits 11 | - abseil/base (0.20200225.0): 12 | - abseil/base/atomic_hook (= 0.20200225.0) 13 | - abseil/base/base (= 0.20200225.0) 14 | - abseil/base/base_internal (= 0.20200225.0) 15 | - abseil/base/bits (= 0.20200225.0) 16 | - abseil/base/config (= 0.20200225.0) 17 | - abseil/base/core_headers (= 0.20200225.0) 18 | - abseil/base/dynamic_annotations (= 0.20200225.0) 19 | - abseil/base/endian (= 0.20200225.0) 20 | - abseil/base/errno_saver (= 0.20200225.0) 21 | - abseil/base/exponential_biased (= 0.20200225.0) 22 | - abseil/base/log_severity (= 0.20200225.0) 23 | - abseil/base/malloc_internal (= 0.20200225.0) 24 | - abseil/base/periodic_sampler (= 0.20200225.0) 25 | - abseil/base/pretty_function (= 0.20200225.0) 26 | - abseil/base/raw_logging_internal (= 0.20200225.0) 27 | - abseil/base/spinlock_wait (= 0.20200225.0) 28 | - abseil/base/throw_delegate (= 0.20200225.0) 29 | - abseil/base/atomic_hook (0.20200225.0): 30 | - abseil/base/config 31 | - abseil/base/core_headers 32 | - abseil/base/base (0.20200225.0): 33 | - abseil/base/atomic_hook 34 | - abseil/base/base_internal 35 | - abseil/base/config 36 | - abseil/base/core_headers 37 | - abseil/base/dynamic_annotations 38 | - abseil/base/log_severity 39 | - abseil/base/raw_logging_internal 40 | - abseil/base/spinlock_wait 41 | - abseil/meta/type_traits 42 | - abseil/base/base_internal (0.20200225.0): 43 | - abseil/base/config 44 | - abseil/meta/type_traits 45 | - abseil/base/bits (0.20200225.0): 46 | - abseil/base/config 47 | - abseil/base/core_headers 48 | - abseil/base/config (0.20200225.0) 49 | - abseil/base/core_headers (0.20200225.0): 50 | - abseil/base/config 51 | - abseil/base/dynamic_annotations (0.20200225.0) 52 | - abseil/base/endian (0.20200225.0): 53 | - abseil/base/config 54 | - abseil/base/core_headers 55 | - abseil/base/errno_saver (0.20200225.0): 56 | - abseil/base/config 57 | - abseil/base/exponential_biased (0.20200225.0): 58 | - abseil/base/config 59 | - abseil/base/core_headers 60 | - abseil/base/log_severity (0.20200225.0): 61 | - abseil/base/config 62 | - abseil/base/core_headers 63 | - abseil/base/malloc_internal (0.20200225.0): 64 | - abseil/base/base 65 | - abseil/base/base_internal 66 | - abseil/base/config 67 | - abseil/base/core_headers 68 | - abseil/base/dynamic_annotations 69 | - abseil/base/raw_logging_internal 70 | - abseil/base/periodic_sampler (0.20200225.0): 71 | - abseil/base/core_headers 72 | - abseil/base/exponential_biased 73 | - abseil/base/pretty_function (0.20200225.0) 74 | - abseil/base/raw_logging_internal (0.20200225.0): 75 | - abseil/base/atomic_hook 76 | - abseil/base/config 77 | - abseil/base/core_headers 78 | - abseil/base/log_severity 79 | - abseil/base/spinlock_wait (0.20200225.0): 80 | - abseil/base/base_internal 81 | - abseil/base/core_headers 82 | - abseil/base/errno_saver 83 | - abseil/base/throw_delegate (0.20200225.0): 84 | - abseil/base/config 85 | - abseil/base/raw_logging_internal 86 | - abseil/container/compressed_tuple (0.20200225.0): 87 | - abseil/utility/utility 88 | - abseil/container/inlined_vector (0.20200225.0): 89 | - abseil/algorithm/algorithm 90 | - abseil/base/core_headers 91 | - abseil/base/throw_delegate 92 | - abseil/container/inlined_vector_internal 93 | - abseil/memory/memory 94 | - abseil/container/inlined_vector_internal (0.20200225.0): 95 | - abseil/base/core_headers 96 | - abseil/container/compressed_tuple 97 | - abseil/memory/memory 98 | - abseil/meta/type_traits 99 | - abseil/types/span 100 | - abseil/memory (0.20200225.0): 101 | - abseil/memory/memory (= 0.20200225.0) 102 | - abseil/memory/memory (0.20200225.0): 103 | - abseil/base/core_headers 104 | - abseil/meta/type_traits 105 | - abseil/meta (0.20200225.0): 106 | - abseil/meta/type_traits (= 0.20200225.0) 107 | - abseil/meta/type_traits (0.20200225.0): 108 | - abseil/base/config 109 | - abseil/numeric/int128 (0.20200225.0): 110 | - abseil/base/config 111 | - abseil/base/core_headers 112 | - abseil/strings/internal (0.20200225.0): 113 | - abseil/base/config 114 | - abseil/base/core_headers 115 | - abseil/base/endian 116 | - abseil/base/raw_logging_internal 117 | - abseil/meta/type_traits 118 | - abseil/strings/str_format (0.20200225.0): 119 | - abseil/strings/str_format_internal 120 | - abseil/strings/str_format_internal (0.20200225.0): 121 | - abseil/base/config 122 | - abseil/base/core_headers 123 | - abseil/meta/type_traits 124 | - abseil/numeric/int128 125 | - abseil/strings/strings 126 | - abseil/types/span 127 | - abseil/strings/strings (0.20200225.0): 128 | - abseil/base/base 129 | - abseil/base/bits 130 | - abseil/base/config 131 | - abseil/base/core_headers 132 | - abseil/base/endian 133 | - abseil/base/raw_logging_internal 134 | - abseil/base/throw_delegate 135 | - abseil/memory/memory 136 | - abseil/meta/type_traits 137 | - abseil/numeric/int128 138 | - abseil/strings/internal 139 | - abseil/time (0.20200225.0): 140 | - abseil/time/internal (= 0.20200225.0) 141 | - abseil/time/time (= 0.20200225.0) 142 | - abseil/time/internal (0.20200225.0): 143 | - abseil/time/internal/cctz (= 0.20200225.0) 144 | - abseil/time/internal/cctz (0.20200225.0): 145 | - abseil/time/internal/cctz/civil_time (= 0.20200225.0) 146 | - abseil/time/internal/cctz/time_zone (= 0.20200225.0) 147 | - abseil/time/internal/cctz/civil_time (0.20200225.0): 148 | - abseil/base/config 149 | - abseil/time/internal/cctz/time_zone (0.20200225.0): 150 | - abseil/base/config 151 | - abseil/time/internal/cctz/civil_time 152 | - abseil/time/time (0.20200225.0): 153 | - abseil/base/base 154 | - abseil/base/core_headers 155 | - abseil/base/raw_logging_internal 156 | - abseil/numeric/int128 157 | - abseil/strings/strings 158 | - abseil/time/internal/cctz/civil_time 159 | - abseil/time/internal/cctz/time_zone 160 | - abseil/types (0.20200225.0): 161 | - abseil/types/any (= 0.20200225.0) 162 | - abseil/types/bad_any_cast (= 0.20200225.0) 163 | - abseil/types/bad_any_cast_impl (= 0.20200225.0) 164 | - abseil/types/bad_optional_access (= 0.20200225.0) 165 | - abseil/types/bad_variant_access (= 0.20200225.0) 166 | - abseil/types/compare (= 0.20200225.0) 167 | - abseil/types/optional (= 0.20200225.0) 168 | - abseil/types/span (= 0.20200225.0) 169 | - abseil/types/variant (= 0.20200225.0) 170 | - abseil/types/any (0.20200225.0): 171 | - abseil/base/config 172 | - abseil/base/core_headers 173 | - abseil/meta/type_traits 174 | - abseil/types/bad_any_cast 175 | - abseil/utility/utility 176 | - abseil/types/bad_any_cast (0.20200225.0): 177 | - abseil/base/config 178 | - abseil/types/bad_any_cast_impl 179 | - abseil/types/bad_any_cast_impl (0.20200225.0): 180 | - abseil/base/config 181 | - abseil/base/raw_logging_internal 182 | - abseil/types/bad_optional_access (0.20200225.0): 183 | - abseil/base/config 184 | - abseil/base/raw_logging_internal 185 | - abseil/types/bad_variant_access (0.20200225.0): 186 | - abseil/base/config 187 | - abseil/base/raw_logging_internal 188 | - abseil/types/compare (0.20200225.0): 189 | - abseil/base/core_headers 190 | - abseil/meta/type_traits 191 | - abseil/types/optional (0.20200225.0): 192 | - abseil/base/base_internal 193 | - abseil/base/config 194 | - abseil/base/core_headers 195 | - abseil/memory/memory 196 | - abseil/meta/type_traits 197 | - abseil/types/bad_optional_access 198 | - abseil/utility/utility 199 | - abseil/types/span (0.20200225.0): 200 | - abseil/algorithm/algorithm 201 | - abseil/base/core_headers 202 | - abseil/base/throw_delegate 203 | - abseil/meta/type_traits 204 | - abseil/types/variant (0.20200225.0): 205 | - abseil/base/base_internal 206 | - abseil/base/config 207 | - abseil/base/core_headers 208 | - abseil/meta/type_traits 209 | - abseil/types/bad_variant_access 210 | - abseil/utility/utility 211 | - abseil/utility/utility (0.20200225.0): 212 | - abseil/base/base_internal 213 | - abseil/base/config 214 | - abseil/meta/type_traits 215 | - BoringSSL-GRPC (0.0.7): 216 | - BoringSSL-GRPC/Implementation (= 0.0.7) 217 | - BoringSSL-GRPC/Interface (= 0.0.7) 218 | - BoringSSL-GRPC/Implementation (0.0.7): 219 | - BoringSSL-GRPC/Interface (= 0.0.7) 220 | - BoringSSL-GRPC/Interface (0.0.7) 221 | - cloud_firestore (1.0.4): 222 | - Firebase/Firestore (= 7.3.0) 223 | - firebase_core 224 | - Flutter 225 | - Firebase/Auth (7.3.0): 226 | - Firebase/CoreOnly 227 | - FirebaseAuth (~> 7.3.0) 228 | - Firebase/CoreOnly (7.3.0): 229 | - FirebaseCore (= 7.3.0) 230 | - Firebase/Firestore (7.3.0): 231 | - Firebase/CoreOnly 232 | - FirebaseFirestore (~> 7.3.0) 233 | - firebase_auth (1.0.2): 234 | - Firebase/Auth (= 7.3.0) 235 | - firebase_core 236 | - Flutter 237 | - firebase_core (1.0.3): 238 | - Firebase/CoreOnly (= 7.3.0) 239 | - Flutter 240 | - FirebaseAuth (7.3.0): 241 | - FirebaseCore (~> 7.0) 242 | - GoogleUtilities/AppDelegateSwizzler (~> 7.0) 243 | - GoogleUtilities/Environment (~> 7.0) 244 | - GTMSessionFetcher/Core (~> 1.4) 245 | - FirebaseCore (7.3.0): 246 | - FirebaseCoreDiagnostics (~> 7.0) 247 | - GoogleUtilities/Environment (~> 7.0) 248 | - GoogleUtilities/Logger (~> 7.0) 249 | - FirebaseCoreDiagnostics (7.3.0): 250 | - GoogleDataTransport (~> 8.0) 251 | - GoogleUtilities/Environment (~> 7.0) 252 | - GoogleUtilities/Logger (~> 7.0) 253 | - nanopb (~> 2.30906.0) 254 | - FirebaseFirestore (7.3.0): 255 | - abseil/algorithm (= 0.20200225.0) 256 | - abseil/base (= 0.20200225.0) 257 | - abseil/memory (= 0.20200225.0) 258 | - abseil/meta (= 0.20200225.0) 259 | - abseil/strings/strings (= 0.20200225.0) 260 | - abseil/time (= 0.20200225.0) 261 | - abseil/types (= 0.20200225.0) 262 | - FirebaseCore (~> 7.0) 263 | - "gRPC-C++ (~> 1.28.0)" 264 | - leveldb-library (~> 1.22) 265 | - nanopb (~> 2.30906.0) 266 | - Flutter (1.0.0) 267 | - GoogleDataTransport (8.1.0): 268 | - nanopb (~> 2.30906.0) 269 | - GoogleUtilities/AppDelegateSwizzler (7.2.2): 270 | - GoogleUtilities/Environment 271 | - GoogleUtilities/Logger 272 | - GoogleUtilities/Network 273 | - GoogleUtilities/Environment (7.2.2): 274 | - PromisesObjC (~> 1.2) 275 | - GoogleUtilities/Logger (7.2.2): 276 | - GoogleUtilities/Environment 277 | - GoogleUtilities/Network (7.2.2): 278 | - GoogleUtilities/Logger 279 | - "GoogleUtilities/NSData+zlib" 280 | - GoogleUtilities/Reachability 281 | - "GoogleUtilities/NSData+zlib (7.2.2)" 282 | - GoogleUtilities/Reachability (7.2.2): 283 | - GoogleUtilities/Logger 284 | - "gRPC-C++ (1.28.2)": 285 | - "gRPC-C++/Implementation (= 1.28.2)" 286 | - "gRPC-C++/Interface (= 1.28.2)" 287 | - "gRPC-C++/Implementation (1.28.2)": 288 | - abseil/container/inlined_vector (= 0.20200225.0) 289 | - abseil/memory/memory (= 0.20200225.0) 290 | - abseil/strings/str_format (= 0.20200225.0) 291 | - abseil/strings/strings (= 0.20200225.0) 292 | - abseil/types/optional (= 0.20200225.0) 293 | - "gRPC-C++/Interface (= 1.28.2)" 294 | - gRPC-Core (= 1.28.2) 295 | - "gRPC-C++/Interface (1.28.2)" 296 | - gRPC-Core (1.28.2): 297 | - gRPC-Core/Implementation (= 1.28.2) 298 | - gRPC-Core/Interface (= 1.28.2) 299 | - gRPC-Core/Implementation (1.28.2): 300 | - abseil/container/inlined_vector (= 0.20200225.0) 301 | - abseil/memory/memory (= 0.20200225.0) 302 | - abseil/strings/str_format (= 0.20200225.0) 303 | - abseil/strings/strings (= 0.20200225.0) 304 | - abseil/types/optional (= 0.20200225.0) 305 | - BoringSSL-GRPC (= 0.0.7) 306 | - gRPC-Core/Interface (= 1.28.2) 307 | - gRPC-Core/Interface (1.28.2) 308 | - GTMSessionFetcher/Core (1.5.0) 309 | - leveldb-library (1.22) 310 | - nanopb (2.30906.0): 311 | - nanopb/decode (= 2.30906.0) 312 | - nanopb/encode (= 2.30906.0) 313 | - nanopb/decode (2.30906.0) 314 | - nanopb/encode (2.30906.0) 315 | - path_provider (0.0.1): 316 | - Flutter 317 | - PromisesObjC (1.2.12) 318 | 319 | DEPENDENCIES: 320 | - cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`) 321 | - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) 322 | - firebase_core (from `.symlinks/plugins/firebase_core/ios`) 323 | - Flutter (from `Flutter`) 324 | - path_provider (from `.symlinks/plugins/path_provider/ios`) 325 | 326 | SPEC REPOS: 327 | trunk: 328 | - abseil 329 | - BoringSSL-GRPC 330 | - Firebase 331 | - FirebaseAuth 332 | - FirebaseCore 333 | - FirebaseCoreDiagnostics 334 | - FirebaseFirestore 335 | - GoogleDataTransport 336 | - GoogleUtilities 337 | - "gRPC-C++" 338 | - gRPC-Core 339 | - GTMSessionFetcher 340 | - leveldb-library 341 | - nanopb 342 | - PromisesObjC 343 | 344 | EXTERNAL SOURCES: 345 | cloud_firestore: 346 | :path: ".symlinks/plugins/cloud_firestore/ios" 347 | firebase_auth: 348 | :path: ".symlinks/plugins/firebase_auth/ios" 349 | firebase_core: 350 | :path: ".symlinks/plugins/firebase_core/ios" 351 | Flutter: 352 | :path: Flutter 353 | path_provider: 354 | :path: ".symlinks/plugins/path_provider/ios" 355 | 356 | SPEC CHECKSUMS: 357 | abseil: 6c8eb7892aefa08d929b39f9bb108e5367e3228f 358 | BoringSSL-GRPC: 8edf627ee524575e2f8d19d56f068b448eea3879 359 | cloud_firestore: d5902cdb3c48c2e8c73cd093fc9471ebe7f44a4c 360 | Firebase: 26223c695fe322633274198cb19dca8cb7e54416 361 | firebase_auth: 9e9e667b904ee5701ab50330a5718cce0bf1a469 362 | firebase_core: b5d81dfd4fb2d6f700e67de34d9a633ae325c4e9 363 | FirebaseAuth: c224a0cf1afa0949bd5c7bfcf154b4f5ce8ddef2 364 | FirebaseCore: 4d3c72622ce0e2106aaa07bb4b2935ba2c370972 365 | FirebaseCoreDiagnostics: d50e11039e5984d92c8a512be2395f13df747350 366 | FirebaseFirestore: 1906bf163afdb7c432d2e3b5c40ceb9dd2df5820 367 | Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c 368 | GoogleDataTransport: 116c84c4bdeb76be2a7a46de51244368f9794eab 369 | GoogleUtilities: 31c5b01f978a70c6cff2afc6272b3f1921614b43 370 | "gRPC-C++": 13d8ccef97d5c3c441b7e3c529ef28ebee86fad2 371 | gRPC-Core: 4afa11bfbedf7cdecd04de535a9e046893404ed5 372 | GTMSessionFetcher: b3503b20a988c4e20cc189aa798fd18220133f52 373 | leveldb-library: 55d93ee664b4007aac644a782d11da33fba316f7 374 | nanopb: 1bf24dd71191072e120b83dd02d08f3da0d65e53 375 | path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c 376 | PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97 377 | 378 | PODFILE CHECKSUM: 7368163408c647b7eb699d0d788ba6718e18fb8d 379 | 380 | COCOAPODS: 1.10.1 381 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 51; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 2E5833C324342ED500830150 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 2E5833C224342ED500830150 /* GoogleService-Info.plist */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 14 | 82F798BCCFC6D6226ACA286F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 30051A2D6B1659C900C28076 /* Pods_Runner.framework */; }; 15 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 16 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 17 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXCopyFilesBuildPhase section */ 21 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 22 | isa = PBXCopyFilesBuildPhase; 23 | buildActionMask = 2147483647; 24 | dstPath = ""; 25 | dstSubfolderSpec = 10; 26 | files = ( 27 | ); 28 | name = "Embed Frameworks"; 29 | runOnlyForDeploymentPostprocessing = 0; 30 | }; 31 | /* End PBXCopyFilesBuildPhase section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 35 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 36 | 21771023C31C7AD00E2E5700 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 37 | 2E5833C224342ED500830150 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 38 | 2EE2152924579A5C002431C5 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; 39 | 30051A2D6B1659C900C28076 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 41 | 526314EF7DA2AD6AAC6CFB26 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 42 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 43 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 44 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 45 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 46 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 47 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 49 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 50 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 51 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 52 | FF252FC998AB9F74EB188D26 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 53 | /* End PBXFileReference section */ 54 | 55 | /* Begin PBXFrameworksBuildPhase section */ 56 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 57 | isa = PBXFrameworksBuildPhase; 58 | buildActionMask = 2147483647; 59 | files = ( 60 | 82F798BCCFC6D6226ACA286F /* Pods_Runner.framework in Frameworks */, 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | /* End PBXFrameworksBuildPhase section */ 65 | 66 | /* Begin PBXGroup section */ 67 | 6736A9D1F96982A3A594AB50 /* Frameworks */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | 30051A2D6B1659C900C28076 /* Pods_Runner.framework */, 71 | ); 72 | name = Frameworks; 73 | sourceTree = ""; 74 | }; 75 | 8DD15DB468BEA89EBC7F2F1A /* Pods */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | 21771023C31C7AD00E2E5700 /* Pods-Runner.debug.xcconfig */, 79 | FF252FC998AB9F74EB188D26 /* Pods-Runner.release.xcconfig */, 80 | 526314EF7DA2AD6AAC6CFB26 /* Pods-Runner.profile.xcconfig */, 81 | ); 82 | path = Pods; 83 | sourceTree = ""; 84 | }; 85 | 9740EEB11CF90186004384FC /* Flutter */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 89 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 90 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 91 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 92 | ); 93 | name = Flutter; 94 | sourceTree = ""; 95 | }; 96 | 97C146E51CF9000F007C117D = { 97 | isa = PBXGroup; 98 | children = ( 99 | 9740EEB11CF90186004384FC /* Flutter */, 100 | 97C146F01CF9000F007C117D /* Runner */, 101 | 97C146EF1CF9000F007C117D /* Products */, 102 | 8DD15DB468BEA89EBC7F2F1A /* Pods */, 103 | 6736A9D1F96982A3A594AB50 /* Frameworks */, 104 | ); 105 | sourceTree = ""; 106 | }; 107 | 97C146EF1CF9000F007C117D /* Products */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 97C146EE1CF9000F007C117D /* Runner.app */, 111 | ); 112 | name = Products; 113 | sourceTree = ""; 114 | }; 115 | 97C146F01CF9000F007C117D /* Runner */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 2EE2152924579A5C002431C5 /* Runner.entitlements */, 119 | 2E5833C224342ED500830150 /* GoogleService-Info.plist */, 120 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 121 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 122 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 123 | 97C147021CF9000F007C117D /* Info.plist */, 124 | 97C146F11CF9000F007C117D /* Supporting Files */, 125 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 126 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 127 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 128 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 129 | ); 130 | path = Runner; 131 | sourceTree = ""; 132 | }; 133 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | ); 137 | name = "Supporting Files"; 138 | sourceTree = ""; 139 | }; 140 | /* End PBXGroup section */ 141 | 142 | /* Begin PBXNativeTarget section */ 143 | 97C146ED1CF9000F007C117D /* Runner */ = { 144 | isa = PBXNativeTarget; 145 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 146 | buildPhases = ( 147 | C404887B96C841A9CE6156EF /* [CP] Check Pods Manifest.lock */, 148 | 9740EEB61CF901F6004384FC /* Run Script */, 149 | 97C146EA1CF9000F007C117D /* Sources */, 150 | 97C146EB1CF9000F007C117D /* Frameworks */, 151 | 97C146EC1CF9000F007C117D /* Resources */, 152 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 153 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 154 | 7E15B24DA6CDB511582AA638 /* [CP] Embed Pods Frameworks */, 155 | ); 156 | buildRules = ( 157 | ); 158 | dependencies = ( 159 | ); 160 | name = Runner; 161 | productName = Runner; 162 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 163 | productType = "com.apple.product-type.application"; 164 | }; 165 | /* End PBXNativeTarget section */ 166 | 167 | /* Begin PBXProject section */ 168 | 97C146E61CF9000F007C117D /* Project object */ = { 169 | isa = PBXProject; 170 | attributes = { 171 | LastUpgradeCheck = 1240; 172 | ORGANIZATIONNAME = ""; 173 | TargetAttributes = { 174 | 97C146ED1CF9000F007C117D = { 175 | CreatedOnToolsVersion = 7.3.1; 176 | LastSwiftMigration = 1100; 177 | }; 178 | }; 179 | }; 180 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 181 | compatibilityVersion = "Xcode 9.3"; 182 | developmentRegion = en; 183 | hasScannedForEncodings = 0; 184 | knownRegions = ( 185 | en, 186 | Base, 187 | ); 188 | mainGroup = 97C146E51CF9000F007C117D; 189 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 190 | projectDirPath = ""; 191 | projectRoot = ""; 192 | targets = ( 193 | 97C146ED1CF9000F007C117D /* Runner */, 194 | ); 195 | }; 196 | /* End PBXProject section */ 197 | 198 | /* Begin PBXResourcesBuildPhase section */ 199 | 97C146EC1CF9000F007C117D /* Resources */ = { 200 | isa = PBXResourcesBuildPhase; 201 | buildActionMask = 2147483647; 202 | files = ( 203 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 204 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 205 | 2E5833C324342ED500830150 /* GoogleService-Info.plist in Resources */, 206 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 207 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 208 | ); 209 | runOnlyForDeploymentPostprocessing = 0; 210 | }; 211 | /* End PBXResourcesBuildPhase section */ 212 | 213 | /* Begin PBXShellScriptBuildPhase section */ 214 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 215 | isa = PBXShellScriptBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | ); 219 | inputPaths = ( 220 | ); 221 | name = "Thin Binary"; 222 | outputPaths = ( 223 | ); 224 | runOnlyForDeploymentPostprocessing = 0; 225 | shellPath = /bin/sh; 226 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 227 | }; 228 | 7E15B24DA6CDB511582AA638 /* [CP] Embed Pods Frameworks */ = { 229 | isa = PBXShellScriptBuildPhase; 230 | buildActionMask = 2147483647; 231 | files = ( 232 | ); 233 | inputFileListPaths = ( 234 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", 235 | ); 236 | name = "[CP] Embed Pods Frameworks"; 237 | outputFileListPaths = ( 238 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", 239 | ); 240 | runOnlyForDeploymentPostprocessing = 0; 241 | shellPath = /bin/sh; 242 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 243 | showEnvVarsInLog = 0; 244 | }; 245 | 9740EEB61CF901F6004384FC /* Run Script */ = { 246 | isa = PBXShellScriptBuildPhase; 247 | buildActionMask = 2147483647; 248 | files = ( 249 | ); 250 | inputPaths = ( 251 | ); 252 | name = "Run Script"; 253 | outputPaths = ( 254 | ); 255 | runOnlyForDeploymentPostprocessing = 0; 256 | shellPath = /bin/sh; 257 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 258 | }; 259 | C404887B96C841A9CE6156EF /* [CP] Check Pods Manifest.lock */ = { 260 | isa = PBXShellScriptBuildPhase; 261 | buildActionMask = 2147483647; 262 | files = ( 263 | ); 264 | inputFileListPaths = ( 265 | ); 266 | inputPaths = ( 267 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 268 | "${PODS_ROOT}/Manifest.lock", 269 | ); 270 | name = "[CP] Check Pods Manifest.lock"; 271 | outputFileListPaths = ( 272 | ); 273 | outputPaths = ( 274 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 275 | ); 276 | runOnlyForDeploymentPostprocessing = 0; 277 | shellPath = /bin/sh; 278 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 279 | showEnvVarsInLog = 0; 280 | }; 281 | /* End PBXShellScriptBuildPhase section */ 282 | 283 | /* Begin PBXSourcesBuildPhase section */ 284 | 97C146EA1CF9000F007C117D /* Sources */ = { 285 | isa = PBXSourcesBuildPhase; 286 | buildActionMask = 2147483647; 287 | files = ( 288 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 289 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 290 | ); 291 | runOnlyForDeploymentPostprocessing = 0; 292 | }; 293 | /* End PBXSourcesBuildPhase section */ 294 | 295 | /* Begin PBXVariantGroup section */ 296 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 297 | isa = PBXVariantGroup; 298 | children = ( 299 | 97C146FB1CF9000F007C117D /* Base */, 300 | ); 301 | name = Main.storyboard; 302 | sourceTree = ""; 303 | }; 304 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 305 | isa = PBXVariantGroup; 306 | children = ( 307 | 97C147001CF9000F007C117D /* Base */, 308 | ); 309 | name = LaunchScreen.storyboard; 310 | sourceTree = ""; 311 | }; 312 | /* End PBXVariantGroup section */ 313 | 314 | /* Begin XCBuildConfiguration section */ 315 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 316 | isa = XCBuildConfiguration; 317 | buildSettings = { 318 | ALWAYS_SEARCH_USER_PATHS = NO; 319 | CLANG_ANALYZER_NONNULL = YES; 320 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 321 | CLANG_CXX_LIBRARY = "libc++"; 322 | CLANG_ENABLE_MODULES = YES; 323 | CLANG_ENABLE_OBJC_ARC = YES; 324 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 325 | CLANG_WARN_BOOL_CONVERSION = YES; 326 | CLANG_WARN_COMMA = YES; 327 | CLANG_WARN_CONSTANT_CONVERSION = YES; 328 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 329 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 330 | CLANG_WARN_EMPTY_BODY = YES; 331 | CLANG_WARN_ENUM_CONVERSION = YES; 332 | CLANG_WARN_INFINITE_RECURSION = YES; 333 | CLANG_WARN_INT_CONVERSION = YES; 334 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 335 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 336 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 337 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 338 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 339 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 340 | CLANG_WARN_STRICT_PROTOTYPES = YES; 341 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 342 | CLANG_WARN_UNREACHABLE_CODE = YES; 343 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 344 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 345 | COPY_PHASE_STRIP = NO; 346 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 347 | ENABLE_NS_ASSERTIONS = NO; 348 | ENABLE_STRICT_OBJC_MSGSEND = YES; 349 | GCC_C_LANGUAGE_STANDARD = gnu99; 350 | GCC_NO_COMMON_BLOCKS = YES; 351 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 352 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 353 | GCC_WARN_UNDECLARED_SELECTOR = YES; 354 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 355 | GCC_WARN_UNUSED_FUNCTION = YES; 356 | GCC_WARN_UNUSED_VARIABLE = YES; 357 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 358 | MTL_ENABLE_DEBUG_INFO = NO; 359 | SDKROOT = iphoneos; 360 | SUPPORTED_PLATFORMS = iphoneos; 361 | TARGETED_DEVICE_FAMILY = "1,2"; 362 | VALIDATE_PRODUCT = YES; 363 | }; 364 | name = Profile; 365 | }; 366 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 367 | isa = XCBuildConfiguration; 368 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 369 | buildSettings = { 370 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 371 | CLANG_ENABLE_MODULES = YES; 372 | CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; 373 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 374 | DEVELOPMENT_TEAM = B64VL7ASS3; 375 | ENABLE_BITCODE = NO; 376 | FRAMEWORK_SEARCH_PATHS = ( 377 | "$(inherited)", 378 | "$(PROJECT_DIR)/Flutter", 379 | ); 380 | INFOPLIST_FILE = Runner/Info.plist; 381 | LD_RUNPATH_SEARCH_PATHS = ( 382 | "$(inherited)", 383 | "@executable_path/Frameworks", 384 | ); 385 | LIBRARY_SEARCH_PATHS = ( 386 | "$(inherited)", 387 | "$(PROJECT_DIR)/Flutter", 388 | ); 389 | PRODUCT_BUNDLE_IDENTIFIER = "com.tapmode.flutter-starter"; 390 | PRODUCT_NAME = "$(TARGET_NAME)"; 391 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 392 | SWIFT_VERSION = 5.0; 393 | VERSIONING_SYSTEM = "apple-generic"; 394 | }; 395 | name = Profile; 396 | }; 397 | 97C147031CF9000F007C117D /* Debug */ = { 398 | isa = XCBuildConfiguration; 399 | buildSettings = { 400 | ALWAYS_SEARCH_USER_PATHS = NO; 401 | CLANG_ANALYZER_NONNULL = YES; 402 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 403 | CLANG_CXX_LIBRARY = "libc++"; 404 | CLANG_ENABLE_MODULES = YES; 405 | CLANG_ENABLE_OBJC_ARC = YES; 406 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 407 | CLANG_WARN_BOOL_CONVERSION = YES; 408 | CLANG_WARN_COMMA = YES; 409 | CLANG_WARN_CONSTANT_CONVERSION = YES; 410 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 411 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 412 | CLANG_WARN_EMPTY_BODY = YES; 413 | CLANG_WARN_ENUM_CONVERSION = YES; 414 | CLANG_WARN_INFINITE_RECURSION = YES; 415 | CLANG_WARN_INT_CONVERSION = YES; 416 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 417 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 418 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 419 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 420 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 421 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 422 | CLANG_WARN_STRICT_PROTOTYPES = YES; 423 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 424 | CLANG_WARN_UNREACHABLE_CODE = YES; 425 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 426 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 427 | COPY_PHASE_STRIP = NO; 428 | DEBUG_INFORMATION_FORMAT = dwarf; 429 | ENABLE_STRICT_OBJC_MSGSEND = YES; 430 | ENABLE_TESTABILITY = YES; 431 | GCC_C_LANGUAGE_STANDARD = gnu99; 432 | GCC_DYNAMIC_NO_PIC = NO; 433 | GCC_NO_COMMON_BLOCKS = YES; 434 | GCC_OPTIMIZATION_LEVEL = 0; 435 | GCC_PREPROCESSOR_DEFINITIONS = ( 436 | "DEBUG=1", 437 | "$(inherited)", 438 | ); 439 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 440 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 441 | GCC_WARN_UNDECLARED_SELECTOR = YES; 442 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 443 | GCC_WARN_UNUSED_FUNCTION = YES; 444 | GCC_WARN_UNUSED_VARIABLE = YES; 445 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 446 | MTL_ENABLE_DEBUG_INFO = YES; 447 | ONLY_ACTIVE_ARCH = YES; 448 | SDKROOT = iphoneos; 449 | TARGETED_DEVICE_FAMILY = "1,2"; 450 | }; 451 | name = Debug; 452 | }; 453 | 97C147041CF9000F007C117D /* Release */ = { 454 | isa = XCBuildConfiguration; 455 | buildSettings = { 456 | ALWAYS_SEARCH_USER_PATHS = NO; 457 | CLANG_ANALYZER_NONNULL = YES; 458 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 459 | CLANG_CXX_LIBRARY = "libc++"; 460 | CLANG_ENABLE_MODULES = YES; 461 | CLANG_ENABLE_OBJC_ARC = YES; 462 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 463 | CLANG_WARN_BOOL_CONVERSION = YES; 464 | CLANG_WARN_COMMA = YES; 465 | CLANG_WARN_CONSTANT_CONVERSION = YES; 466 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 467 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 468 | CLANG_WARN_EMPTY_BODY = YES; 469 | CLANG_WARN_ENUM_CONVERSION = YES; 470 | CLANG_WARN_INFINITE_RECURSION = YES; 471 | CLANG_WARN_INT_CONVERSION = YES; 472 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 473 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 474 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 475 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 476 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 477 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 478 | CLANG_WARN_STRICT_PROTOTYPES = YES; 479 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 480 | CLANG_WARN_UNREACHABLE_CODE = YES; 481 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 482 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 483 | COPY_PHASE_STRIP = NO; 484 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 485 | ENABLE_NS_ASSERTIONS = NO; 486 | ENABLE_STRICT_OBJC_MSGSEND = YES; 487 | GCC_C_LANGUAGE_STANDARD = gnu99; 488 | GCC_NO_COMMON_BLOCKS = YES; 489 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 490 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 491 | GCC_WARN_UNDECLARED_SELECTOR = YES; 492 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 493 | GCC_WARN_UNUSED_FUNCTION = YES; 494 | GCC_WARN_UNUSED_VARIABLE = YES; 495 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 496 | MTL_ENABLE_DEBUG_INFO = NO; 497 | SDKROOT = iphoneos; 498 | SUPPORTED_PLATFORMS = iphoneos; 499 | SWIFT_COMPILATION_MODE = wholemodule; 500 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 501 | TARGETED_DEVICE_FAMILY = "1,2"; 502 | VALIDATE_PRODUCT = YES; 503 | }; 504 | name = Release; 505 | }; 506 | 97C147061CF9000F007C117D /* Debug */ = { 507 | isa = XCBuildConfiguration; 508 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 509 | buildSettings = { 510 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 511 | CLANG_ENABLE_MODULES = YES; 512 | CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; 513 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 514 | DEVELOPMENT_TEAM = B64VL7ASS3; 515 | ENABLE_BITCODE = NO; 516 | FRAMEWORK_SEARCH_PATHS = ( 517 | "$(inherited)", 518 | "$(PROJECT_DIR)/Flutter", 519 | ); 520 | INFOPLIST_FILE = Runner/Info.plist; 521 | LD_RUNPATH_SEARCH_PATHS = ( 522 | "$(inherited)", 523 | "@executable_path/Frameworks", 524 | ); 525 | LIBRARY_SEARCH_PATHS = ( 526 | "$(inherited)", 527 | "$(PROJECT_DIR)/Flutter", 528 | ); 529 | PRODUCT_BUNDLE_IDENTIFIER = "com.tapmode.flutter-starter"; 530 | PRODUCT_NAME = "$(TARGET_NAME)"; 531 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 532 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 533 | SWIFT_VERSION = 5.0; 534 | VERSIONING_SYSTEM = "apple-generic"; 535 | }; 536 | name = Debug; 537 | }; 538 | 97C147071CF9000F007C117D /* Release */ = { 539 | isa = XCBuildConfiguration; 540 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 541 | buildSettings = { 542 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 543 | CLANG_ENABLE_MODULES = YES; 544 | CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; 545 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 546 | DEVELOPMENT_TEAM = B64VL7ASS3; 547 | ENABLE_BITCODE = NO; 548 | FRAMEWORK_SEARCH_PATHS = ( 549 | "$(inherited)", 550 | "$(PROJECT_DIR)/Flutter", 551 | ); 552 | INFOPLIST_FILE = Runner/Info.plist; 553 | LD_RUNPATH_SEARCH_PATHS = ( 554 | "$(inherited)", 555 | "@executable_path/Frameworks", 556 | ); 557 | LIBRARY_SEARCH_PATHS = ( 558 | "$(inherited)", 559 | "$(PROJECT_DIR)/Flutter", 560 | ); 561 | PRODUCT_BUNDLE_IDENTIFIER = "com.tapmode.flutter-starter"; 562 | PRODUCT_NAME = "$(TARGET_NAME)"; 563 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 564 | SWIFT_VERSION = 5.0; 565 | VERSIONING_SYSTEM = "apple-generic"; 566 | }; 567 | name = Release; 568 | }; 569 | /* End XCBuildConfiguration section */ 570 | 571 | /* Begin XCConfigurationList section */ 572 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 573 | isa = XCConfigurationList; 574 | buildConfigurations = ( 575 | 97C147031CF9000F007C117D /* Debug */, 576 | 97C147041CF9000F007C117D /* Release */, 577 | 249021D3217E4FDB00AE95B9 /* Profile */, 578 | ); 579 | defaultConfigurationIsVisible = 0; 580 | defaultConfigurationName = Release; 581 | }; 582 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 583 | isa = XCConfigurationList; 584 | buildConfigurations = ( 585 | 97C147061CF9000F007C117D /* Debug */, 586 | 97C147071CF9000F007C117D /* Release */, 587 | 249021D4217E4FDB00AE95B9 /* Profile */, 588 | ); 589 | defaultConfigurationIsVisible = 0; 590 | defaultConfigurationName = Release; 591 | }; 592 | /* End XCConfigurationList section */ 593 | }; 594 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 595 | } 596 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.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/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 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/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/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/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/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/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/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/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/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/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/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/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/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/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/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/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/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/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/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/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/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/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/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/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/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CLIENT_ID 6 | 524197078337-96irqf3smo96s6f48mc1neh2vh7jber5.apps.googleusercontent.com 7 | REVERSED_CLIENT_ID 8 | com.googleusercontent.apps.524197078337-96irqf3smo96s6f48mc1neh2vh7jber5 9 | API_KEY 10 | AIzaSyAZaaG4pky5QFv-P_mXKhmn8O5ms2ifWXE 11 | GCM_SENDER_ID 12 | 524197078337 13 | PLIST_VERSION 14 | 1 15 | BUNDLE_ID 16 | com.tapmode.flutter-starter 17 | PROJECT_ID 18 | flutter-starter-delay 19 | STORAGE_BUCKET 20 | flutter-starter-delay.appspot.com 21 | IS_ADS_ENABLED 22 | 23 | IS_ANALYTICS_ENABLED 24 | 25 | IS_APPINVITE_ENABLED 26 | 27 | IS_GCM_ENABLED 28 | 29 | IS_SIGNIN_ENABLED 30 | 31 | GOOGLE_APP_ID 32 | 1:524197078337:ios:7373a07c0abd9619fa2dc0 33 | DATABASE_URL 34 | https://flutter-starter-delay.firebaseio.com 35 | 36 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | flutter_starter 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleURLTypes 22 | 23 | 24 | CFBundleTypeRole 25 | Editor 26 | CFBundleURLSchemes 27 | 28 | com.googleusercontent.apps.524197078337-96irqf3smo96s6f48mc1neh2vh7jber5 29 | 30 | 31 | 32 | CFBundleVersion 33 | $(FLUTTER_BUILD_NUMBER) 34 | LSRequiresIPhoneOS 35 | 36 | UILaunchStoryboardName 37 | LaunchScreen 38 | UIMainStoryboardFile 39 | Main 40 | UISupportedInterfaceOrientations 41 | 42 | UIInterfaceOrientationPortrait 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | UISupportedInterfaceOrientations~ipad 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationPortraitUpsideDown 50 | UIInterfaceOrientationLandscapeLeft 51 | UIInterfaceOrientationLandscapeRight 52 | 53 | UIViewControllerBasedStatusBarAppearance 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /ios/Runner/Runner.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.applesignin 6 | 7 | Default 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /lib/constants/app_routes.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:flutter_starter/ui/ui.dart'; 3 | import 'package:flutter_starter/ui/auth/auth.dart'; 4 | 5 | class AppRoutes { 6 | AppRoutes._(); //this is to prevent anyone from instantiating this object 7 | static final routes = [ 8 | GetPage(name: '/', page: () => SplashUI()), 9 | GetPage(name: '/signin', page: () => SignInUI()), 10 | GetPage(name: '/signup', page: () => SignUpUI()), 11 | GetPage(name: '/home', page: () => HomeUI()), 12 | GetPage(name: '/settings', page: () => SettingsUI()), 13 | GetPage(name: '/reset-password', page: () => ResetPasswordUI()), 14 | GetPage(name: '/update-profile', page: () => UpdateProfileUI()), 15 | ]; 16 | } 17 | -------------------------------------------------------------------------------- /lib/constants/app_themes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AppThemes { 4 | AppThemes._(); 5 | 6 | static const Color dodgerBlue = Color.fromRGBO(29, 161, 242, 1); 7 | static const Color whiteLilac = Color.fromRGBO(248, 250, 252, 1); 8 | static const Color blackPearl = Color.fromRGBO(30, 31, 43, 1); 9 | static const Color brinkPink = Color.fromRGBO(255, 97, 136, 1); 10 | static const Color juneBud = Color.fromRGBO(186, 215, 97, 1); 11 | static const Color white = Color.fromRGBO(255, 255, 255, 1); 12 | static const Color nevada = Color.fromRGBO(105, 109, 119, 1); 13 | static const Color ebonyClay = Color.fromRGBO(40, 42, 58, 1); 14 | 15 | static String font1 = "ProductSans"; 16 | static String font2 = "Roboto"; 17 | //constants color range for light theme 18 | //main color 19 | static const Color _lightPrimaryColor = dodgerBlue; 20 | 21 | //Background Colors 22 | static const Color _lightBackgroundColor = whiteLilac; 23 | static const Color _lightBackgroundAppBarColor = _lightPrimaryColor; 24 | static const Color _lightBackgroundSecondaryColor = white; 25 | static const Color _lightBackgroundAlertColor = blackPearl; 26 | static const Color _lightBackgroundActionTextColor = white; 27 | static const Color _lightBackgroundErrorColor = brinkPink; 28 | static const Color _lightBackgroundSuccessColor = juneBud; 29 | 30 | //Text Colors 31 | static const Color _lightTextColor = Colors.black; 32 | static const Color _lightAlertTextColor = Colors.black; 33 | static const Color _lightTextSecondaryColor = Colors.black; 34 | 35 | //Border Color 36 | static const Color _lightBorderColor = nevada; 37 | 38 | //Icon Color 39 | static const Color _lightIconColor = nevada; 40 | 41 | //form input colors 42 | static const Color _lightInputFillColor = _lightBackgroundSecondaryColor; 43 | static const Color _lightBorderActiveColor = _lightPrimaryColor; 44 | static const Color _lightBorderErrorColor = brinkPink; 45 | 46 | //constants color range for dark theme 47 | static const Color _darkPrimaryColor = dodgerBlue; 48 | 49 | //Background Colors 50 | static const Color _darkBackgroundColor = ebonyClay; 51 | static const Color _darkBackgroundAppBarColor = _darkPrimaryColor; 52 | static const Color _darkBackgroundSecondaryColor = 53 | Color.fromRGBO(0, 0, 0, .6); 54 | static const Color _darkBackgroundAlertColor = blackPearl; 55 | static const Color _darkBackgroundActionTextColor = white; 56 | 57 | static const Color _darkBackgroundErrorColor = 58 | Color.fromRGBO(255, 97, 136, 1); 59 | static const Color _darkBackgroundSuccessColor = 60 | Color.fromRGBO(186, 215, 97, 1); 61 | 62 | //Text Colors 63 | static const Color _darkTextColor = Colors.white; 64 | static const Color _darkAlertTextColor = Colors.black; 65 | static const Color _darkTextSecondaryColor = Colors.black; 66 | 67 | //Border Color 68 | static const Color _darkBorderColor = nevada; 69 | 70 | //Icon Color 71 | static const Color _darkIconColor = nevada; 72 | 73 | static const Color _darkInputFillColor = _darkBackgroundSecondaryColor; 74 | static const Color _darkBorderActiveColor = _darkPrimaryColor; 75 | static const Color _darkBorderErrorColor = brinkPink; 76 | 77 | //text theme for light theme 78 | static final TextTheme _lightTextTheme = TextTheme( 79 | headline1: TextStyle(fontSize: 20.0, color: _lightTextColor), 80 | bodyText1: TextStyle(fontSize: 16.0, color: _lightTextColor), 81 | bodyText2: TextStyle(fontSize: 14.0, color: Colors.grey), 82 | button: TextStyle( 83 | fontSize: 15.0, color: _lightTextColor, fontWeight: FontWeight.w600), 84 | headline6: TextStyle(fontSize: 16.0, color: _lightTextColor), 85 | subtitle1: TextStyle(fontSize: 16.0, color: _lightTextColor), 86 | caption: TextStyle(fontSize: 12.0, color: _lightBackgroundAppBarColor), 87 | ); 88 | 89 | //the light theme 90 | static final ThemeData lightTheme = ThemeData( 91 | brightness: Brightness.light, 92 | fontFamily: font1, 93 | scaffoldBackgroundColor: _lightBackgroundColor, 94 | floatingActionButtonTheme: FloatingActionButtonThemeData( 95 | backgroundColor: _lightPrimaryColor, 96 | ), 97 | appBarTheme: AppBarTheme( 98 | color: _lightBackgroundAppBarColor, 99 | iconTheme: IconThemeData(color: _lightTextColor), 100 | textTheme: _lightTextTheme, 101 | ), 102 | colorScheme: ColorScheme.light( 103 | primary: _lightPrimaryColor, 104 | primaryVariant: _lightBackgroundColor, 105 | // secondary: _lightSecondaryColor, 106 | ), 107 | snackBarTheme: SnackBarThemeData( 108 | backgroundColor: _lightBackgroundAlertColor, 109 | actionTextColor: _lightBackgroundActionTextColor), 110 | iconTheme: IconThemeData( 111 | color: _lightIconColor, 112 | ), 113 | popupMenuTheme: PopupMenuThemeData(color: _lightBackgroundAppBarColor), 114 | textTheme: _lightTextTheme, 115 | buttonTheme: ButtonThemeData( 116 | shape: RoundedRectangleBorder( 117 | borderRadius: BorderRadius.circular(8), 118 | ), 119 | buttonColor: _lightPrimaryColor, 120 | textTheme: ButtonTextTheme.primary), 121 | unselectedWidgetColor: _lightPrimaryColor, 122 | inputDecorationTheme: InputDecorationTheme( 123 | //prefixStyle: TextStyle(color: _lightIconColor), 124 | border: OutlineInputBorder( 125 | borderSide: BorderSide(width: 1.0), 126 | borderRadius: BorderRadius.all( 127 | Radius.circular(8.0), 128 | )), 129 | enabledBorder: OutlineInputBorder( 130 | borderSide: BorderSide(color: _lightBorderColor, width: 1.0), 131 | borderRadius: BorderRadius.all(Radius.circular(8.0)), 132 | ), 133 | focusedBorder: OutlineInputBorder( 134 | borderSide: BorderSide(color: _lightBorderActiveColor), 135 | borderRadius: BorderRadius.all(Radius.circular(8.0)), 136 | ), 137 | errorBorder: OutlineInputBorder( 138 | borderSide: BorderSide(color: _lightBorderErrorColor), 139 | borderRadius: BorderRadius.all(Radius.circular(8.0)), 140 | ), 141 | focusedErrorBorder: OutlineInputBorder( 142 | borderSide: BorderSide(color: _lightBorderErrorColor), 143 | borderRadius: BorderRadius.all(Radius.circular(8.0)), 144 | ), 145 | fillColor: _lightBackgroundSecondaryColor, 146 | //focusColor: _lightBorderActiveColor, 147 | ), 148 | ); 149 | 150 | //text theme for dark theme 151 | /*static final TextStyle _darkScreenHeadingTextStyle = 152 | _lightScreenHeadingTextStyle.copyWith(color: _darkTextColor); 153 | static final TextStyle _darkScreenTaskNameTextStyle = 154 | _lightScreenTaskNameTextStyle.copyWith(color: _darkTextColor); 155 | static final TextStyle _darkScreenTaskDurationTextStyle = 156 | _lightScreenTaskDurationTextStyle; 157 | static final TextStyle _darkScreenButtonTextStyle = TextStyle( 158 | fontSize: 14.0, color: _darkTextColor, fontWeight: FontWeight.w500); 159 | static final TextStyle _darkScreenCaptionTextStyle = TextStyle( 160 | fontSize: 12.0, 161 | color: _darkBackgroundAppBarColor, 162 | fontWeight: FontWeight.w100);*/ 163 | 164 | static final TextTheme _darkTextTheme = TextTheme( 165 | headline1: TextStyle(fontSize: 20.0, color: _darkTextColor), 166 | bodyText1: TextStyle(fontSize: 16.0, color: _darkTextColor), 167 | bodyText2: TextStyle(fontSize: 14.0, color: Colors.grey), 168 | button: TextStyle( 169 | fontSize: 15.0, color: _darkTextColor, fontWeight: FontWeight.w600), 170 | headline6: TextStyle(fontSize: 16.0, color: _darkTextColor), 171 | subtitle1: TextStyle(fontSize: 16.0, color: _darkTextColor), 172 | caption: TextStyle(fontSize: 12.0, color: _darkBackgroundAppBarColor), 173 | ); 174 | 175 | //the dark theme 176 | static final ThemeData darkTheme = ThemeData( 177 | brightness: Brightness.dark, 178 | //primarySwatch: _darkPrimaryColor, //cant be Color on MaterialColor so it can compute different shades. 179 | accentColor: _darkPrimaryColor, //prefix icon color form input on focus 180 | 181 | fontFamily: font1, 182 | scaffoldBackgroundColor: _darkBackgroundColor, 183 | floatingActionButtonTheme: FloatingActionButtonThemeData( 184 | backgroundColor: _darkPrimaryColor, 185 | ), 186 | appBarTheme: AppBarTheme( 187 | color: _darkBackgroundAppBarColor, 188 | iconTheme: IconThemeData(color: _darkTextColor), 189 | textTheme: _darkTextTheme, 190 | ), 191 | colorScheme: ColorScheme.dark( 192 | primary: _darkPrimaryColor, 193 | primaryVariant: _darkBackgroundColor, 194 | 195 | // secondary: _darkSecondaryColor, 196 | ), 197 | snackBarTheme: SnackBarThemeData( 198 | contentTextStyle: TextStyle(color: Colors.white), 199 | backgroundColor: _darkBackgroundAlertColor, 200 | actionTextColor: _darkBackgroundActionTextColor), 201 | iconTheme: IconThemeData( 202 | color: _darkIconColor, //_darkIconColor, 203 | ), 204 | popupMenuTheme: PopupMenuThemeData(color: _darkBackgroundAppBarColor), 205 | textTheme: _darkTextTheme, 206 | buttonTheme: ButtonThemeData( 207 | shape: RoundedRectangleBorder( 208 | borderRadius: BorderRadius.circular(8), 209 | ), 210 | buttonColor: _darkPrimaryColor, 211 | textTheme: ButtonTextTheme.primary), 212 | unselectedWidgetColor: _darkPrimaryColor, 213 | inputDecorationTheme: InputDecorationTheme( 214 | prefixStyle: TextStyle(color: _darkIconColor), 215 | //labelStyle: TextStyle(color: nevada), 216 | border: OutlineInputBorder( 217 | borderSide: BorderSide(width: 1.0), 218 | borderRadius: BorderRadius.all( 219 | Radius.circular(8.0), 220 | )), 221 | enabledBorder: OutlineInputBorder( 222 | borderSide: BorderSide(color: _darkBorderColor, width: 1.0), 223 | borderRadius: BorderRadius.all(Radius.circular(8.0)), 224 | ), 225 | focusedBorder: OutlineInputBorder( 226 | borderSide: BorderSide(color: _darkBorderActiveColor), 227 | borderRadius: BorderRadius.all(Radius.circular(8.0)), 228 | ), 229 | errorBorder: OutlineInputBorder( 230 | borderSide: BorderSide(color: _darkBorderErrorColor), 231 | borderRadius: BorderRadius.all(Radius.circular(8.0)), 232 | ), 233 | focusedErrorBorder: OutlineInputBorder( 234 | borderSide: BorderSide(color: _darkBorderErrorColor), 235 | borderRadius: BorderRadius.all(Radius.circular(8.0)), 236 | ), 237 | fillColor: _darkInputFillColor, 238 | //focusColor: _darkBorderActiveColor, 239 | ), 240 | ); 241 | } 242 | -------------------------------------------------------------------------------- /lib/constants/constants.dart: -------------------------------------------------------------------------------- 1 | export 'app_routes.dart'; 2 | export 'globals.dart'; 3 | export 'app_themes.dart'; 4 | -------------------------------------------------------------------------------- /lib/constants/globals.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_starter/models/models.dart'; 2 | 3 | class Globals { 4 | static final String defaultLanguage = 'en'; 5 | //List of languages that are supported. Used in selector. 6 | //Follow this plugin for translating a google sheet to languages 7 | //https://github.com/aloisdeniel/flutter_sheet_localization 8 | //Flutter App translations google sheet 9 | //https://docs.google.com/spreadsheets/d/1oS7iJ6ocrZBA53SxRfKF0CG9HAaXeKtzvsTBhgG4Zzk/edit?usp=sharing 10 | 11 | static final List languageOptions = [ 12 | MenuOptionsModel(key: "zh", value: "中文"), //Chinese 13 | MenuOptionsModel(key: "de", value: "Deutsche"), //German 14 | MenuOptionsModel(key: "en", value: "English"), //English 15 | MenuOptionsModel(key: "es", value: "Español"), //Spanish 16 | MenuOptionsModel(key: "fr", value: "Français"), //French 17 | MenuOptionsModel(key: "hi", value: "हिन्दी"), //Hindi 18 | MenuOptionsModel(key: "ja", value: "日本語"), //Japanese 19 | MenuOptionsModel(key: "pt", value: "Português"), //Portuguese 20 | MenuOptionsModel(key: "ru", value: "русский"), //Russian 21 | ]; 22 | } 23 | -------------------------------------------------------------------------------- /lib/controllers/auth_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'dart:async'; 4 | import 'package:firebase_auth/firebase_auth.dart'; 5 | import 'package:get/get.dart'; 6 | import 'package:cloud_firestore/cloud_firestore.dart'; 7 | import 'package:flutter_starter/models/models.dart'; 8 | import 'package:flutter_starter/ui/auth/auth.dart'; 9 | import 'package:flutter_starter/ui/ui.dart'; 10 | import 'package:flutter_starter/ui/components/components.dart'; 11 | import 'package:flutter_starter/helpers/helpers.dart'; 12 | 13 | class AuthController extends GetxController { 14 | static AuthController to = Get.find(); 15 | TextEditingController nameController = TextEditingController(); 16 | TextEditingController emailController = TextEditingController(); 17 | TextEditingController passwordController = TextEditingController(); 18 | final FirebaseAuth _auth = FirebaseAuth.instance; 19 | final FirebaseFirestore _db = FirebaseFirestore.instance; 20 | Rxn firebaseUser = Rxn(); 21 | Rxn firestoreUser = Rxn(); 22 | final RxBool admin = false.obs; 23 | 24 | @override 25 | void onReady() async { 26 | //run every time auth state changes 27 | ever(firebaseUser, handleAuthChanged); 28 | 29 | firebaseUser.bindStream(user); 30 | 31 | super.onReady(); 32 | } 33 | 34 | @override 35 | void onClose() { 36 | nameController.dispose(); 37 | emailController.dispose(); 38 | passwordController.dispose(); 39 | super.onClose(); 40 | } 41 | 42 | handleAuthChanged(_firebaseUser) async { 43 | //get user data from firestore 44 | if (_firebaseUser?.uid != null) { 45 | firestoreUser.bindStream(streamFirestoreUser()); 46 | await isAdmin(); 47 | } 48 | 49 | if (_firebaseUser == null) { 50 | print('Send to signin'); 51 | Get.offAll(SignInUI()); 52 | } else { 53 | Get.offAll(HomeUI()); 54 | } 55 | } 56 | 57 | // Firebase user one-time fetch 58 | Future get getUser async => _auth.currentUser!; 59 | 60 | // Firebase user a realtime stream 61 | Stream get user => _auth.authStateChanges(); 62 | 63 | //Streams the firestore user from the firestore collection 64 | Stream streamFirestoreUser() { 65 | print('streamFirestoreUser()'); 66 | 67 | return _db 68 | .doc('/users/${firebaseUser.value!.uid}') 69 | .snapshots() 70 | .map((snapshot) => UserModel.fromMap(snapshot.data()!)); 71 | } 72 | 73 | //get the firestore user from the firestore collection 74 | Future getFirestoreUser() { 75 | return _db.doc('/users/${firebaseUser.value!.uid}').get().then( 76 | (documentSnapshot) => UserModel.fromMap(documentSnapshot.data()!)); 77 | } 78 | 79 | //Method to handle user sign in using email and password 80 | signInWithEmailAndPassword(BuildContext context) async { 81 | showLoadingIndicator(); 82 | try { 83 | await _auth.signInWithEmailAndPassword( 84 | email: emailController.text.trim(), 85 | password: passwordController.text.trim()); 86 | emailController.clear(); 87 | passwordController.clear(); 88 | hideLoadingIndicator(); 89 | } catch (error) { 90 | hideLoadingIndicator(); 91 | Get.snackbar('auth.signInErrorTitle'.tr, 'auth.signInError'.tr, 92 | snackPosition: SnackPosition.BOTTOM, 93 | duration: Duration(seconds: 7), 94 | backgroundColor: Get.theme.snackBarTheme.backgroundColor, 95 | colorText: Get.theme.snackBarTheme.actionTextColor); 96 | } 97 | } 98 | 99 | // User registration using email and password 100 | registerWithEmailAndPassword(BuildContext context) async { 101 | showLoadingIndicator(); 102 | try { 103 | await _auth 104 | .createUserWithEmailAndPassword( 105 | email: emailController.text, password: passwordController.text) 106 | .then((result) async { 107 | print('uID: ' + result.user!.uid.toString()); 108 | print('email: ' + result.user!.email.toString()); 109 | //get photo url from gravatar if user has one 110 | Gravatar gravatar = Gravatar(emailController.text); 111 | String gravatarUrl = gravatar.imageUrl( 112 | size: 200, 113 | defaultImage: GravatarImage.retro, 114 | rating: GravatarRating.pg, 115 | fileExtension: true, 116 | ); 117 | //create the new user object 118 | UserModel _newUser = UserModel( 119 | uid: result.user!.uid, 120 | email: result.user!.email!, 121 | name: nameController.text, 122 | photoUrl: gravatarUrl); 123 | //create the user in firestore 124 | _createUserFirestore(_newUser, result.user!); 125 | emailController.clear(); 126 | passwordController.clear(); 127 | hideLoadingIndicator(); 128 | }); 129 | } on FirebaseAuthException catch (error) { 130 | hideLoadingIndicator(); 131 | Get.snackbar('auth.signUpErrorTitle'.tr, error.message!, 132 | snackPosition: SnackPosition.BOTTOM, 133 | duration: Duration(seconds: 10), 134 | backgroundColor: Get.theme.snackBarTheme.backgroundColor, 135 | colorText: Get.theme.snackBarTheme.actionTextColor); 136 | } 137 | } 138 | 139 | //handles updating the user when updating profile 140 | Future updateUser(BuildContext context, UserModel user, String oldEmail, 141 | String password) async { 142 | String _authUpdateUserNoticeTitle = 'auth.updateUserSuccessNoticeTitle'.tr; 143 | String _authUpdateUserNotice = 'auth.updateUserSuccessNotice'.tr; 144 | try { 145 | showLoadingIndicator(); 146 | try { 147 | await _auth 148 | .signInWithEmailAndPassword(email: oldEmail, password: password) 149 | .then((_firebaseUser) async { 150 | await _firebaseUser.user! 151 | .updateEmail(user.email) 152 | .then((value) => _updateUserFirestore(user, _firebaseUser.user!)); 153 | }); 154 | } catch (err) { 155 | print('Caught error: $err'); 156 | //not yet working, see this issue https://github.com/delay/flutter_starter/issues/21 157 | if (err.toString() == 158 | "[firebase_auth/email-already-in-use] The email address is already in use by another account.") { 159 | _authUpdateUserNoticeTitle = 'auth.updateUserEmailInUse'.tr; 160 | _authUpdateUserNotice = 'auth.updateUserEmailInUse'.tr; 161 | } else { 162 | _authUpdateUserNoticeTitle = 'auth.wrongPasswordNotice'.tr; 163 | _authUpdateUserNotice = 'auth.wrongPasswordNotice'.tr; 164 | } 165 | } 166 | hideLoadingIndicator(); 167 | Get.snackbar(_authUpdateUserNoticeTitle, _authUpdateUserNotice, 168 | snackPosition: SnackPosition.BOTTOM, 169 | duration: Duration(seconds: 5), 170 | backgroundColor: Get.theme.snackBarTheme.backgroundColor, 171 | colorText: Get.theme.snackBarTheme.actionTextColor); 172 | } on PlatformException catch (error) { 173 | //List errors = error.toString().split(','); 174 | // print("Error: " + errors[1]); 175 | hideLoadingIndicator(); 176 | print(error.code); 177 | String authError; 178 | switch (error.code) { 179 | case 'ERROR_WRONG_PASSWORD': 180 | authError = 'auth.wrongPasswordNotice'.tr; 181 | break; 182 | default: 183 | authError = 'auth.unknownError'.tr; 184 | break; 185 | } 186 | Get.snackbar('auth.wrongPasswordNoticeTitle'.tr, authError, 187 | snackPosition: SnackPosition.BOTTOM, 188 | duration: Duration(seconds: 10), 189 | backgroundColor: Get.theme.snackBarTheme.backgroundColor, 190 | colorText: Get.theme.snackBarTheme.actionTextColor); 191 | } 192 | } 193 | 194 | //updates the firestore user in users collection 195 | void _updateUserFirestore(UserModel user, User _firebaseUser) { 196 | _db.doc('/users/${_firebaseUser.uid}').update(user.toJson()); 197 | update(); 198 | } 199 | 200 | //create the firestore user in users collection 201 | void _createUserFirestore(UserModel user, User _firebaseUser) { 202 | _db.doc('/users/${_firebaseUser.uid}').set(user.toJson()); 203 | update(); 204 | } 205 | 206 | //password reset email 207 | Future sendPasswordResetEmail(BuildContext context) async { 208 | showLoadingIndicator(); 209 | try { 210 | await _auth.sendPasswordResetEmail(email: emailController.text); 211 | hideLoadingIndicator(); 212 | Get.snackbar( 213 | 'auth.resetPasswordNoticeTitle'.tr, 'auth.resetPasswordNotice'.tr, 214 | snackPosition: SnackPosition.BOTTOM, 215 | duration: Duration(seconds: 5), 216 | backgroundColor: Get.theme.snackBarTheme.backgroundColor, 217 | colorText: Get.theme.snackBarTheme.actionTextColor); 218 | } on FirebaseAuthException catch (error) { 219 | hideLoadingIndicator(); 220 | Get.snackbar('auth.resetPasswordFailed'.tr, error.message!, 221 | snackPosition: SnackPosition.BOTTOM, 222 | duration: Duration(seconds: 10), 223 | backgroundColor: Get.theme.snackBarTheme.backgroundColor, 224 | colorText: Get.theme.snackBarTheme.actionTextColor); 225 | } 226 | } 227 | 228 | //check if user is an admin user 229 | isAdmin() async { 230 | await getUser.then((user) async { 231 | DocumentSnapshot adminRef = 232 | await _db.collection('admin').doc(user.uid).get(); 233 | if (adminRef.exists) { 234 | admin.value = true; 235 | } else { 236 | admin.value = false; 237 | } 238 | update(); 239 | }); 240 | } 241 | 242 | // Sign out 243 | Future signOut() { 244 | nameController.clear(); 245 | emailController.clear(); 246 | passwordController.clear(); 247 | return _auth.signOut(); 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /lib/controllers/controllers.dart: -------------------------------------------------------------------------------- 1 | export 'theme_controller.dart'; 2 | export 'language_controller.dart'; 3 | export 'auth_controller.dart'; 4 | -------------------------------------------------------------------------------- /lib/controllers/language_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_starter/constants/globals.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:get_storage/get_storage.dart'; 5 | import 'dart:ui' as ui; 6 | 7 | class LanguageController extends GetxController { 8 | static LanguageController get to => Get.find(); 9 | final language = "".obs; 10 | final store = GetStorage(); 11 | 12 | String get currentLanguage => language.value; 13 | 14 | @override 15 | void onReady() async { 16 | //setInitialLocalLanguage(); 17 | super.onInit(); 18 | } 19 | 20 | // Retrieves and Sets language based on device settings 21 | setInitialLocalLanguage() { 22 | if (currentLanguageStore.value == '') { 23 | String _deviceLanguage = ui.window.locale.toString(); 24 | _deviceLanguage = 25 | _deviceLanguage.substring(0, 2); //only get 1st 2 characters 26 | print(ui.window.locale.toString()); 27 | updateLanguage(_deviceLanguage); 28 | } 29 | } 30 | 31 | // Gets current language stored 32 | RxString get currentLanguageStore { 33 | language.value = store.read('language') ?? ''; 34 | return language; 35 | } 36 | 37 | // gets the language locale app is set to 38 | Locale? get getLocale { 39 | if (currentLanguageStore.value == '') { 40 | language.value = Globals.defaultLanguage; 41 | updateLanguage(Globals.defaultLanguage); 42 | } else if (currentLanguageStore.value != '') { 43 | //set the stored string country code to the locale 44 | return Locale(currentLanguageStore.value); 45 | } 46 | // gets the default language key for the system. 47 | return Get.deviceLocale; 48 | } 49 | 50 | // updates the language stored 51 | Future updateLanguage(String value) async { 52 | language.value = value; 53 | await store.write('language', value); 54 | if (getLocale != null) { 55 | Get.updateLocale(getLocale!); 56 | } 57 | update(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/controllers/theme_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:get_storage/get_storage.dart'; 4 | 5 | // https://gist.github.com/RodBr/37310335c6639f486bb3c8a628052405 6 | //https://medium.com/swlh/flutter-dynamic-themes-in-3-lines-c3b375f292e3 7 | 8 | class ThemeController extends GetxController { 9 | static ThemeController get to => Get.find(); 10 | final theme = "system".obs; 11 | final store = GetStorage(); 12 | late ThemeMode _themeMode; 13 | 14 | ThemeMode get themeMode => _themeMode; 15 | String get currentTheme => theme.value; 16 | 17 | Future setThemeMode(String value) async { 18 | theme.value = value; 19 | _themeMode = getThemeModeFromString(value); 20 | Get.changeThemeMode(_themeMode); 21 | await store.write('theme', value); 22 | update(); 23 | } 24 | 25 | ThemeMode getThemeModeFromString(String themeString) { 26 | ThemeMode _setThemeMode = ThemeMode.system; 27 | if (themeString == 'light') { 28 | _setThemeMode = ThemeMode.light; 29 | } 30 | if (themeString == 'dark') { 31 | _setThemeMode = ThemeMode.dark; 32 | } 33 | return _setThemeMode; 34 | } 35 | 36 | getThemeModeFromStore() async { 37 | String _themeString = store.read('theme') ?? 'system'; 38 | setThemeMode(_themeString); 39 | } 40 | 41 | // checks whether darkmode is set via system or previously by user 42 | bool get isDarkModeOn { 43 | if (currentTheme == 'system') { 44 | if (WidgetsBinding.instance!.window.platformBrightness == 45 | Brightness.dark) { 46 | return true; 47 | } 48 | } 49 | if (currentTheme == 'dark') { 50 | return true; 51 | } 52 | return false; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/helpers/gravatar.dart: -------------------------------------------------------------------------------- 1 | //code from uses wrong version of crypto 2 | //to use default package https://github.com/subosito/simple_gravatar/blob/master/lib/simple_gravatar.dart 3 | 4 | import 'dart:convert'; 5 | import 'package:crypto/crypto.dart'; 6 | 7 | enum GravatarImage { 8 | nf, // 404 9 | mp, // mystery person 10 | identicon, 11 | monsterid, 12 | wavatar, 13 | retro, 14 | robohash, 15 | blank, 16 | } 17 | 18 | enum GravatarRating { 19 | g, 20 | pg, 21 | r, 22 | x, 23 | } 24 | 25 | class Gravatar { 26 | final String email; 27 | final String hash; 28 | 29 | Gravatar(this.email) : this.hash = _generateHash(email); 30 | 31 | static String _generateHash(String email) { 32 | String preparedEmail = email.trim().toLowerCase(); 33 | return md5.convert(utf8.encode(preparedEmail)).toString(); 34 | } 35 | 36 | String imageUrl({ 37 | int? size, 38 | GravatarImage? defaultImage, 39 | bool forceDefault = false, 40 | bool fileExtension = false, 41 | GravatarRating? rating, 42 | }) { 43 | String hashDigest = hash; 44 | Map query = {}; 45 | 46 | if (size != null) query['s'] = size.toString(); 47 | if (defaultImage != null) query['d'] = _imageString(defaultImage); 48 | if (forceDefault) query['f'] = 'y'; 49 | if (rating != null) query['r'] = _ratingString(rating); 50 | if (fileExtension) hashDigest += '.png'; 51 | //if (query.isEmpty) query = null; 52 | 53 | return Uri.https('www.gravatar.com', '/avatar/$hashDigest', query) 54 | .toString(); 55 | } 56 | 57 | String jsonUrl() { 58 | return Uri.https('www.gravatar.com', '/$hash.json').toString(); 59 | } 60 | 61 | String qrUrl() { 62 | return Uri.https('www.gravatar.com', '/$hash.qr').toString(); 63 | } 64 | 65 | String toString() { 66 | return imageUrl(); 67 | } 68 | 69 | String _imageString(GravatarImage value) { 70 | switch (value) { 71 | case GravatarImage.nf: 72 | return '404'; 73 | case GravatarImage.mp: 74 | return 'mp'; 75 | case GravatarImage.identicon: 76 | return 'identicon'; 77 | case GravatarImage.monsterid: 78 | return 'monsterid'; 79 | case GravatarImage.wavatar: 80 | return 'wavatar'; 81 | case GravatarImage.retro: 82 | return 'retro'; 83 | case GravatarImage.robohash: 84 | return 'robohash'; 85 | case GravatarImage.blank: 86 | return 'blank'; 87 | } 88 | } 89 | 90 | String _ratingString(GravatarRating value) { 91 | switch (value) { 92 | case GravatarRating.g: 93 | return 'g'; 94 | case GravatarRating.pg: 95 | return 'pg'; 96 | case GravatarRating.r: 97 | return 'r'; 98 | case GravatarRating.x: 99 | return 'x'; 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /lib/helpers/helpers.dart: -------------------------------------------------------------------------------- 1 | export 'validator.dart'; 2 | export 'gravatar.dart'; 3 | export 'localization.g.dart'; 4 | -------------------------------------------------------------------------------- /lib/helpers/localization.g.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | class Localization extends Translations { 4 | @override 5 | Map> get keys => { 6 | 'en': { 7 | 'auth.signInButton': 'Sign In', 8 | 'auth.signUpButton': 'Sign Up', 9 | 'auth.resetPasswordButton': 'Send Password Reset', 10 | 'auth.emailFormField': 'Email', 11 | 'auth.passwordFormField': 'Password', 12 | 'auth.nameFormField': 'Name', 13 | 'auth.signInErrorTitle': 'Sign In Error', 14 | 'auth.signInError': 'Login failed: email or password incorrect.', 15 | 'auth.resetPasswordLabelButton': 'Forgot password?', 16 | 'auth.signUpLabelButton': 'Create an Account', 17 | 'auth.signUpErrorTitle': 'Sign Up Failed.', 18 | 'auth.signUpError': 'There was a problem signing up. Please try again later.', 19 | 'auth.signInLabelButton': 'Have an Account? Sign In.', 20 | 'auth.resetPasswordNoticeTitle': 'Password Reset Email Sent', 21 | 'auth.resetPasswordNotice': 'Check your email and follow the instructions to reset your password.', 22 | 'auth.resetPasswordFailed': 'Password Reset Email Failed', 23 | 'auth.signInonResetPasswordLabelButton': 'Sign In', 24 | 'auth.updateUser': 'Update Profile', 25 | 'auth.updateUserSuccessNoticeTitle': 'User Updated', 26 | 'auth.updateUserSuccessNotice': 'User information successfully updated.', 27 | 'auth.updateUserEmailInUse': 'That email address already has an account.', 28 | 'auth.updateUserFailNotice': 'Failed to update user', 29 | 'auth.enterPassword': 'Enter your password', 30 | 'auth.cancel': 'Cancel', 31 | 'auth.submit': 'Submit', 32 | 'auth.changePasswordLabelButton': 'Change Password', 33 | 'auth.resetPasswordTitle': 'Reset Password', 34 | 'auth.updateProfileTitle': 'Update Profile', 35 | 'auth.wrongPasswordNoticeTitle': 'Login Failed', 36 | 'auth.wrongPasswordNotice': 'The password does not match our records.', 37 | 'auth.unknownError': 'Unknown Error', 38 | 'settings.title': 'Settings', 39 | 'settings.language': 'Language', 40 | 'settings.theme': 'Theme', 41 | 'settings.signOut': 'Sign Out', 42 | 'settings.dark': 'Dark', 43 | 'settings.light': 'Light', 44 | 'settings.system': 'System', 45 | 'settings.updateProfile': 'Update Profile', 46 | 'home.title': 'Home', 47 | 'home.nameLabel': 'Name', 48 | 'home.uidLabel': 'UID', 49 | 'home.emailLabel': 'Email', 50 | 'home.adminUserLabel': 'Admin User', 51 | 'app.title': 'Flutter Starter Project', 52 | 'validator.email': 'Please enter a valid email address.', 53 | 'validator.password': 'Password must be at least 6 characters.', 54 | 'validator.name': 'Please enter a name.', 55 | 'validator.number': 'Please enter a number.', 56 | 'validator.notEmpty': 'This is a required field.', 57 | 'validator.amount': 'Please enter a number i.e. 250 - no dollar symbol and no cents', 58 | }, 59 | 'fr': { 60 | 'auth.signInButton': 'S\'identifier', 61 | 'auth.signUpButton': 'S\'inscrire', 62 | 'auth.resetPasswordButton': 'Envoyer Password Reset', 63 | 'auth.emailFormField': 'E-mail', 64 | 'auth.passwordFormField': 'Mot de passe', 65 | 'auth.nameFormField': 'Nom', 66 | 'auth.signInErrorTitle': 'Erreur de connexion', 67 | 'auth.signInError': 'Échec de la connexion: e-mail ou mot de passe incorrect.', 68 | 'auth.resetPasswordLabelButton': 'Mot de passe oublié?', 69 | 'auth.signUpLabelButton': 'Créer un compte', 70 | 'auth.signUpErrorTitle': 'Échec de l\'inscription.', 71 | 'auth.signUpError': 'Il y avait un problème de signer. Veuillez réessayer plus tard.', 72 | 'auth.signInLabelButton': 'Avoir un compte? S\'identifier.', 73 | 'auth.resetPasswordNoticeTitle': 'Réinitialiser le mot de passe e-mail envoyé', 74 | 'auth.resetPasswordNotice': 'Vérifiez votre e-mail et suivez les instructions pour réinitialiser votre mot de passe.', 75 | 'auth.resetPasswordFailed': 'Réinitialiser le mot de passe Email Échec', 76 | 'auth.signInonResetPasswordLabelButton': 'S\'identifier', 77 | 'auth.updateUser': 'Mettre à jour le profil', 78 | 'auth.updateUserSuccessNoticeTitle': 'Mise à jour l\'utilisateur', 79 | 'auth.updateUserSuccessNotice': 'Informations sur l\'utilisateur mis à jour avec succès.', 80 | 'auth.updateUserEmailInUse': 'Cette adresse e-mail a déjà un compte.', 81 | 'auth.updateUserFailNotice': 'Impossible de mettre à jour l\'utilisateur', 82 | 'auth.enterPassword': 'Tapez votre mot de passe', 83 | 'auth.cancel': 'Annuler', 84 | 'auth.submit': 'Soumettre', 85 | 'auth.changePasswordLabelButton': 'Changer le mot de passe', 86 | 'auth.resetPasswordTitle': 'réinitialiser le mot de passe', 87 | 'auth.updateProfileTitle': 'Mettre à jour le profil', 88 | 'auth.wrongPasswordNoticeTitle': 'Échec de la connexion', 89 | 'auth.wrongPasswordNotice': 'Le mot de passe ne correspond pas à nos dossiers.', 90 | 'auth.unknownError': 'Erreur inconnue', 91 | 'settings.title': 'Paramètres', 92 | 'settings.language': 'Langue', 93 | 'settings.theme': 'Thème', 94 | 'settings.signOut': 'Se déconnecter', 95 | 'settings.dark': 'Sombre', 96 | 'settings.light': 'Lumière', 97 | 'settings.system': 'Système', 98 | 'settings.updateProfile': 'Mettre à jour le profil', 99 | 'home.title': 'Domicile', 100 | 'home.nameLabel': 'Nom', 101 | 'home.uidLabel': 'UID', 102 | 'home.emailLabel': 'E-mail', 103 | 'home.adminUserLabel': 'utilisateur admin', 104 | 'app.title': 'Flutter projet de démarrage', 105 | 'validator.email': 'S\'il vous plaît, mettez une adresse email valide.', 106 | 'validator.password': 'Le mot de passe doit être au moins de 6 caractères.', 107 | 'validator.name': 'S\'il vous plaît entrer un nom.', 108 | 'validator.number': 'S\'il vous plaît entrer un numéro.', 109 | 'validator.notEmpty': 'Ceci est un champ obligatoire.', 110 | 'validator.amount': 'S\'il vous plaît entrer un numéro à savoir 250 - aucun symbole du dollar et pas cents', 111 | }, 112 | 'es': { 113 | 'auth.signInButton': 'Iniciar sesión', 114 | 'auth.signUpButton': 'Inscribirse', 115 | 'auth.resetPasswordButton': 'Enviar restablecimiento de contraseña', 116 | 'auth.emailFormField': 'Correo electrónico', 117 | 'auth.passwordFormField': 'Contraseña', 118 | 'auth.nameFormField': 'Nombre', 119 | 'auth.signInErrorTitle': 'Error al iniciar sesión', 120 | 'auth.signInError': 'La conexión falló: correo electrónico o contraseña incorrecta.', 121 | 'auth.resetPasswordLabelButton': '¿Se te olvidó tu contraseña?', 122 | 'auth.signUpLabelButton': 'Crea una cuenta', 123 | 'auth.signUpErrorTitle': 'Registro fallido.', 124 | 'auth.signUpError': 'Hubo un problema al inscribirse. Por favor, inténtelo de nuevo más tarde.', 125 | 'auth.signInLabelButton': '¿Tener una cuenta? Iniciar sesión.', 126 | 'auth.resetPasswordNoticeTitle': 'Restablecer contraseña de correo electrónico enviados', 127 | 'auth.resetPasswordNotice': 'Consultar su correo electrónico y siga las instrucciones para restablecer su contraseña.', 128 | 'auth.resetPasswordFailed': 'Restablecer contraseña de correo electrónico incorrecto', 129 | 'auth.signInonResetPasswordLabelButton': 'Iniciar sesión', 130 | 'auth.updateUser': 'Actualización del perfil', 131 | 'auth.updateUserSuccessNoticeTitle': 'Actualización de usuario', 132 | 'auth.updateUserSuccessNotice': 'La información de usuario actualizada correctamente.', 133 | 'auth.updateUserEmailInUse': 'Esa dirección de correo electrónico ya tiene una cuenta.', 134 | 'auth.updateUserFailNotice': 'Error al usuario la actualización', 135 | 'auth.enterPassword': 'Ingresa tu contraseña', 136 | 'auth.cancel': 'Cancelar', 137 | 'auth.submit': 'Enviar', 138 | 'auth.changePasswordLabelButton': 'Cambiar la contraseña', 139 | 'auth.resetPasswordTitle': 'Restablecer la contraseña', 140 | 'auth.updateProfileTitle': 'Actualización del perfil', 141 | 'auth.wrongPasswordNoticeTitle': 'Error de inicio de sesion', 142 | 'auth.wrongPasswordNotice': 'La contraseña no coincide con nuestros registros.', 143 | 'auth.unknownError': 'Error desconocido', 144 | 'settings.title': 'Ajustes', 145 | 'settings.language': 'Idioma', 146 | 'settings.theme': 'Tema', 147 | 'settings.signOut': 'Desconectar', 148 | 'settings.dark': 'Oscuro', 149 | 'settings.light': 'Luz', 150 | 'settings.system': 'Sistema', 151 | 'settings.updateProfile': 'Actualización del perfil', 152 | 'home.title': 'Casa', 153 | 'home.nameLabel': 'Nombre', 154 | 'home.uidLabel': 'UID', 155 | 'home.emailLabel': 'Correo electrónico', 156 | 'home.adminUserLabel': 'admin User', 157 | 'app.title': 'Proyecto de arranque aleteo', 158 | 'validator.email': 'Por favor, introduce una dirección de correo electrónico válida.', 159 | 'validator.password': 'La contraseña debe tener al menos 6 caracteres.', 160 | 'validator.name': 'Por favor, introduzca un nombre.', 161 | 'validator.number': 'Por favor, introduzca un número.', 162 | 'validator.notEmpty': 'Este es un campo obligatorio.', 163 | 'validator.amount': 'Por favor, introduzca un número, es decir 250 - ningún símbolo del dólar y sin centavos', 164 | }, 165 | 'de': { 166 | 'auth.signInButton': 'Einloggen', 167 | 'auth.signUpButton': 'Anmeldung', 168 | 'auth.resetPasswordButton': 'Senden Passwort zurücksetzen', 169 | 'auth.emailFormField': 'Email', 170 | 'auth.passwordFormField': 'Passwort', 171 | 'auth.nameFormField': 'Name', 172 | 'auth.signInErrorTitle': 'Anmelden Fehler', 173 | 'auth.signInError': 'Fehler bei der Anmeldung: E-Mail oder Passwort falsch.', 174 | 'auth.resetPasswordLabelButton': 'Passwort vergessen?', 175 | 'auth.signUpLabelButton': 'Ein Konto erstellen', 176 | 'auth.signUpErrorTitle': 'Anmeldung gescheitert.', 177 | 'auth.signUpError': 'Es gab ein Problem anmeldest. Bitte versuchen Sie es später noch einmal.', 178 | 'auth.signInLabelButton': 'Ein Konto haben? Einloggen.', 179 | 'auth.resetPasswordNoticeTitle': 'Passwort zurücksetzen E-Mail gesendet', 180 | 'auth.resetPasswordNotice': 'Überprüfen Sie Ihre E-Mail und folgen Sie den Anweisungen, um Ihr Passwort zurücksetzen können.', 181 | 'auth.resetPasswordFailed': 'Passwort zurücksetzen E-Mail fehlgeschlagen', 182 | 'auth.signInonResetPasswordLabelButton': 'Einloggen', 183 | 'auth.updateUser': 'Profil aktualisieren', 184 | 'auth.updateUserSuccessNoticeTitle': 'Benutzer Aktualisiert', 185 | 'auth.updateUserSuccessNotice': 'Benutzerinformationen erfolgreich aktualisiert.', 186 | 'auth.updateUserEmailInUse': 'Die E-Mail-Adresse hat bereits ein Konto.', 187 | 'auth.updateUserFailNotice': 'Fehler beim Update Benutzer', 188 | 'auth.enterPassword': 'Geben Sie Ihr Passwort', 189 | 'auth.cancel': 'Stornieren', 190 | 'auth.submit': 'einreichen', 191 | 'auth.changePasswordLabelButton': 'Kennwort ändern', 192 | 'auth.resetPasswordTitle': 'Passwort zurücksetzen', 193 | 'auth.updateProfileTitle': 'Profil aktualisieren', 194 | 'auth.wrongPasswordNoticeTitle': 'Fehler bei der Anmeldung', 195 | 'auth.wrongPasswordNotice': 'Das Passwort nicht unsere entsprechenden Datensätze gefunden.', 196 | 'auth.unknownError': 'Unbekannter Fehler', 197 | 'settings.title': 'die Einstellungen', 198 | 'settings.language': 'Sprache', 199 | 'settings.theme': 'Thema', 200 | 'settings.signOut': 'Austragen', 201 | 'settings.dark': 'Dunkel', 202 | 'settings.light': 'Licht', 203 | 'settings.system': 'System', 204 | 'settings.updateProfile': 'Profil aktualisieren', 205 | 'home.title': 'Zuhause', 206 | 'home.nameLabel': 'Name', 207 | 'home.uidLabel': 'UID', 208 | 'home.emailLabel': 'Email', 209 | 'home.adminUserLabel': 'Admin Benutzer', 210 | 'app.title': 'Flutter Starter-Projekt', 211 | 'validator.email': 'Bitte geben Sie eine gültige E-Mail-Adresse ein.', 212 | 'validator.password': 'Passwort muss mindestens 6 Zeichen lang sein.', 213 | 'validator.name': 'Bitte geben Sie einen Namen.', 214 | 'validator.number': 'Bitte gebe eine Nummer ein.', 215 | 'validator.notEmpty': 'Dies ist ein Pflichtfeld.', 216 | 'validator.amount': 'Bitte geben Sie eine Zahl das heißt 250 - kein Dollar-Symbol und keinen Cent', 217 | }, 218 | 'hi': { 219 | 'auth.signInButton': 'दाखिल करना', 220 | 'auth.signUpButton': 'साइन अप करें', 221 | 'auth.resetPasswordButton': 'भेजें पासवर्ड रीसेट', 222 | 'auth.emailFormField': 'ईमेल', 223 | 'auth.passwordFormField': 'कुंजिका', 224 | 'auth.nameFormField': 'नाम', 225 | 'auth.signInErrorTitle': 'साइन इन त्रुटि', 226 | 'auth.signInError': 'लॉगइन असफल: ईमेल या पासवर्ड गलत है।', 227 | 'auth.resetPasswordLabelButton': 'पासवर्ड भूल गए?', 228 | 'auth.signUpLabelButton': 'खाता बनाएं', 229 | 'auth.signUpErrorTitle': 'साइन अप करने में विफल रहा।', 230 | 'auth.signUpError': 'साइन अप करने में समस्या हुई थी। बाद में पुन: प्रयास करें।', 231 | 'auth.signInLabelButton': 'एक खाता है? दाखिल करना।', 232 | 'auth.resetPasswordNoticeTitle': 'पासवर्ड रीसेट ईमेल भेजा', 233 | 'auth.resetPasswordNotice': 'अपने ईमेल की जाँच करें और निर्देशों का अपना पासवर्ड रीसेट करने का पालन करें।', 234 | 'auth.resetPasswordFailed': 'पासवर्ड रीसेट ईमेल में विफल', 235 | 'auth.signInonResetPasswordLabelButton': 'दाखिल करना', 236 | 'auth.updateUser': 'प्रोफ़ाइल अपडेट करें', 237 | 'auth.updateUserSuccessNoticeTitle': 'उपयोगकर्ता अपडेट किया गया', 238 | 'auth.updateUserSuccessNotice': 'उपयोगकर्ता जानकारी सफलतापूर्वक अपडेट।', 239 | 'auth.updateUserEmailInUse': 'यही कारण है कि ईमेल पता पहले से ही खाता है।', 240 | 'auth.updateUserFailNotice': 'उपयोगकर्ता अद्यतन करने में विफल', 241 | 'auth.enterPassword': 'अपना पासवर्ड डालें', 242 | 'auth.cancel': 'रद्द करना', 243 | 'auth.submit': 'प्रस्तुत', 244 | 'auth.changePasswordLabelButton': 'पासवर्ड बदलें', 245 | 'auth.resetPasswordTitle': 'पासवर्ड रीसेट', 246 | 'auth.updateProfileTitle': 'प्रोफ़ाइल अपडेट करें', 247 | 'auth.wrongPasswordNoticeTitle': 'लॉगिन विफल', 248 | 'auth.wrongPasswordNotice': 'पासवर्ड हमारे रिकॉर्ड से मेल नहीं खाता।', 249 | 'auth.unknownError': 'अज्ञात त्रुटि', 250 | 'settings.title': 'समायोजन', 251 | 'settings.language': 'भाषा: हिन्दी', 252 | 'settings.theme': 'विषय', 253 | 'settings.signOut': 'प्रस्थान करें', 254 | 'settings.dark': 'अंधेरा', 255 | 'settings.light': 'रोशनी', 256 | 'settings.system': 'प्रणाली', 257 | 'settings.updateProfile': 'प्रोफ़ाइल अपडेट करें', 258 | 'home.title': 'घर', 259 | 'home.nameLabel': 'नाम', 260 | 'home.uidLabel': 'यूआईडी', 261 | 'home.emailLabel': 'ईमेल', 262 | 'home.adminUserLabel': 'व्यवस्थापक उपयोगकर्ता', 263 | 'app.title': 'स्पंदन स्टार्टर परियोजना', 264 | 'validator.email': 'कृपया एक वैध ई - मेल एड्रेस डालें।', 265 | 'validator.password': 'पासवर्ड कम से कम 6 अंकों का होना चाहिए।', 266 | 'validator.name': 'एक नाम दर्ज करें।', 267 | 'validator.number': 'एक संख्या दर्ज करें।', 268 | 'validator.notEmpty': 'यह एक आवश्यक फील्ड है।', 269 | 'validator.amount': 'कोई डॉलर प्रतीक और कोई सेंट - एक नंबर अर्थात 250 दर्ज करें', 270 | }, 271 | 'pt': { 272 | 'auth.signInButton': 'Entrar', 273 | 'auth.signUpButton': 'Inscrever-se', 274 | 'auth.resetPasswordButton': 'Enviar Password Reset', 275 | 'auth.emailFormField': 'E-mail', 276 | 'auth.passwordFormField': 'Senha', 277 | 'auth.nameFormField': 'Nome', 278 | 'auth.signInErrorTitle': 'Entrar erro', 279 | 'auth.signInError': 'Falha de logon: e-mail ou senha incorreta.', 280 | 'auth.resetPasswordLabelButton': 'Esqueceu a senha?', 281 | 'auth.signUpLabelButton': 'Crie a sua conta aqui', 282 | 'auth.signUpErrorTitle': 'Registre-se Falhou.', 283 | 'auth.signUpError': 'Houve um problema se inscrever. Por favor, tente novamente mais tarde.', 284 | 'auth.signInLabelButton': 'Ter uma conta? Entrar.', 285 | 'auth.resetPasswordNoticeTitle': 'Senha enviada uma reinicialização', 286 | 'auth.resetPasswordNotice': 'Verifique se o seu e-mail e siga as instruções para redefinir sua senha.', 287 | 'auth.resetPasswordFailed': 'Password Reset-mail Falha', 288 | 'auth.signInonResetPasswordLabelButton': 'Entrar', 289 | 'auth.updateUser': 'Atualizar perfil', 290 | 'auth.updateUserSuccessNoticeTitle': 'do usuário atualizada', 291 | 'auth.updateUserSuccessNotice': 'informações do usuário atualizado com sucesso.', 292 | 'auth.updateUserEmailInUse': 'Este endereço de email já tem uma conta.', 293 | 'auth.updateUserFailNotice': 'Falha ao usuário de atualização', 294 | 'auth.enterPassword': 'Coloque sua senha', 295 | 'auth.cancel': 'Cancelar', 296 | 'auth.submit': 'Enviar', 297 | 'auth.changePasswordLabelButton': 'Alterar a senha', 298 | 'auth.resetPasswordTitle': 'Password Reset', 299 | 'auth.updateProfileTitle': 'Atualizar perfil', 300 | 'auth.wrongPasswordNoticeTitle': 'Falha no login', 301 | 'auth.wrongPasswordNotice': 'A senha não coincide com nossos registros.', 302 | 'auth.unknownError': 'Erro desconhecido', 303 | 'settings.title': 'Definições', 304 | 'settings.language': 'Língua', 305 | 'settings.theme': 'Tema', 306 | 'settings.signOut': 'Sair', 307 | 'settings.dark': 'Escuro', 308 | 'settings.light': 'Luz', 309 | 'settings.system': 'Sistema', 310 | 'settings.updateProfile': 'Atualizar perfil', 311 | 'home.title': 'Casa', 312 | 'home.nameLabel': 'Nome', 313 | 'home.uidLabel': 'UID', 314 | 'home.emailLabel': 'E-mail', 315 | 'home.adminUserLabel': 'admin User', 316 | 'app.title': 'Projeto de arranque Flutter', 317 | 'validator.email': 'Por favor insira um endereço de e-mail válido.', 318 | 'validator.password': 'A senha deve ter pelo menos 6 caracteres.', 319 | 'validator.name': 'Por favor, indique um nome.', 320 | 'validator.number': 'Por favor, coloque um numero.', 321 | 'validator.notEmpty': 'Este é um campo obrigatório.', 322 | 'validator.amount': 'Por favor insira um número ou seja 250 - nenhum símbolo dólar e há centavos', 323 | }, 324 | 'zh': { 325 | 'auth.signInButton': '登入', 326 | 'auth.signUpButton': '报名', 327 | 'auth.resetPasswordButton': '发送密码重置', 328 | 'auth.emailFormField': '电子邮件', 329 | 'auth.passwordFormField': '密码', 330 | 'auth.nameFormField': '名称', 331 | 'auth.signInErrorTitle': '登录错误', 332 | 'auth.signInError': '登录失败:电子邮件或密码不正确。', 333 | 'auth.resetPasswordLabelButton': '忘记密码?', 334 | 'auth.signUpLabelButton': '创建一个帐户', 335 | 'auth.signUpErrorTitle': '注册失败。', 336 | 'auth.signUpError': '有注册的问题。请稍后再试。', 337 | 'auth.signInLabelButton': '有一个账户?登入。', 338 | 'auth.resetPasswordNoticeTitle': '密码重置邮件已发送', 339 | 'auth.resetPasswordNotice': '检查你的电子邮件,并按照重置密码的说明。', 340 | 'auth.resetPasswordFailed': '密码重置电子邮件失败', 341 | 'auth.signInonResetPasswordLabelButton': '登入', 342 | 'auth.updateUser': '更新个人信息', 343 | 'auth.updateUserSuccessNoticeTitle': '用户更新', 344 | 'auth.updateUserSuccessNotice': '用户信息更新成功。', 345 | 'auth.updateUserEmailInUse': '该电子邮件地址已经有一个帐户。', 346 | 'auth.updateUserFailNotice': '无法更新用户', 347 | 'auth.enterPassword': '输入您的密码', 348 | 'auth.cancel': '取消', 349 | 'auth.submit': '提交', 350 | 'auth.changePasswordLabelButton': '更改密码', 351 | 'auth.resetPasswordTitle': '重设密码', 352 | 'auth.updateProfileTitle': '更新个人信息', 353 | 'auth.wrongPasswordNoticeTitle': '登录失败', 354 | 'auth.wrongPasswordNotice': '该密码不符合我们的记录。', 355 | 'auth.unknownError': '未知错误', 356 | 'settings.title': '设置', 357 | 'settings.language': '语', 358 | 'settings.theme': '主题', 359 | 'settings.signOut': '登出', 360 | 'settings.dark': '黑暗的', 361 | 'settings.light': '光', 362 | 'settings.system': '系统', 363 | 'settings.updateProfile': '更新个人信息', 364 | 'home.title': '家', 365 | 'home.nameLabel': '名称', 366 | 'home.uidLabel': 'UID', 367 | 'home.emailLabel': '电子邮件', 368 | 'home.adminUserLabel': '管理员用户', 369 | 'app.title': '扑启动项目', 370 | 'validator.email': '请输入有效的电子邮件地址。', 371 | 'validator.password': '密码必须至少6个字符。', 372 | 'validator.name': '请输入姓名。', 373 | 'validator.number': '请输入一个数字。', 374 | 'validator.notEmpty': '这是一个必填字段。', 375 | 'validator.amount': '请输入一个数,即250 - 没有美元符号和无分', 376 | }, 377 | 'ja': { 378 | 'auth.signInButton': 'サインイン', 379 | 'auth.signUpButton': 'サインアップ', 380 | 'auth.resetPasswordButton': '送信パスワードリセット', 381 | 'auth.emailFormField': 'Eメール', 382 | 'auth.passwordFormField': 'パスワード', 383 | 'auth.nameFormField': '名前', 384 | 'auth.signInErrorTitle': 'エラーサインイン', 385 | 'auth.signInError': 'ログインに失敗しました:電子メールまたはパスワードが正しくありません。', 386 | 'auth.resetPasswordLabelButton': 'パスワードをお忘れですか?', 387 | 'auth.signUpLabelButton': 'アカウントを作成する', 388 | 'auth.signUpErrorTitle': 'サインアップは失敗しました。', 389 | 'auth.signUpError': 'サインアップする問題が発生しました。後ほど再度お試しください。', 390 | 'auth.signInLabelButton': 'アカウントを持っています?サインイン。', 391 | 'auth.resetPasswordNoticeTitle': 'パスワードリセットのメール送信され', 392 | 'auth.resetPasswordNotice': 'あなたの電子メールをチェックして、あなたのパスワードをリセットするための指示に従ってください。', 393 | 'auth.resetPasswordFailed': 'パスワードリセットのメールが失敗しました。', 394 | 'auth.signInonResetPasswordLabelButton': 'サインイン', 395 | 'auth.updateUser': 'プロフィールを更新', 396 | 'auth.updateUserSuccessNoticeTitle': 'ユーザーの更新', 397 | 'auth.updateUserSuccessNotice': 'ユーザー情報が正常に更新します。', 398 | 'auth.updateUserEmailInUse': 'そのメールアドレスは、既にアカウントを持っています。', 399 | 'auth.updateUserFailNotice': '更新ユーザーに失敗しました。', 400 | 'auth.enterPassword': 'パスワードを入力してください', 401 | 'auth.cancel': 'キャンセル', 402 | 'auth.submit': '参加する', 403 | 'auth.changePasswordLabelButton': 'パスワードを変更する', 404 | 'auth.resetPasswordTitle': 'パスワードを再設定する', 405 | 'auth.updateProfileTitle': 'プロフィールを更新', 406 | 'auth.wrongPasswordNoticeTitle': 'ログインに失敗しました', 407 | 'auth.wrongPasswordNotice': 'パスワードは我々の記録と一致しません。', 408 | 'auth.unknownError': '不明なエラー', 409 | 'settings.title': '設定', 410 | 'settings.language': '言語', 411 | 'settings.theme': 'テーマ', 412 | 'settings.signOut': 'サインアウト', 413 | 'settings.dark': '闇', 414 | 'settings.light': '光', 415 | 'settings.system': 'システム', 416 | 'settings.updateProfile': 'プロフィールを更新', 417 | 'home.title': '家', 418 | 'home.nameLabel': '名前', 419 | 'home.uidLabel': 'UID', 420 | 'home.emailLabel': 'Eメール', 421 | 'home.adminUserLabel': '管理者ユーザー', 422 | 'app.title': 'フラッタースタータープロジェクト', 423 | 'validator.email': '有効なメールアドレスを入力してください。', 424 | 'validator.password': 'パスワードは少なくとも6文字でなければなりません。', 425 | 'validator.name': '名前を入力してください。', 426 | 'validator.number': '番号を入力してください。', 427 | 'validator.notEmpty': 'これは必要項目です。', 428 | 'validator.amount': 'ノードル記号なしセント - すなわち、250番号を入力してください。', 429 | }, 430 | 'ru': { 431 | 'auth.signInButton': 'Войти', 432 | 'auth.signUpButton': 'Зарегистрироваться', 433 | 'auth.resetPasswordButton': 'Отправить Сброс пароля', 434 | 'auth.emailFormField': 'Электронное письмо', 435 | 'auth.passwordFormField': 'Пароль', 436 | 'auth.nameFormField': 'Имя', 437 | 'auth.signInErrorTitle': 'Ошибка входа', 438 | 'auth.signInError': 'Войти не удалось: адрес электронной почты или пароль неверен.', 439 | 'auth.resetPasswordLabelButton': 'забыл пароль?', 440 | 'auth.signUpLabelButton': 'Завести аккаунт', 441 | 'auth.signUpErrorTitle': 'Регистрация прошла неудачно.', 442 | 'auth.signUpError': 'Была проблема подписания. Пожалуйста, повторите попытку позже.', 443 | 'auth.signInLabelButton': 'Иметь аккаунт? Войти.', 444 | 'auth.resetPasswordNoticeTitle': 'Сброс пароля Email Sent', 445 | 'auth.resetPasswordNotice': 'Проверьте электронную почту и следуйте инструкциям, чтобы сбросить пароль.', 446 | 'auth.resetPasswordFailed': 'Сброс пароля Email Failed', 447 | 'auth.signInonResetPasswordLabelButton': 'Войти', 448 | 'auth.updateUser': 'Обновить профиль', 449 | 'auth.updateUserSuccessNoticeTitle': 'Пользователь Обновлено', 450 | 'auth.updateUserSuccessNotice': 'Информация о пользователе успешно обновлена.', 451 | 'auth.updateUserEmailInUse': 'Этот адрес электронной почты уже есть учетная запись.', 452 | 'auth.updateUserFailNotice': 'Не удался пользователь обновления', 453 | 'auth.enterPassword': 'Введите свой пароль', 454 | 'auth.cancel': 'Отмена', 455 | 'auth.submit': 'Представлять на рассмотрение', 456 | 'auth.changePasswordLabelButton': 'Измени пароль', 457 | 'auth.resetPasswordTitle': 'Сброс пароля', 458 | 'auth.updateProfileTitle': 'Обновить профиль', 459 | 'auth.wrongPasswordNoticeTitle': 'Ошибка входа', 460 | 'auth.wrongPasswordNotice': 'Пароль не соответствует нашим данным.', 461 | 'auth.unknownError': 'Неизвестная ошибка', 462 | 'settings.title': 'Настройки', 463 | 'settings.language': 'Язык', 464 | 'settings.theme': 'Тема', 465 | 'settings.signOut': 'Выход', 466 | 'settings.dark': 'Темный', 467 | 'settings.light': 'Свет', 468 | 'settings.system': 'Система', 469 | 'settings.updateProfile': 'Обновить профиль', 470 | 'home.title': 'Дом', 471 | 'home.nameLabel': 'Имя', 472 | 'home.uidLabel': 'UID', 473 | 'home.emailLabel': 'Электронное письмо', 474 | 'home.adminUserLabel': 'Пользователь Admin', 475 | 'app.title': 'Проект флаттер Starter', 476 | 'validator.email': 'Пожалуйста, введите действительный адрес электронной почты.', 477 | 'validator.password': 'Пароль должен быть не менее 6 символов.', 478 | 'validator.name': 'Пожалуйста, введите имя.', 479 | 'validator.number': 'Пожалуйста, введите номер.', 480 | 'validator.notEmpty': 'Это поле обязательно для заполнения.', 481 | 'validator.amount': 'Пожалуйста, введите номер 250 - т.е. без символа доллара и ни цента', 482 | }, 483 | }; 484 | } 485 | -------------------------------------------------------------------------------- /lib/helpers/update_localizations.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'dart:async'; 3 | import 'dart:convert'; 4 | import 'package:http/http.dart' as http; 5 | import 'package:csv/csv.dart'; 6 | 7 | void main() { 8 | updateLocalizationFile(); 9 | } 10 | 11 | Future updateLocalizationFile() async { 12 | //the document id for your google sheet 13 | String documentId = "1oS7iJ6ocrZBA53SxRfKF0CG9HAaXeKtzvsTBhgG4Zzk"; 14 | //the sheetid of your google sheet 15 | String sheetId = "0"; 16 | 17 | String _phraseKey = ''; 18 | List _localizations = []; 19 | String _localizationFile = """import 'package:get/get.dart'; 20 | 21 | class Localization extends Translations { 22 | @override 23 | Map> get keys => { 24 | """; 25 | 26 | try { 27 | final url = 28 | 'https://docs.google.com/spreadsheets/d/$documentId/export?format=csv&id=$documentId&gid=$sheetId'; 29 | 30 | stdout.writeln(''); 31 | stdout.writeln('---------------------------------------'); 32 | stdout.writeln('Downloading Google sheet url "$url" ...'); 33 | stdout.writeln('---------------------------------------'); 34 | var response = await http 35 | .get(Uri.parse(url), headers: {'accept': 'text/csv;charset=UTF-8'}); 36 | 37 | // print('Google sheet csv:\n ${response.body}'); 38 | 39 | final bytes = response.bodyBytes.toList(); 40 | final csv = Stream>.fromIterable([bytes]); 41 | 42 | final fields = await csv 43 | .transform(utf8.decoder) 44 | .transform(CsvToListConverter( 45 | shouldParseNumbers: false, 46 | )) 47 | .toList(); 48 | 49 | final index = fields[0] 50 | .cast() 51 | .map(_uniformizeKey) 52 | .takeWhile((x) => x.isNotEmpty) 53 | .toList(); 54 | 55 | for (var r = 1; r < fields.length; r++) { 56 | final rowValues = fields[r]; 57 | 58 | /// Creating a map 59 | final row = Map.fromEntries( 60 | rowValues 61 | .asMap() 62 | .entries 63 | .where( 64 | (e) => e.key < index.length, 65 | ) 66 | .map( 67 | (e) => MapEntry(index[e.key], e.value), 68 | ), 69 | ); 70 | 71 | row.forEach((key, value) { 72 | if (key == 'key') { 73 | _phraseKey = value; 74 | } else { 75 | bool _languageAdded = false; 76 | _localizations.forEach((element) { 77 | if (element.language == key) { 78 | element.phrases.add(PhraseModel(key: _phraseKey, phrase: value)); 79 | _languageAdded = true; 80 | } 81 | }); 82 | if (_languageAdded == false) { 83 | _localizations.add(LocalizationModel( 84 | language: key, 85 | phrases: [PhraseModel(key: _phraseKey, phrase: value)])); 86 | } 87 | } 88 | }); 89 | } 90 | 91 | _localizations.forEach((_localization) { 92 | String _language = _localization.language; 93 | String _currentLanguageTextCode = "'$_language': {\n"; 94 | _localizationFile = _localizationFile + _currentLanguageTextCode; 95 | _localization.phrases.forEach((_phrase) { 96 | String _phraseKey = _phrase.key; 97 | String _phrasePhrase = _phrase.phrase.replaceAll(r"'", "\\\'"); 98 | String _currentPhraseTextCode = "'$_phraseKey': '$_phrasePhrase',\n"; 99 | _localizationFile = _localizationFile + _currentPhraseTextCode; 100 | }); 101 | String _currentLanguageCodeEnding = "},\n"; 102 | _localizationFile = _localizationFile + _currentLanguageCodeEnding; 103 | }); 104 | String _fileEnding = """ 105 | }; 106 | } 107 | """; 108 | _localizationFile = _localizationFile + _fileEnding; 109 | 110 | stdout.writeln(''); 111 | stdout.writeln('---------------------------------------'); 112 | stdout.writeln('Saving localization.g.dart'); 113 | stdout.writeln('---------------------------------------'); 114 | final file = File('localization.g.dart'); 115 | await file.writeAsString(_localizationFile); 116 | stdout.writeln('Done...'); 117 | } catch (e) { 118 | //output error 119 | stderr.writeln('error: networking error'); 120 | stderr.writeln(e.toString()); 121 | } 122 | } 123 | 124 | String _uniformizeKey(String key) { 125 | key = key.trim().replaceAll('\n', '').toLowerCase(); 126 | return key; 127 | } 128 | 129 | //Localization Model 130 | class LocalizationModel { 131 | final String language; 132 | final List phrases; 133 | 134 | LocalizationModel({ 135 | required this.language, 136 | required this.phrases, 137 | }); 138 | 139 | factory LocalizationModel.fromMap(Map data) { 140 | return LocalizationModel( 141 | language: data['language'], 142 | phrases: 143 | (data['phrases'] as List).map((v) => PhraseModel.fromMap(v)).toList(), 144 | ); 145 | } 146 | 147 | Map toJson() => { 148 | "language": language, 149 | "phrases": List.from(phrases.map((x) => x.toJson())), 150 | }; 151 | } 152 | 153 | class PhraseModel { 154 | String key; 155 | String phrase; 156 | 157 | PhraseModel({required this.key, required this.phrase}); 158 | 159 | factory PhraseModel.fromMap(Map data) { 160 | return PhraseModel( 161 | key: data['key'], 162 | phrase: data['phrase'] ?? '', 163 | ); 164 | } 165 | Map toJson() => { 166 | "key": key, 167 | "phrase": phrase, 168 | }; 169 | } 170 | -------------------------------------------------------------------------------- /lib/helpers/validator.dart: -------------------------------------------------------------------------------- 1 | // matching various patterns for kinds of data 2 | import 'package:get/get.dart'; 3 | 4 | class Validator { 5 | Validator(); 6 | 7 | String? email(String? value) { 8 | String pattern = r'^[a-zA-Z0-9.]+@[a-zA-Z0-9]+\.[a-zA-Z]+'; 9 | RegExp regex = RegExp(pattern); 10 | if (!regex.hasMatch(value!)) 11 | return 'validator.email'.tr; 12 | else 13 | return null; 14 | } 15 | 16 | String? password(String? value) { 17 | String pattern = r'^.{6,}$'; 18 | RegExp regex = RegExp(pattern); 19 | if (!regex.hasMatch(value!)) 20 | return 'validator.password'.tr; 21 | else 22 | return null; 23 | } 24 | 25 | String? name(String? value) { 26 | String pattern = r"^[a-zA-Z]+(([',. -][a-zA-Z ])?[a-zA-Z]*)*$"; 27 | RegExp regex = RegExp(pattern); 28 | if (!regex.hasMatch(value!)) 29 | return 'validator.name'.tr; 30 | else 31 | return null; 32 | } 33 | 34 | String? number(String? value) { 35 | String pattern = r'^\D?(\d{3})\D?\D?(\d{3})\D?(\d{4})$'; 36 | RegExp regex = RegExp(pattern); 37 | if (!regex.hasMatch(value!)) 38 | return 'validator.number'.tr; 39 | else 40 | return null; 41 | } 42 | 43 | String? amount(String? value) { 44 | String pattern = r'^\d+$'; 45 | RegExp regex = RegExp(pattern); 46 | if (!regex.hasMatch(value!)) 47 | return 'validator.amount'.tr; 48 | else 49 | return null; 50 | } 51 | 52 | String? notEmpty(String? value) { 53 | String pattern = r'^\S+$'; 54 | RegExp regex = RegExp(pattern); 55 | if (!regex.hasMatch(value!)) 56 | return 'validator.notEmpty'.tr; 57 | else 58 | return null; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:firebase_core/firebase_core.dart'; 3 | //import 'package:firebase_analytics/firebase_analytics.dart'; 4 | //import 'package:firebase_analytics/observer.dart'; 5 | import 'package:flutter_starter/controllers/controllers.dart'; 6 | import 'package:flutter_starter/constants/constants.dart'; 7 | import 'package:flutter_starter/ui/components/components.dart'; 8 | import 'package:flutter_starter/helpers/helpers.dart'; 9 | import 'package:get/get.dart'; 10 | import 'package:get_storage/get_storage.dart'; 11 | 12 | void main() async { 13 | WidgetsFlutterBinding.ensureInitialized(); 14 | await Firebase.initializeApp(); 15 | await GetStorage.init(); 16 | Get.put(AuthController()); 17 | Get.put(ThemeController()); 18 | Get.put(LanguageController()); 19 | runApp(MyApp()); 20 | } 21 | 22 | class MyApp extends StatelessWidget { 23 | @override 24 | Widget build(BuildContext context) { 25 | ThemeController.to.getThemeModeFromStore(); 26 | return GetBuilder( 27 | builder: (languageController) => Loading( 28 | child: GetMaterialApp( 29 | translations: Localization(), 30 | locale: languageController.getLocale, // <- Current locale 31 | navigatorObservers: [ 32 | // FirebaseAnalyticsObserver(analytics: FirebaseAnalytics()), 33 | ], 34 | debugShowCheckedModeBanner: false, 35 | //defaultTransition: Transition.fade, 36 | theme: AppThemes.lightTheme, 37 | darkTheme: AppThemes.darkTheme, 38 | themeMode: ThemeMode.system, 39 | initialRoute: "/", 40 | getPages: AppRoutes.routes, 41 | ), 42 | ), 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/models/menu_option_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | // Model class to hold menu option data (language and theme) 4 | class MenuOptionsModel { 5 | final String key; 6 | final String value; 7 | final IconData? icon; 8 | 9 | MenuOptionsModel({required this.key, required this.value, this.icon}); 10 | } 11 | -------------------------------------------------------------------------------- /lib/models/models.dart: -------------------------------------------------------------------------------- 1 | export 'user_model.dart'; 2 | export 'menu_option_model.dart'; 3 | -------------------------------------------------------------------------------- /lib/models/user_model.dart: -------------------------------------------------------------------------------- 1 | //User Model 2 | class UserModel { 3 | final String uid; 4 | final String email; 5 | final String name; 6 | final String photoUrl; 7 | 8 | UserModel( 9 | {required this.uid, 10 | required this.email, 11 | required this.name, 12 | required this.photoUrl}); 13 | 14 | factory UserModel.fromMap(Map data) { 15 | return UserModel( 16 | uid: data['uid'], 17 | email: data['email'] ?? '', 18 | name: data['name'] ?? '', 19 | photoUrl: data['photoUrl'] ?? '', 20 | ); 21 | } 22 | 23 | Map toJson() => 24 | {"uid": uid, "email": email, "name": name, "photoUrl": photoUrl}; 25 | } 26 | -------------------------------------------------------------------------------- /lib/ui/auth/auth.dart: -------------------------------------------------------------------------------- 1 | export 'sign_in_ui.dart'; 2 | export 'sign_up_ui.dart'; 3 | export 'reset_password_ui.dart'; 4 | export 'update_profile_ui.dart'; 5 | -------------------------------------------------------------------------------- /lib/ui/auth/reset_password_ui.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:flutter_starter/ui/auth/auth.dart'; 5 | import 'package:flutter_starter/ui/components/components.dart'; 6 | import 'package:flutter_starter/helpers/helpers.dart'; 7 | import 'package:flutter_starter/controllers/controllers.dart'; 8 | 9 | class ResetPasswordUI extends StatelessWidget { 10 | final AuthController authController = AuthController.to; 11 | final GlobalKey _formKey = GlobalKey(); 12 | 13 | Widget build(BuildContext context) { 14 | return Scaffold( 15 | appBar: appBar(context), 16 | body: Form( 17 | key: _formKey, 18 | child: Padding( 19 | padding: const EdgeInsets.symmetric(horizontal: 24.0), 20 | child: Center( 21 | child: SingleChildScrollView( 22 | child: Column( 23 | mainAxisAlignment: MainAxisAlignment.center, 24 | crossAxisAlignment: CrossAxisAlignment.stretch, 25 | children: [ 26 | LogoGraphicHeader(), 27 | SizedBox(height: 48.0), 28 | FormInputFieldWithIcon( 29 | controller: authController.emailController, 30 | iconPrefix: Icons.email, 31 | labelText: 'auth.emailFormField'.tr, 32 | validator: Validator().email, 33 | keyboardType: TextInputType.emailAddress, 34 | onChanged: (value) => null, 35 | onSaved: (value) => 36 | authController.emailController.text = value as String, 37 | ), 38 | FormVerticalSpace(), 39 | PrimaryButton( 40 | labelText: 'auth.resetPasswordButton'.tr, 41 | onPressed: () async { 42 | if (_formKey.currentState!.validate()) { 43 | await authController.sendPasswordResetEmail(context); 44 | } 45 | }), 46 | FormVerticalSpace(), 47 | signInLink(context), 48 | ], 49 | ), 50 | ), 51 | ), 52 | ), 53 | ), 54 | ); 55 | } 56 | 57 | appBar(BuildContext context) { 58 | if (authController.emailController.text == '') { 59 | return null; 60 | } 61 | return AppBar(title: Text('auth.resetPasswordTitle'.tr)); 62 | } 63 | 64 | signInLink(BuildContext context) { 65 | if (authController.emailController.text == '') { 66 | return LabelButton( 67 | labelText: 'auth.signInonResetPasswordLabelButton'.tr, 68 | onPressed: () => Get.offAll(SignInUI()), 69 | ); 70 | } 71 | return Container(width: 0, height: 0); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /lib/ui/auth/sign_in_ui.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | 4 | import 'dart:core'; 5 | import 'package:get/get.dart'; 6 | import 'package:flutter_starter/ui/auth/auth.dart'; 7 | import 'package:flutter_starter/ui/components/components.dart'; 8 | import 'package:flutter_starter/helpers/helpers.dart'; 9 | import 'package:flutter_starter/controllers/controllers.dart'; 10 | 11 | class SignInUI extends StatelessWidget { 12 | final AuthController authController = AuthController.to; 13 | final GlobalKey _formKey = GlobalKey(); 14 | 15 | Widget build(BuildContext context) { 16 | return Scaffold( 17 | body: Form( 18 | key: _formKey, 19 | child: Padding( 20 | padding: const EdgeInsets.symmetric(horizontal: 24.0), 21 | child: Center( 22 | child: SingleChildScrollView( 23 | child: Column( 24 | mainAxisAlignment: MainAxisAlignment.center, 25 | crossAxisAlignment: CrossAxisAlignment.stretch, 26 | children: [ 27 | LogoGraphicHeader(), 28 | SizedBox(height: 48.0), 29 | FormInputFieldWithIcon( 30 | controller: authController.emailController, 31 | iconPrefix: Icons.email, 32 | labelText: 'auth.emailFormField'.tr, 33 | validator: Validator().email, 34 | keyboardType: TextInputType.emailAddress, 35 | onChanged: (value) => null, 36 | onSaved: (value) => 37 | authController.emailController.text = value!, 38 | ), 39 | FormVerticalSpace(), 40 | FormInputFieldWithIcon( 41 | controller: authController.passwordController, 42 | iconPrefix: Icons.lock, 43 | labelText: 'auth.passwordFormField'.tr, 44 | validator: Validator().password, 45 | obscureText: true, 46 | onChanged: (value) => null, 47 | onSaved: (value) => 48 | authController.passwordController.text = value!, 49 | maxLines: 1, 50 | ), 51 | FormVerticalSpace(), 52 | PrimaryButton( 53 | labelText: 'auth.signInButton'.tr, 54 | onPressed: () async { 55 | if (_formKey.currentState!.validate()) { 56 | authController.signInWithEmailAndPassword(context); 57 | } 58 | }), 59 | FormVerticalSpace(), 60 | LabelButton( 61 | labelText: 'auth.resetPasswordLabelButton'.tr, 62 | onPressed: () => Get.to(ResetPasswordUI()), 63 | ), 64 | LabelButton( 65 | labelText: 'auth.signUpLabelButton'.tr, 66 | onPressed: () => Get.to(SignUpUI()), 67 | ), 68 | ], 69 | ), 70 | ), 71 | ), 72 | ), 73 | ), 74 | ); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/ui/auth/sign_up_ui.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:flutter_starter/ui/components/components.dart'; 5 | import 'package:flutter_starter/helpers/helpers.dart'; 6 | import 'package:flutter_starter/controllers/controllers.dart'; 7 | import 'package:flutter_starter/ui/auth/auth.dart'; 8 | 9 | class SignUpUI extends StatelessWidget { 10 | final AuthController authController = AuthController.to; 11 | final GlobalKey _formKey = GlobalKey(); 12 | 13 | Widget build(BuildContext context) { 14 | return Scaffold( 15 | body: Form( 16 | key: _formKey, 17 | child: Padding( 18 | padding: const EdgeInsets.symmetric(horizontal: 24.0), 19 | child: Center( 20 | child: SingleChildScrollView( 21 | child: Column( 22 | mainAxisAlignment: MainAxisAlignment.center, 23 | crossAxisAlignment: CrossAxisAlignment.stretch, 24 | children: [ 25 | LogoGraphicHeader(), 26 | SizedBox(height: 48.0), 27 | FormInputFieldWithIcon( 28 | controller: authController.nameController, 29 | iconPrefix: Icons.person, 30 | labelText: 'auth.nameFormField'.tr, 31 | validator: Validator().name, 32 | onChanged: (value) => null, 33 | onSaved: (value) => 34 | authController.nameController.text = value!, 35 | ), 36 | FormVerticalSpace(), 37 | FormInputFieldWithIcon( 38 | controller: authController.emailController, 39 | iconPrefix: Icons.email, 40 | labelText: 'auth.emailFormField'.tr, 41 | validator: Validator().email, 42 | keyboardType: TextInputType.emailAddress, 43 | onChanged: (value) => null, 44 | onSaved: (value) => 45 | authController.emailController.text = value!, 46 | ), 47 | FormVerticalSpace(), 48 | FormInputFieldWithIcon( 49 | controller: authController.passwordController, 50 | iconPrefix: Icons.lock, 51 | labelText: 'auth.passwordFormField'.tr, 52 | validator: Validator().password, 53 | obscureText: true, 54 | onChanged: (value) => null, 55 | onSaved: (value) => 56 | authController.passwordController.text = value!, 57 | maxLines: 1, 58 | ), 59 | FormVerticalSpace(), 60 | PrimaryButton( 61 | labelText: 'auth.signUpButton'.tr, 62 | onPressed: () async { 63 | if (_formKey.currentState!.validate()) { 64 | SystemChannels.textInput.invokeMethod( 65 | 'TextInput.hide'); //to hide the keyboard - if any 66 | authController.registerWithEmailAndPassword(context); 67 | } 68 | }), 69 | FormVerticalSpace(), 70 | LabelButton( 71 | labelText: 'auth.signInLabelButton'.tr, 72 | onPressed: () => Get.to(SignInUI()), 73 | ), 74 | ], 75 | ), 76 | ), 77 | ), 78 | ), 79 | ), 80 | ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lib/ui/auth/update_profile_ui.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:flutter_starter/models/models.dart'; 5 | import 'package:flutter_starter/ui/components/components.dart'; 6 | import 'package:flutter_starter/helpers/helpers.dart'; 7 | import 'package:flutter_starter/controllers/controllers.dart'; 8 | import 'package:flutter_starter/ui/auth/auth.dart'; 9 | 10 | class UpdateProfileUI extends StatelessWidget { 11 | final AuthController authController = AuthController.to; 12 | final GlobalKey _formKey = GlobalKey(); 13 | 14 | Widget build(BuildContext context) { 15 | //print('user.name: ' + user?.value?.name); 16 | authController.nameController.text = 17 | authController.firestoreUser.value!.name; 18 | authController.emailController.text = 19 | authController.firestoreUser.value!.email; 20 | return Scaffold( 21 | appBar: AppBar(title: Text('auth.updateProfileTitle'.tr)), 22 | body: Form( 23 | key: _formKey, 24 | child: Padding( 25 | padding: const EdgeInsets.symmetric(horizontal: 24.0), 26 | child: Center( 27 | child: SingleChildScrollView( 28 | child: Column( 29 | mainAxisAlignment: MainAxisAlignment.center, 30 | crossAxisAlignment: CrossAxisAlignment.stretch, 31 | children: [ 32 | LogoGraphicHeader(), 33 | SizedBox(height: 48.0), 34 | FormInputFieldWithIcon( 35 | controller: authController.nameController, 36 | iconPrefix: Icons.person, 37 | labelText: 'auth.nameFormField'.tr, 38 | validator: Validator().name, 39 | onChanged: (value) => null, 40 | onSaved: (value) => 41 | authController.nameController.text = value!, 42 | ), 43 | FormVerticalSpace(), 44 | FormInputFieldWithIcon( 45 | controller: authController.emailController, 46 | iconPrefix: Icons.email, 47 | labelText: 'auth.emailFormField'.tr, 48 | validator: Validator().email, 49 | keyboardType: TextInputType.emailAddress, 50 | onChanged: (value) => null, 51 | onSaved: (value) => 52 | authController.emailController.text = value!, 53 | ), 54 | FormVerticalSpace(), 55 | PrimaryButton( 56 | labelText: 'auth.updateUser'.tr, 57 | onPressed: () async { 58 | if (_formKey.currentState!.validate()) { 59 | SystemChannels.textInput 60 | .invokeMethod('TextInput.hide'); 61 | UserModel _updatedUser = UserModel( 62 | uid: authController.firestoreUser.value!.uid, 63 | name: authController.nameController.text, 64 | email: authController.emailController.text, 65 | photoUrl: 66 | authController.firestoreUser.value!.photoUrl); 67 | _updateUserConfirm(context, _updatedUser, 68 | authController.firestoreUser.value!.email); 69 | } 70 | }), 71 | FormVerticalSpace(), 72 | LabelButton( 73 | labelText: 'auth.resetPasswordLabelButton'.tr, 74 | onPressed: () => Get.to(ResetPasswordUI()), 75 | ), 76 | ], 77 | ), 78 | ), 79 | ), 80 | ), 81 | ), 82 | ); 83 | } 84 | 85 | Future _updateUserConfirm( 86 | BuildContext context, UserModel updatedUser, String oldEmail) async { 87 | final AuthController authController = AuthController.to; 88 | final TextEditingController _password = new TextEditingController(); 89 | return Get.dialog( 90 | AlertDialog( 91 | shape: RoundedRectangleBorder( 92 | borderRadius: BorderRadius.all(Radius.circular(8.0))), 93 | title: Text( 94 | 'auth.enterPassword'.tr, 95 | ), 96 | content: FormInputFieldWithIcon( 97 | controller: _password, 98 | iconPrefix: Icons.lock, 99 | labelText: 'auth.passwordFormField'.tr, 100 | validator: (value) { 101 | String pattern = r'^.{6,}$'; 102 | RegExp regex = RegExp(pattern); 103 | if (!regex.hasMatch(value!)) 104 | return 'validator.password'.tr; 105 | else 106 | return null; 107 | }, 108 | obscureText: true, 109 | onChanged: (value) => null, 110 | onSaved: (value) => _password.text = value!, 111 | maxLines: 1, 112 | ), 113 | actions: [ 114 | new TextButton( 115 | child: new Text('auth.cancel'.tr.toUpperCase()), 116 | onPressed: () { 117 | Get.back(); 118 | }, 119 | ), 120 | new TextButton( 121 | child: new Text('auth.submit'.tr.toUpperCase()), 122 | onPressed: () async { 123 | Get.back(); 124 | await authController.updateUser( 125 | context, updatedUser, oldEmail, _password.text); 126 | }, 127 | ) 128 | ], 129 | ), 130 | ); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /lib/ui/components/avatar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_starter/models/models.dart'; 3 | import 'package:flutter_starter/ui/components/components.dart'; 4 | 5 | class Avatar extends StatelessWidget { 6 | Avatar( 7 | this.user, 8 | ); 9 | final UserModel user; 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | if (user.photoUrl == '') { 14 | return LogoGraphicHeader(); 15 | } 16 | return Hero( 17 | tag: 'User Avatar Image', 18 | child: CircleAvatar( 19 | foregroundColor: Colors.blue, 20 | backgroundColor: Colors.white, 21 | radius: 70.0, 22 | child: ClipOval( 23 | child: Image.network( 24 | user.photoUrl, 25 | fit: BoxFit.cover, 26 | width: 120.0, 27 | height: 120.0, 28 | ), 29 | )), 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/ui/components/components.dart: -------------------------------------------------------------------------------- 1 | export 'form_input_field_with_icon.dart'; 2 | export 'form_input_field.dart'; 3 | export 'form_vertical_spacing.dart'; 4 | export 'primary_button.dart'; 5 | export 'label_button.dart'; 6 | export 'logo_graphic_header.dart'; 7 | export 'dropdown_picker.dart'; 8 | export 'dropdown_picker_with_icon.dart'; 9 | export 'avatar.dart'; 10 | export 'loading.dart'; 11 | -------------------------------------------------------------------------------- /lib/ui/components/dropdown_picker.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | /* 3 | DropdownPicker( 4 | menuOptions: list of dropdown options in key value pairs, 5 | selectedOption: menu option string value, 6 | onChanged: (value) => print('changed'), 7 | ), 8 | */ 9 | 10 | class DropdownPicker extends StatelessWidget { 11 | DropdownPicker( 12 | {required this.menuOptions, 13 | required this.selectedOption, 14 | required this.onChanged}); 15 | 16 | final List menuOptions; 17 | final String selectedOption; 18 | final void Function(String?) onChanged; 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return DropdownButton( 23 | items: menuOptions 24 | .map((data) => DropdownMenuItem( 25 | child: Text( 26 | data.value, 27 | ), 28 | value: data.key, 29 | )) 30 | .toList(), 31 | value: selectedOption, 32 | onChanged: onChanged); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/ui/components/dropdown_picker_with_icon.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /* 4 | DropdownPickerWithIcon( 5 | menuOptions: list of dropdown options in key value pairs, 6 | selectedOption: menu option string value, 7 | onChanged: (value) => print('changed'), 8 | ), 9 | */ 10 | class DropdownPickerWithIcon extends StatelessWidget { 11 | DropdownPickerWithIcon( 12 | {required this.menuOptions, 13 | required this.selectedOption, 14 | this.onChanged}); 15 | 16 | final List menuOptions; 17 | final String selectedOption; 18 | final void Function(String?)? onChanged; 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | //if (Platform.isIOS) {} 23 | return DropdownButton( 24 | items: menuOptions 25 | .map((data) => DropdownMenuItem( 26 | child: Row( 27 | mainAxisAlignment: MainAxisAlignment.start, 28 | children: [ 29 | Icon(data.icon), 30 | SizedBox(width: 10), 31 | Text( 32 | data.value, 33 | ), 34 | ], 35 | ), 36 | value: data.key, 37 | )) 38 | .toList(), 39 | value: selectedOption, 40 | onChanged: onChanged); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/ui/components/form_input_field.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /* 4 | FormInputField( 5 | controller: _url, 6 | labelText: 'Post URL', 7 | validator: Validator.notEmpty, 8 | keyboardType: TextInputType.multiline, 9 | minLines: 3, 10 | onChanged: (value) => print('changed'), 11 | onSaved: (value) => print('implement me'), 12 | ), 13 | */ 14 | 15 | class FormInputField extends StatelessWidget { 16 | FormInputField( 17 | {required this.controller, 18 | required this.labelText, 19 | required this.validator, 20 | this.keyboardType = TextInputType.text, 21 | this.obscureText = false, 22 | this.minLines = 1, 23 | required this.onChanged, 24 | required this.onSaved}); 25 | 26 | final TextEditingController controller; 27 | final String labelText; 28 | final String? Function(String?)? validator; 29 | final TextInputType keyboardType; 30 | final bool obscureText; 31 | final int minLines; 32 | final void Function(String) onChanged; 33 | final void Function(String?)? onSaved; 34 | 35 | @override 36 | Widget build(BuildContext context) { 37 | return TextFormField( 38 | decoration: InputDecoration( 39 | border: OutlineInputBorder( 40 | borderRadius: BorderRadius.all( 41 | Radius.circular(8.0), 42 | )), 43 | enabledBorder: OutlineInputBorder( 44 | borderSide: BorderSide(color: Colors.black45, width: 1.0), 45 | borderRadius: BorderRadius.all(Radius.circular(8.0)), 46 | ), 47 | focusedBorder: OutlineInputBorder( 48 | borderSide: BorderSide( 49 | color: /*Palette.focusedinputBorderColor*/ Colors.black45, 50 | width: 1.0), 51 | borderRadius: BorderRadius.all(Radius.circular(8.0)), 52 | ), 53 | filled: true, 54 | fillColor: Colors.black45, //Palette.inputFillColor, 55 | labelText: labelText, 56 | ), 57 | controller: controller, 58 | onSaved: onSaved, 59 | onChanged: onChanged, 60 | keyboardType: keyboardType, 61 | obscureText: obscureText, 62 | maxLines: null, 63 | minLines: minLines, 64 | validator: validator, 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/ui/components/form_input_field_with_icon.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | /* 3 | FormInputFieldWithIcon( 4 | controller: _email, 5 | iconPrefix: Icons.link, 6 | labelText: 'Post URL', 7 | validator: Validator.notEmpty, 8 | keyboardType: TextInputType.multiline, 9 | minLines: 3, 10 | onChanged: (value) => print('changed'), 11 | onSaved: (value) => print('implement me'), 12 | ), 13 | */ 14 | 15 | class FormInputFieldWithIcon extends StatelessWidget { 16 | FormInputFieldWithIcon( 17 | {required this.controller, 18 | required this.iconPrefix, 19 | required this.labelText, 20 | required this.validator, 21 | this.keyboardType = TextInputType.text, 22 | this.obscureText = false, 23 | this.minLines = 1, 24 | this.maxLines, 25 | required this.onChanged, 26 | required this.onSaved}); 27 | 28 | final TextEditingController controller; 29 | final IconData iconPrefix; 30 | final String labelText; 31 | final String? Function(String?)? validator; 32 | final TextInputType keyboardType; 33 | final bool obscureText; 34 | final int minLines; 35 | final int? maxLines; 36 | final void Function(String) onChanged; 37 | final void Function(String?)? onSaved; 38 | 39 | @override 40 | Widget build(BuildContext context) { 41 | return TextFormField( 42 | decoration: InputDecoration( 43 | filled: true, 44 | prefixIcon: Icon(iconPrefix), 45 | labelText: labelText, 46 | ), 47 | controller: controller, 48 | onSaved: onSaved, 49 | onChanged: onChanged, 50 | keyboardType: keyboardType, 51 | obscureText: obscureText, 52 | maxLines: maxLines, 53 | minLines: minLines, 54 | validator: validator, 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/ui/components/form_vertical_spacing.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class FormVerticalSpace extends SizedBox { 4 | FormVerticalSpace({double height = 24.0}) : super(height: height); 5 | } 6 | -------------------------------------------------------------------------------- /lib/ui/components/label_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | /* 3 | LabelButton( 4 | labelText: 'Some Text', 5 | onPressed: () => print('implement me'), 6 | ), 7 | */ 8 | 9 | class LabelButton extends StatelessWidget { 10 | LabelButton({required this.labelText, required this.onPressed}); 11 | final String labelText; 12 | final void Function() onPressed; 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return TextButton( 17 | child: Text( 18 | labelText, 19 | ), 20 | onPressed: onPressed, 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/ui/components/loading.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | 4 | //loading indicator code is a modified and simplified version of this code 5 | //https://github.com/fayaz07/ots 6 | 7 | final _tKey = GlobalKey(debugLabel: 'overlay_parent'); 8 | 9 | /// Updates with the latest [OverlayEntry] child 10 | late OverlayEntry _loaderEntry; 11 | 12 | /// is dark theme 13 | bool isDarkTheme = false; 14 | 15 | /// To keep track if the [Overlay] is shown 16 | bool _loaderShown = false; 17 | 18 | class Loading extends StatelessWidget { 19 | final Widget? child; 20 | final bool darkTheme; 21 | 22 | const Loading({Key? key, this.child, this.darkTheme = false}) 23 | : super(key: key); 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | isDarkTheme = darkTheme; 28 | return SizedBox( 29 | key: _tKey, 30 | child: child, 31 | ); 32 | } 33 | } 34 | 35 | OverlayState? get _overlayState { 36 | final context = _tKey.currentContext; 37 | if (context == null) return null; 38 | 39 | NavigatorState? navigator; 40 | 41 | void visitor(Element element) { 42 | if (navigator != null) return; 43 | 44 | if (element.widget is Navigator) { 45 | navigator = (element as StatefulElement).state as NavigatorState; 46 | } else { 47 | element.visitChildElements(visitor); 48 | } 49 | } 50 | 51 | // ignore: unused_element 52 | context.visitChildElements(visitor); 53 | 54 | assert(navigator != null, '''unable to show overlay'''); 55 | return navigator!.overlay; 56 | } 57 | 58 | /// To handle a loader for the application 59 | Future showLoadingIndicator( 60 | {bool isModal = true, Color? modalColor}) async { 61 | try { 62 | debugPrint('Showing loading overlay'); 63 | final _child = Center( 64 | child: SizedBox( 65 | child: CircularProgressIndicator(), 66 | /*(Platform.isAndroid 67 | ? CircularProgressIndicator() 68 | : CupertinoActivityIndicator()),*/ 69 | width: 30, 70 | height: 30, 71 | ), 72 | ); 73 | await _showOverlay( 74 | child: isModal 75 | ? Stack( 76 | children: [ 77 | ModalBarrier( 78 | color: modalColor, 79 | ), 80 | _child 81 | ], 82 | ) 83 | : _child, 84 | ); 85 | } catch (err) { 86 | debugPrint('Exception showing loading overlay\n${err.toString()}'); 87 | throw err; 88 | } 89 | } 90 | 91 | Future hideLoadingIndicator() async { 92 | try { 93 | debugPrint('Hiding loading overlay'); 94 | await _hideOverlay(); 95 | } catch (err) { 96 | debugPrint('Exception hiding loading overlay'); 97 | throw err; 98 | } 99 | } 100 | 101 | ///---------------------------------------------------------------------------- 102 | /// These methods deal with showing and hiding the overlay 103 | Future _showOverlay({required Widget child}) async { 104 | try { 105 | final overlay = _overlayState; 106 | 107 | if (_loaderShown) { 108 | debugPrint('An overlay is already showing'); 109 | return Future.value(false); 110 | } 111 | 112 | final overlayEntry = OverlayEntry( 113 | builder: (context) => child, 114 | ); 115 | 116 | overlay?.insert(overlayEntry); 117 | _loaderEntry = overlayEntry; 118 | _loaderShown = true; 119 | } catch (err) { 120 | debugPrint('Exception inserting loading overlay\n${err.toString()}'); 121 | throw err; 122 | } 123 | } 124 | 125 | Future _hideOverlay() async { 126 | try { 127 | _loaderEntry.remove(); 128 | _loaderShown = false; 129 | } catch (err) { 130 | debugPrint('Exception removing loading overlay\n${err.toString()}'); 131 | throw err; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /lib/ui/components/logo_graphic_header.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_starter/controllers/controllers.dart'; 3 | 4 | class LogoGraphicHeader extends StatelessWidget { 5 | LogoGraphicHeader(); 6 | final ThemeController themeController = ThemeController.to; 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | String _imageLogo = 'assets/images/default.png'; 11 | if (themeController.isDarkModeOn == true) { 12 | _imageLogo = 'assets/images/defaultDark.png'; 13 | } 14 | return Hero( 15 | tag: 'App Logo', 16 | child: CircleAvatar( 17 | foregroundColor: Colors.blue, 18 | backgroundColor: Colors.transparent, 19 | radius: 60.0, 20 | child: ClipOval( 21 | child: Image.asset( 22 | _imageLogo, 23 | fit: BoxFit.cover, 24 | width: 120.0, 25 | height: 120.0, 26 | ), 27 | )), 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/ui/components/primary_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /* 4 | PrimaryButton( 5 | labelText: 'UPDATE', 6 | onPressed: () => print('Submit'), 7 | ), 8 | */ 9 | 10 | class PrimaryButton extends StatelessWidget { 11 | PrimaryButton({required this.labelText, required this.onPressed}); 12 | 13 | final String labelText; 14 | final void Function() onPressed; 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return ElevatedButton( 19 | onPressed: onPressed, 20 | child: Text( 21 | labelText.toUpperCase(), 22 | style: TextStyle(fontWeight: FontWeight.bold), 23 | ), 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/ui/components/segmented_selector.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | //import 'package:flutter_starter/ui/components/sliding_segmented_control.dart'; 4 | /* 5 | SegmentedSelector( 6 | menuOptions: list of dropdown options in key value pairs, 7 | selectedOption: menu option string value, 8 | onChanged: (value) => print('changed'), 9 | ), 10 | */ 11 | 12 | class SegmentedSelector extends StatelessWidget { 13 | SegmentedSelector( 14 | {required this.menuOptions, 15 | required this.selectedOption, 16 | required this.onValueChanged}); 17 | 18 | final List menuOptions; 19 | final String selectedOption; 20 | final void Function(dynamic) onValueChanged; 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | //if (Platform.isIOS) {} 25 | 26 | return CupertinoSlidingSegmentedControl( 27 | //thumbColor: Theme.of(context).primaryColor, 28 | groupValue: selectedOption, 29 | children: Map.fromIterable( 30 | menuOptions, 31 | key: (option) => option.key, 32 | value: (option) => Row( 33 | mainAxisAlignment: MainAxisAlignment.center, 34 | children: [ 35 | Icon(option.icon), 36 | SizedBox(width: 6), 37 | Text(option.value), 38 | ], 39 | ), 40 | ), 41 | onValueChanged: onValueChanged); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/ui/home_ui.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_starter/controllers/controllers.dart'; 3 | import 'package:flutter_starter/ui/components/components.dart'; 4 | import 'package:flutter_starter/ui/ui.dart'; 5 | import 'package:get/get.dart'; 6 | 7 | class HomeUI extends StatelessWidget { 8 | @override 9 | Widget build(BuildContext context) { 10 | return GetBuilder( 11 | init: AuthController(), 12 | builder: (controller) => controller.firestoreUser.value!.uid == null 13 | ? Center( 14 | child: CircularProgressIndicator(), 15 | ) 16 | : Scaffold( 17 | appBar: AppBar( 18 | title: Text('home.title'.tr), 19 | actions: [ 20 | IconButton( 21 | icon: Icon(Icons.settings), 22 | onPressed: () { 23 | Get.to(SettingsUI()); 24 | }), 25 | ], 26 | ), 27 | body: Center( 28 | child: Column( 29 | children: [ 30 | SizedBox(height: 120), 31 | Avatar(controller.firestoreUser.value!), 32 | Column( 33 | mainAxisAlignment: MainAxisAlignment.start, 34 | crossAxisAlignment: CrossAxisAlignment.start, 35 | children: [ 36 | FormVerticalSpace(), 37 | Text( 38 | 'home.uidLabel'.tr + 39 | ': ' + 40 | controller.firestoreUser.value!.uid, 41 | style: TextStyle(fontSize: 16)), 42 | FormVerticalSpace(), 43 | Text( 44 | 'home.nameLabel'.tr + 45 | ': ' + 46 | controller.firestoreUser.value!.name, 47 | style: TextStyle(fontSize: 16)), 48 | FormVerticalSpace(), 49 | Text( 50 | 'home.emailLabel'.tr + 51 | ': ' + 52 | controller.firestoreUser.value!.email, 53 | style: TextStyle(fontSize: 16)), 54 | FormVerticalSpace(), 55 | Text( 56 | 'home.adminUserLabel'.tr + 57 | ': ' + 58 | controller.admin.value.toString(), 59 | style: TextStyle(fontSize: 16)), 60 | ], 61 | ), 62 | ], 63 | ), 64 | ), 65 | ), 66 | ); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/ui/settings_ui.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter_starter/ui/auth/auth.dart'; 4 | import 'package:get/get.dart'; 5 | import 'package:flutter_starter/ui/components/segmented_selector.dart'; 6 | import 'package:flutter_starter/controllers/controllers.dart'; 7 | import 'package:flutter_starter/ui/components/components.dart'; 8 | import 'package:flutter_starter/models/models.dart'; 9 | import 'package:flutter_starter/constants/constants.dart'; 10 | 11 | class SettingsUI extends StatelessWidget { 12 | //final LanguageController languageController = LanguageController.to; 13 | //final ThemeController themeController = ThemeController.to; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Scaffold( 18 | appBar: AppBar( 19 | title: Text('settings.title'.tr), 20 | ), 21 | body: _buildLayoutSection(context), 22 | ); 23 | } 24 | 25 | Widget _buildLayoutSection(BuildContext context) { 26 | return ListView( 27 | children: [ 28 | languageListTile(context), 29 | themeListTile(context), 30 | ListTile( 31 | title: Text('settings.updateProfile'.tr), 32 | trailing: ElevatedButton( 33 | onPressed: () async { 34 | Get.to(UpdateProfileUI()); 35 | }, 36 | child: Text( 37 | 'settings.updateProfile'.tr, 38 | ), 39 | )), 40 | ListTile( 41 | title: Text('settings.signOut'.tr), 42 | trailing: ElevatedButton( 43 | onPressed: () { 44 | AuthController.to.signOut(); 45 | }, 46 | child: Text( 47 | 'settings.signOut'.tr, 48 | ), 49 | ), 50 | ) 51 | ], 52 | ); 53 | } 54 | 55 | languageListTile(BuildContext context) { 56 | return GetBuilder( 57 | builder: (controller) => ListTile( 58 | title: Text('settings.language'.tr), 59 | trailing: DropdownPicker( 60 | menuOptions: Globals.languageOptions, 61 | selectedOption: controller.currentLanguage, 62 | onChanged: (value) async { 63 | await controller.updateLanguage(value!); 64 | Get.forceAppUpdate(); 65 | }, 66 | ), 67 | ), 68 | ); 69 | } 70 | 71 | themeListTile(BuildContext context) { 72 | final List themeOptions = [ 73 | MenuOptionsModel( 74 | key: "system", value: 'settings.system'.tr, icon: Icons.brightness_4), 75 | MenuOptionsModel( 76 | key: "light", value: 'settings.light'.tr, icon: Icons.brightness_low), 77 | MenuOptionsModel( 78 | key: "dark", value: 'settings.dark'.tr, icon: Icons.brightness_3) 79 | ]; 80 | return GetBuilder( 81 | builder: (controller) => ListTile( 82 | title: Text('settings.theme'.tr), 83 | trailing: SegmentedSelector( 84 | selectedOption: controller.currentTheme, 85 | menuOptions: themeOptions, 86 | onValueChanged: (value) { 87 | controller.setThemeMode(value); 88 | }, 89 | ), 90 | ), 91 | ); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lib/ui/splash_ui.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class SplashUI extends StatelessWidget { 4 | @override 5 | Widget build(BuildContext context) { 6 | return Scaffold( 7 | body: Container( 8 | child: Center( 9 | child: CircularProgressIndicator(), 10 | ), 11 | ), 12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/ui/ui.dart: -------------------------------------------------------------------------------- 1 | export 'home_ui.dart'; 2 | export 'settings_ui.dart'; 3 | export 'splash_ui.dart'; 4 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.9.0" 11 | characters: 12 | dependency: transitive 13 | description: 14 | name: characters 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "1.2.0" 18 | charcode: 19 | dependency: transitive 20 | description: 21 | name: charcode 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.2.0" 25 | clock: 26 | dependency: transitive 27 | description: 28 | name: clock 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.1.0" 32 | cloud_firestore: 33 | dependency: "direct main" 34 | description: 35 | name: cloud_firestore 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.0.4" 39 | cloud_firestore_platform_interface: 40 | dependency: transitive 41 | description: 42 | name: cloud_firestore_platform_interface 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "4.0.1" 46 | cloud_firestore_web: 47 | dependency: transitive 48 | description: 49 | name: cloud_firestore_web 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.0.4" 53 | collection: 54 | dependency: transitive 55 | description: 56 | name: collection 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.16.0" 60 | crypto: 61 | dependency: "direct main" 62 | description: 63 | name: crypto 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "3.0.1" 67 | csv: 68 | dependency: "direct main" 69 | description: 70 | name: csv 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "5.0.0" 74 | cupertino_icons: 75 | dependency: "direct main" 76 | description: 77 | name: cupertino_icons 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "0.1.3" 81 | ffi: 82 | dependency: transitive 83 | description: 84 | name: ffi 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "1.0.0" 88 | file: 89 | dependency: transitive 90 | description: 91 | name: file 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "6.0.0" 95 | firebase_auth: 96 | dependency: "direct main" 97 | description: 98 | name: firebase_auth 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "1.0.2" 102 | firebase_auth_platform_interface: 103 | dependency: transitive 104 | description: 105 | name: firebase_auth_platform_interface 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "4.0.1" 109 | firebase_auth_web: 110 | dependency: transitive 111 | description: 112 | name: firebase_auth_web 113 | url: "https://pub.dartlang.org" 114 | source: hosted 115 | version: "1.0.4" 116 | firebase_core: 117 | dependency: "direct main" 118 | description: 119 | name: firebase_core 120 | url: "https://pub.dartlang.org" 121 | source: hosted 122 | version: "1.0.3" 123 | firebase_core_platform_interface: 124 | dependency: transitive 125 | description: 126 | name: firebase_core_platform_interface 127 | url: "https://pub.dartlang.org" 128 | source: hosted 129 | version: "4.0.0" 130 | firebase_core_web: 131 | dependency: transitive 132 | description: 133 | name: firebase_core_web 134 | url: "https://pub.dartlang.org" 135 | source: hosted 136 | version: "1.0.2" 137 | flutter: 138 | dependency: "direct main" 139 | description: flutter 140 | source: sdk 141 | version: "0.0.0" 142 | flutter_localizations: 143 | dependency: "direct main" 144 | description: flutter 145 | source: sdk 146 | version: "0.0.0" 147 | flutter_web_plugins: 148 | dependency: transitive 149 | description: flutter 150 | source: sdk 151 | version: "0.0.0" 152 | get: 153 | dependency: "direct main" 154 | description: 155 | name: get 156 | url: "https://pub.dartlang.org" 157 | source: hosted 158 | version: "4.1.3" 159 | get_storage: 160 | dependency: "direct main" 161 | description: 162 | name: get_storage 163 | url: "https://pub.dartlang.org" 164 | source: hosted 165 | version: "2.0.2" 166 | http: 167 | dependency: "direct main" 168 | description: 169 | name: http 170 | url: "https://pub.dartlang.org" 171 | source: hosted 172 | version: "0.13.5" 173 | http_parser: 174 | dependency: transitive 175 | description: 176 | name: http_parser 177 | url: "https://pub.dartlang.org" 178 | source: hosted 179 | version: "4.0.0" 180 | intl: 181 | dependency: transitive 182 | description: 183 | name: intl 184 | url: "https://pub.dartlang.org" 185 | source: hosted 186 | version: "0.17.0" 187 | js: 188 | dependency: transitive 189 | description: 190 | name: js 191 | url: "https://pub.dartlang.org" 192 | source: hosted 193 | version: "0.6.4" 194 | material_color_utilities: 195 | dependency: transitive 196 | description: 197 | name: material_color_utilities 198 | url: "https://pub.dartlang.org" 199 | source: hosted 200 | version: "0.1.4" 201 | meta: 202 | dependency: transitive 203 | description: 204 | name: meta 205 | url: "https://pub.dartlang.org" 206 | source: hosted 207 | version: "1.7.0" 208 | path: 209 | dependency: transitive 210 | description: 211 | name: path 212 | url: "https://pub.dartlang.org" 213 | source: hosted 214 | version: "1.8.1" 215 | path_provider: 216 | dependency: transitive 217 | description: 218 | name: path_provider 219 | url: "https://pub.dartlang.org" 220 | source: hosted 221 | version: "2.0.1" 222 | path_provider_linux: 223 | dependency: transitive 224 | description: 225 | name: path_provider_linux 226 | url: "https://pub.dartlang.org" 227 | source: hosted 228 | version: "2.0.0" 229 | path_provider_macos: 230 | dependency: transitive 231 | description: 232 | name: path_provider_macos 233 | url: "https://pub.dartlang.org" 234 | source: hosted 235 | version: "2.0.0" 236 | path_provider_platform_interface: 237 | dependency: transitive 238 | description: 239 | name: path_provider_platform_interface 240 | url: "https://pub.dartlang.org" 241 | source: hosted 242 | version: "2.0.1" 243 | path_provider_windows: 244 | dependency: transitive 245 | description: 246 | name: path_provider_windows 247 | url: "https://pub.dartlang.org" 248 | source: hosted 249 | version: "2.0.0" 250 | platform: 251 | dependency: transitive 252 | description: 253 | name: platform 254 | url: "https://pub.dartlang.org" 255 | source: hosted 256 | version: "3.0.0" 257 | plugin_platform_interface: 258 | dependency: transitive 259 | description: 260 | name: plugin_platform_interface 261 | url: "https://pub.dartlang.org" 262 | source: hosted 263 | version: "2.0.0" 264 | process: 265 | dependency: transitive 266 | description: 267 | name: process 268 | url: "https://pub.dartlang.org" 269 | source: hosted 270 | version: "4.0.0" 271 | sky_engine: 272 | dependency: transitive 273 | description: flutter 274 | source: sdk 275 | version: "0.0.99" 276 | source_span: 277 | dependency: transitive 278 | description: 279 | name: source_span 280 | url: "https://pub.dartlang.org" 281 | source: hosted 282 | version: "1.8.1" 283 | string_scanner: 284 | dependency: transitive 285 | description: 286 | name: string_scanner 287 | url: "https://pub.dartlang.org" 288 | source: hosted 289 | version: "1.1.0" 290 | term_glyph: 291 | dependency: transitive 292 | description: 293 | name: term_glyph 294 | url: "https://pub.dartlang.org" 295 | source: hosted 296 | version: "1.2.0" 297 | typed_data: 298 | dependency: transitive 299 | description: 300 | name: typed_data 301 | url: "https://pub.dartlang.org" 302 | source: hosted 303 | version: "1.3.0" 304 | vector_math: 305 | dependency: transitive 306 | description: 307 | name: vector_math 308 | url: "https://pub.dartlang.org" 309 | source: hosted 310 | version: "2.1.2" 311 | win32: 312 | dependency: transitive 313 | description: 314 | name: win32 315 | url: "https://pub.dartlang.org" 316 | source: hosted 317 | version: "2.0.5" 318 | xdg_directories: 319 | dependency: transitive 320 | description: 321 | name: xdg_directories 322 | url: "https://pub.dartlang.org" 323 | source: hosted 324 | version: "0.2.0" 325 | sdks: 326 | dart: ">=2.17.0-0 <3.0.0" 327 | flutter: ">=1.20.0" 328 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_starter 2 | description: A new Flutter project. 3 | 4 | version: 2.0.0 5 | 6 | environment: 7 | sdk: '>=2.12.0 <3.0.0' 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | flutter_localizations: 13 | sdk: flutter 14 | cupertino_icons: 15 | firebase_core: ^1.0.3 16 | # firebase_analytics: ^7.1.1 no nullsafe... skip it for now 17 | firebase_auth: ^1.0.2 18 | cloud_firestore: ^1.0.4 19 | 20 | # google_sign_in: 21 | # apple_sign_in: 22 | get: ^4.1.3 23 | get_storage: ^2.0.2 24 | 25 | crypto: ^3.0.1 26 | http: ^0.13.5 27 | csv: ^5.0.0 28 | 29 | dependency_overrides: 30 | # analyzer: 1.4.0 31 | # intl: 0.17.0 32 | 33 | dev_dependencies: 34 | 35 | flutter: 36 | uses-material-design: true 37 | assets: 38 | - assets/images/default.png 39 | - assets/images/defaultDark.png 40 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/delay/flutter_starter/4d3077eb4efbc850b93d69a28f461e761b0bf49a/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | flutter_starter 18 | 19 | 20 | 21 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 46 | 49 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flutter_starter", 3 | "short_name": "flutter_starter", 4 | "start_url": ".", 5 | "display": "minimal-ui", 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 | --------------------------------------------------------------------------------