├── .gitignore ├── .metadata ├── .vscode └── launch.json ├── README.md ├── android ├── .gitignore ├── app │ ├── build.gradle │ ├── google-services.json │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── fireflutter_sample_app │ │ │ │ └── MainActivity.kt │ │ └── 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 │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── 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 ├── keystore.key ├── lib ├── global_variables.dart ├── keys.dart ├── main.dart ├── screens │ ├── admin │ │ ├── admin.category.screen.dart │ │ └── admin.screen.dart │ ├── forum │ │ ├── comment.dart │ │ ├── comment.form.dart │ │ ├── display_photos.dart │ │ ├── edit_photos.dart │ │ ├── photo_upload.dart │ │ ├── post.edit.screen.dart │ │ └── post.list.screen.dart │ ├── home │ │ └── home.screen.dart │ ├── login │ │ └── login.screen.dart │ ├── phone_auth │ │ ├── phone_auth.screen.dart │ │ └── phone_auth_verification_code.screen.dart │ ├── profile │ │ └── profile.screen.dart │ ├── push-notification │ │ └── push-notification.screen.dart │ ├── register │ │ └── register.screen.dart │ ├── search │ │ └── search.screen.dart │ └── settings │ │ └── settings.screen.dart └── translations.dart ├── pubspec.lock ├── pubspec.yaml ├── test └── widget_test.dart └── test_driver ├── app.dart ├── app_test.dart └── test_runner.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | packages/ 3 | # Miscellaneous 4 | *.class 5 | *.log 6 | *.pyc 7 | *.swp 8 | .DS_Store 9 | .atom/ 10 | .buildlog/ 11 | .history 12 | .svn/ 13 | 14 | # IntelliJ related 15 | *.iml 16 | *.ipr 17 | *.iws 18 | .idea/ 19 | 20 | # The .vscode folder contains launch configuration and tasks you configure in 21 | # VS Code which you may wish to be included in version control, so this line 22 | # is commented out by default. 23 | #.vscode/ 24 | 25 | # Flutter/Dart/Pub related 26 | **/doc/api/ 27 | **/ios/Flutter/.last_build_id 28 | .dart_tool/ 29 | .flutter-plugins 30 | .flutter-plugins-dependencies 31 | .packages 32 | .pub-cache/ 33 | .pub/ 34 | /build/ 35 | 36 | # Web related 37 | lib/generated_plugin_registrant.dart 38 | 39 | # Symbolication related 40 | app.*.symbols 41 | 42 | # Obfuscation related 43 | app.*.map.json 44 | -------------------------------------------------------------------------------- /.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: 8874f21e79d7ec66d0457c7ab338348e31b17f1d 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | 8 | { 9 | "name": "Current Device", 10 | "program": "lib/main.dart", 11 | "request": "launch", 12 | "type": "dart" 13 | }, 14 | { 15 | "name": "Pixel 3a - Android", 16 | "request": "launch", 17 | "type": "dart", 18 | "deviceId": "emulator-5554" 19 | }, 20 | { 21 | "name": "Simulator - iPhone 11 Pro - 14.0", 22 | "request": "launch", 23 | "type": "dart", 24 | "deviceId": "DD183AE3-66CE-4282-A2BA-ADEBEA771AA8" 25 | }, 26 | { 27 | "name": "iPhone 11", 28 | "request": "launch", 29 | "type": "dart", 30 | "deviceId": "00008030-000904C80290802E" 31 | }, 32 | { 33 | "name": "iPhone 11 Pro Max", 34 | "request": "launch", 35 | "type": "dart", 36 | "deviceId": "B33AA4A3-1FBA-4FC1-AC49-26465C64EB78" 37 | }, 38 | { 39 | "name": "App Instrument Emulator", 40 | "program": "test_driver/app.dart", 41 | "request": "launch", 42 | "type": "dart", 43 | "args": [ 44 | "--observatory-port", 45 | "12345", 46 | "--disable-service-auth-codes" 47 | ], 48 | "deviceId": "emulator-5554" 49 | }, 50 | { 51 | "name": "App Instrument Simulator", 52 | "program": "test_driver/app.dart", 53 | "request": "launch", 54 | "type": "dart", 55 | "args": [ 56 | "--observatory-port", 57 | "12345", 58 | "--disable-service-auth-codes" 59 | ], 60 | "deviceId": "004CE606-8161-441D-8AB7-7FA5DEC5DAF0" 61 | } 62 | ], 63 | "compounds": [ 64 | { 65 | "name": "All Devices", 66 | "configurations": [ 67 | "Pixel 3a - Android", 68 | "Simulator - iPhone 11 Pro - 14.0", 69 | "iPhone 11" 70 | ], 71 | } 72 | ] 73 | } 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FireFlutter Sample App 2 | 3 | This is a sample app to demonstrate how to use [FireFlutter](https://pub.dev/packages/fireflutter) package. 4 | 5 | ## Branches 6 | 7 | - You may refer the branches in the following order to see gradual update of the sample app. 8 | - install-fireflutter. Add fireflutter to Flutter project. 9 | - install-getx. Add GetX package to Flutter project. 10 | - routes. Page routes 11 | - register. User registration. 12 | - register-v2. add more code on register page 13 | - social-login. social login. Google, Apple, Facebook. 14 | - login-with-email-password. 15 | - translations. text translation. 16 | - admin-page. adding admin page. 17 | - category-admin. adding category management page and do the code. For using admin page, yo need to login as admin. Read how to set admin in fireflutter README.md if you don't know. 18 | - post-edit. post create without post update 19 | - photo-upload. file upload code without file delete 20 | - forum-list 21 | - Listing posts of a forum cateogry. 22 | - Post create button 23 | - Inifinite scrolling 24 | - Displaying 'no posts' message if there is no post created under a forum. 25 | - Displaying 'no more posts' message if there is no more posts to fetch. 26 | - forum-list-with-photo 27 | - post-delete. 28 | - comment-crud. 29 | - vote. 30 | - phone-verification 31 | - algolia 32 | - settings 33 | - language-settings 34 | - integration-test. The sample app is following the standard Flutter integration test. You can run and test exactly the same way of Flutter integration test. 35 | - vote-settings. To show or hide like/dislike buttons on each forum. You can test it editing the settings on Firestore. 36 | - forum-subscription 37 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 29 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 41 | applicationId "com.sonub.fireflutter" 42 | minSdkVersion 21 43 | targetSdkVersion 29 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | } 47 | 48 | buildTypes { 49 | release { 50 | // TODO: Add your own signing config for the release build. 51 | // Signing with the debug keys for now, so `flutter run --release` works. 52 | signingConfig signingConfigs.debug 53 | } 54 | } 55 | } 56 | 57 | flutter { 58 | source '../..' 59 | } 60 | 61 | dependencies { 62 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 63 | } 64 | 65 | apply plugin: 'com.google.gms.google-services' -------------------------------------------------------------------------------- /android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "389649476574", 4 | "firebase_url": "https://fireflutter-test.firebaseio.com", 5 | "project_id": "fireflutter-test", 6 | "storage_bucket": "fireflutter-test.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:389649476574:android:325835405d373f0f56cb4f", 12 | "android_client_info": { 13 | "package_name": "com.sonub.fireflutter" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "389649476574-efs1rlrnttfvmumpobtitbie4omcqd84.apps.googleusercontent.com", 19 | "client_type": 3 20 | } 21 | ], 22 | "api_key": [ 23 | { 24 | "current_key": "AIzaSyB4XDiZZinEC4MpvrZ5YKNxw4OHS1f-vVU" 25 | } 26 | ], 27 | "services": { 28 | "appinvite_service": { 29 | "other_platform_oauth_client": [ 30 | { 31 | "client_id": "389649476574-efs1rlrnttfvmumpobtitbie4omcqd84.apps.googleusercontent.com", 32 | "client_type": 3 33 | }, 34 | { 35 | "client_id": "389649476574-3pp2tstq9l5k8ecotn228v6b0gl905vp.apps.googleusercontent.com", 36 | "client_type": 2, 37 | "ios_info": { 38 | "bundle_id": "com.sonub.fireflutter" 39 | } 40 | } 41 | ] 42 | } 43 | } 44 | } 45 | ], 46 | "configuration_version": "1" 47 | } -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 13 | 14 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 32 | 33 | 34 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/fireflutter_sample_app/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.sonub.fireflutter 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /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/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SMS APP 3 | 2760886707561630 4 | fb2760886707561630 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /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.useAndroidX=true 3 | android.enableJetifier=true 4 | android.enableR8=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 localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /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/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 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /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 | - AppAuth (1.4.0): 216 | - AppAuth/Core (= 1.4.0) 217 | - AppAuth/ExternalUserAgent (= 1.4.0) 218 | - AppAuth/Core (1.4.0) 219 | - AppAuth/ExternalUserAgent (1.4.0) 220 | - BoringSSL-GRPC (0.0.7): 221 | - BoringSSL-GRPC/Implementation (= 0.0.7) 222 | - BoringSSL-GRPC/Interface (= 0.0.7) 223 | - BoringSSL-GRPC/Implementation (0.0.7): 224 | - BoringSSL-GRPC/Interface (= 0.0.7) 225 | - BoringSSL-GRPC/Interface (0.0.7) 226 | - cloud_firestore (0.14.1-3): 227 | - Firebase/CoreOnly (~> 6.26.0) 228 | - Firebase/Firestore (~> 6.26.0) 229 | - firebase_core 230 | - Flutter 231 | - FBSDKCoreKit (7.1.1): 232 | - FBSDKCoreKit/Basics (= 7.1.1) 233 | - FBSDKCoreKit/Core (= 7.1.1) 234 | - FBSDKCoreKit/Basics (7.1.1) 235 | - FBSDKCoreKit/Core (7.1.1): 236 | - FBSDKCoreKit/Basics 237 | - FBSDKLoginKit (7.1.1): 238 | - FBSDKLoginKit/Login (= 7.1.1) 239 | - FBSDKLoginKit/Login (7.1.1): 240 | - FBSDKCoreKit (~> 7.1.1) 241 | - Firebase/Auth (6.26.0): 242 | - Firebase/CoreOnly 243 | - FirebaseAuth (~> 6.5.3) 244 | - Firebase/CoreOnly (6.26.0): 245 | - FirebaseCore (= 6.7.2) 246 | - Firebase/Firestore (6.26.0): 247 | - Firebase/CoreOnly 248 | - FirebaseFirestore (~> 1.15.0) 249 | - Firebase/Messaging (6.26.0): 250 | - Firebase/CoreOnly 251 | - FirebaseMessaging (~> 4.4.1) 252 | - Firebase/Storage (6.26.0): 253 | - Firebase/CoreOnly 254 | - FirebaseStorage (~> 3.6.1) 255 | - firebase_auth (0.18.1-2): 256 | - Firebase/Auth (~> 6.26.0) 257 | - Firebase/CoreOnly (~> 6.26.0) 258 | - firebase_core 259 | - Flutter 260 | - firebase_core (0.5.0-1): 261 | - Firebase/CoreOnly (~> 6.26.0) 262 | - Flutter 263 | - firebase_messaging (7.0.3): 264 | - Firebase/CoreOnly (~> 6.26.0) 265 | - Firebase/Messaging (~> 6.26.0) 266 | - firebase_core 267 | - Flutter 268 | - firebase_storage (5.0.0-dev.3): 269 | - Firebase/CoreOnly (~> 6.26.0) 270 | - Firebase/Storage (~> 6.26.0) 271 | - firebase_core 272 | - Flutter 273 | - FirebaseAnalyticsInterop (1.5.0) 274 | - FirebaseAuth (6.5.3): 275 | - FirebaseAuthInterop (~> 1.0) 276 | - FirebaseCore (~> 6.6) 277 | - GoogleUtilities/AppDelegateSwizzler (~> 6.5) 278 | - GoogleUtilities/Environment (~> 6.5) 279 | - GTMSessionFetcher/Core (~> 1.1) 280 | - FirebaseAuthInterop (1.1.0) 281 | - FirebaseCore (6.7.2): 282 | - FirebaseCoreDiagnostics (~> 1.3) 283 | - FirebaseCoreDiagnosticsInterop (~> 1.2) 284 | - GoogleUtilities/Environment (~> 6.5) 285 | - GoogleUtilities/Logger (~> 6.5) 286 | - FirebaseCoreDiagnostics (1.5.0): 287 | - GoogleDataTransport (~> 7.0) 288 | - GoogleUtilities/Environment (~> 6.7) 289 | - GoogleUtilities/Logger (~> 6.7) 290 | - nanopb (~> 1.30905.0) 291 | - FirebaseCoreDiagnosticsInterop (1.2.0) 292 | - FirebaseFirestore (1.15.0): 293 | - abseil/algorithm (= 0.20200225.0) 294 | - abseil/base (= 0.20200225.0) 295 | - abseil/memory (= 0.20200225.0) 296 | - abseil/meta (= 0.20200225.0) 297 | - abseil/strings/strings (= 0.20200225.0) 298 | - abseil/time (= 0.20200225.0) 299 | - abseil/types (= 0.20200225.0) 300 | - FirebaseAuthInterop (~> 1.0) 301 | - FirebaseCore (~> 6.2) 302 | - "gRPC-C++ (~> 1.28.0)" 303 | - leveldb-library (~> 1.22) 304 | - nanopb (~> 1.30905.0) 305 | - FirebaseInstallations (1.3.0): 306 | - FirebaseCore (~> 6.6) 307 | - GoogleUtilities/Environment (~> 6.6) 308 | - GoogleUtilities/UserDefaults (~> 6.6) 309 | - PromisesObjC (~> 1.2) 310 | - FirebaseInstanceID (4.3.4): 311 | - FirebaseCore (~> 6.6) 312 | - FirebaseInstallations (~> 1.0) 313 | - GoogleUtilities/Environment (~> 6.5) 314 | - GoogleUtilities/UserDefaults (~> 6.5) 315 | - FirebaseMessaging (4.4.1): 316 | - FirebaseAnalyticsInterop (~> 1.5) 317 | - FirebaseCore (~> 6.6) 318 | - FirebaseInstanceID (~> 4.3) 319 | - GoogleUtilities/AppDelegateSwizzler (~> 6.5) 320 | - GoogleUtilities/Environment (~> 6.5) 321 | - GoogleUtilities/Reachability (~> 6.5) 322 | - GoogleUtilities/UserDefaults (~> 6.5) 323 | - Protobuf (>= 3.9.2, ~> 3.9) 324 | - FirebaseStorage (3.6.1): 325 | - FirebaseAuthInterop (~> 1.1) 326 | - FirebaseCore (~> 6.6) 327 | - GTMSessionFetcher/Core (~> 1.1) 328 | - Flutter (1.0.0) 329 | - flutter_facebook_auth (0.3.1): 330 | - FBSDKCoreKit (~> 7.1.0) 331 | - FBSDKLoginKit (~> 7.1.0) 332 | - Flutter 333 | - flutter_image_compress (0.0.1): 334 | - Flutter 335 | - Mantle 336 | - SDWebImageWebPCoder 337 | - google_sign_in (0.0.1): 338 | - Flutter 339 | - GoogleSignIn (~> 5.0) 340 | - GoogleDataTransport (7.2.0): 341 | - nanopb (~> 1.30905.0) 342 | - GoogleSignIn (5.0.2): 343 | - AppAuth (~> 1.2) 344 | - GTMAppAuth (~> 1.0) 345 | - GTMSessionFetcher/Core (~> 1.1) 346 | - GoogleUtilities/AppDelegateSwizzler (6.7.2): 347 | - GoogleUtilities/Environment 348 | - GoogleUtilities/Logger 349 | - GoogleUtilities/Network 350 | - GoogleUtilities/Environment (6.7.2): 351 | - PromisesObjC (~> 1.2) 352 | - GoogleUtilities/Logger (6.7.2): 353 | - GoogleUtilities/Environment 354 | - GoogleUtilities/Network (6.7.2): 355 | - GoogleUtilities/Logger 356 | - "GoogleUtilities/NSData+zlib" 357 | - GoogleUtilities/Reachability 358 | - "GoogleUtilities/NSData+zlib (6.7.2)" 359 | - GoogleUtilities/Reachability (6.7.2): 360 | - GoogleUtilities/Logger 361 | - GoogleUtilities/UserDefaults (6.7.2): 362 | - GoogleUtilities/Logger 363 | - "gRPC-C++ (1.28.2)": 364 | - "gRPC-C++/Implementation (= 1.28.2)" 365 | - "gRPC-C++/Interface (= 1.28.2)" 366 | - "gRPC-C++/Implementation (1.28.2)": 367 | - abseil/container/inlined_vector (= 0.20200225.0) 368 | - abseil/memory/memory (= 0.20200225.0) 369 | - abseil/strings/str_format (= 0.20200225.0) 370 | - abseil/strings/strings (= 0.20200225.0) 371 | - abseil/types/optional (= 0.20200225.0) 372 | - "gRPC-C++/Interface (= 1.28.2)" 373 | - gRPC-Core (= 1.28.2) 374 | - "gRPC-C++/Interface (1.28.2)" 375 | - gRPC-Core (1.28.2): 376 | - gRPC-Core/Implementation (= 1.28.2) 377 | - gRPC-Core/Interface (= 1.28.2) 378 | - gRPC-Core/Implementation (1.28.2): 379 | - abseil/container/inlined_vector (= 0.20200225.0) 380 | - abseil/memory/memory (= 0.20200225.0) 381 | - abseil/strings/str_format (= 0.20200225.0) 382 | - abseil/strings/strings (= 0.20200225.0) 383 | - abseil/types/optional (= 0.20200225.0) 384 | - BoringSSL-GRPC (= 0.0.7) 385 | - gRPC-Core/Interface (= 1.28.2) 386 | - gRPC-Core/Interface (1.28.2) 387 | - GTMAppAuth (1.1.0): 388 | - AppAuth/Core (~> 1.4) 389 | - GTMSessionFetcher (~> 1.4) 390 | - GTMSessionFetcher (1.4.0): 391 | - GTMSessionFetcher/Full (= 1.4.0) 392 | - GTMSessionFetcher/Core (1.4.0) 393 | - GTMSessionFetcher/Full (1.4.0): 394 | - GTMSessionFetcher/Core (= 1.4.0) 395 | - image_picker (0.0.1): 396 | - Flutter 397 | - leveldb-library (1.22) 398 | - libwebp (1.1.0): 399 | - libwebp/demux (= 1.1.0) 400 | - libwebp/mux (= 1.1.0) 401 | - libwebp/webp (= 1.1.0) 402 | - libwebp/demux (1.1.0): 403 | - libwebp/webp 404 | - libwebp/mux (1.1.0): 405 | - libwebp/demux 406 | - libwebp/webp (1.1.0) 407 | - Mantle (2.1.6): 408 | - Mantle/extobjc (= 2.1.6) 409 | - Mantle/extobjc (2.1.6) 410 | - nanopb (1.30905.0): 411 | - nanopb/decode (= 1.30905.0) 412 | - nanopb/encode (= 1.30905.0) 413 | - nanopb/decode (1.30905.0) 414 | - nanopb/encode (1.30905.0) 415 | - path_provider (0.0.1): 416 | - Flutter 417 | - "permission_handler (5.0.1+1)": 418 | - Flutter 419 | - PromisesObjC (1.2.11) 420 | - Protobuf (3.13.0) 421 | - SDWebImage/Core (5.9.3) 422 | - SDWebImageWebPCoder (0.6.1): 423 | - libwebp (~> 1.0) 424 | - SDWebImage/Core (~> 5.7) 425 | - sign_in_with_apple (0.0.1): 426 | - Flutter 427 | 428 | DEPENDENCIES: 429 | - cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`) 430 | - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) 431 | - firebase_core (from `.symlinks/plugins/firebase_core/ios`) 432 | - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) 433 | - firebase_storage (from `.symlinks/plugins/firebase_storage/ios`) 434 | - Flutter (from `Flutter`) 435 | - flutter_facebook_auth (from `.symlinks/plugins/flutter_facebook_auth/ios`) 436 | - flutter_image_compress (from `.symlinks/plugins/flutter_image_compress/ios`) 437 | - google_sign_in (from `.symlinks/plugins/google_sign_in/ios`) 438 | - image_picker (from `.symlinks/plugins/image_picker/ios`) 439 | - path_provider (from `.symlinks/plugins/path_provider/ios`) 440 | - permission_handler (from `.symlinks/plugins/permission_handler/ios`) 441 | - sign_in_with_apple (from `.symlinks/plugins/sign_in_with_apple/ios`) 442 | 443 | SPEC REPOS: 444 | trunk: 445 | - abseil 446 | - AppAuth 447 | - BoringSSL-GRPC 448 | - FBSDKCoreKit 449 | - FBSDKLoginKit 450 | - Firebase 451 | - FirebaseAnalyticsInterop 452 | - FirebaseAuth 453 | - FirebaseAuthInterop 454 | - FirebaseCore 455 | - FirebaseCoreDiagnostics 456 | - FirebaseCoreDiagnosticsInterop 457 | - FirebaseFirestore 458 | - FirebaseInstallations 459 | - FirebaseInstanceID 460 | - FirebaseMessaging 461 | - FirebaseStorage 462 | - GoogleDataTransport 463 | - GoogleSignIn 464 | - GoogleUtilities 465 | - "gRPC-C++" 466 | - gRPC-Core 467 | - GTMAppAuth 468 | - GTMSessionFetcher 469 | - leveldb-library 470 | - libwebp 471 | - Mantle 472 | - nanopb 473 | - PromisesObjC 474 | - Protobuf 475 | - SDWebImage 476 | - SDWebImageWebPCoder 477 | 478 | EXTERNAL SOURCES: 479 | cloud_firestore: 480 | :path: ".symlinks/plugins/cloud_firestore/ios" 481 | firebase_auth: 482 | :path: ".symlinks/plugins/firebase_auth/ios" 483 | firebase_core: 484 | :path: ".symlinks/plugins/firebase_core/ios" 485 | firebase_messaging: 486 | :path: ".symlinks/plugins/firebase_messaging/ios" 487 | firebase_storage: 488 | :path: ".symlinks/plugins/firebase_storage/ios" 489 | Flutter: 490 | :path: Flutter 491 | flutter_facebook_auth: 492 | :path: ".symlinks/plugins/flutter_facebook_auth/ios" 493 | flutter_image_compress: 494 | :path: ".symlinks/plugins/flutter_image_compress/ios" 495 | google_sign_in: 496 | :path: ".symlinks/plugins/google_sign_in/ios" 497 | image_picker: 498 | :path: ".symlinks/plugins/image_picker/ios" 499 | path_provider: 500 | :path: ".symlinks/plugins/path_provider/ios" 501 | permission_handler: 502 | :path: ".symlinks/plugins/permission_handler/ios" 503 | sign_in_with_apple: 504 | :path: ".symlinks/plugins/sign_in_with_apple/ios" 505 | 506 | SPEC CHECKSUMS: 507 | abseil: 6c8eb7892aefa08d929b39f9bb108e5367e3228f 508 | AppAuth: 31bcec809a638d7bd2f86ea8a52bd45f6e81e7c7 509 | BoringSSL-GRPC: 8edf627ee524575e2f8d19d56f068b448eea3879 510 | cloud_firestore: db3dab8f6a348f0f6ec01c3ba911ec5ac5e0df1a 511 | FBSDKCoreKit: b46507dc8b8cefed31d644e74d7cc30e2a715ef8 512 | FBSDKLoginKit: 1a61d79e2b25e2fc0d03dccab1e34b38bbdf2546 513 | Firebase: 7cf5f9c67f03cb3b606d1d6535286e1080e57eb6 514 | firebase_auth: 8ae6798925da84bf8745668a73c936b148c1b04d 515 | firebase_core: 00e54a4744164a6b5a250b96dd1ad5afaba7a342 516 | firebase_messaging: 666d9994651b1ecf8c582b52dd913f3fa58c17ef 517 | firebase_storage: 1195e13abca4fc81fde62d0fde5d0e9508a398e0 518 | FirebaseAnalyticsInterop: 3f86269c38ae41f47afeb43ebf32a001f58fcdae 519 | FirebaseAuth: 7047aec89c0b17ecd924a550c853f0c27ac6015e 520 | FirebaseAuthInterop: a0f37ae05833af156e72028f648d313f7e7592e9 521 | FirebaseCore: f42e5e5f382cdcf6b617ed737bf6c871a6947b17 522 | FirebaseCoreDiagnostics: 7535fe695737f8c5b350584292a70b7f8ff0357b 523 | FirebaseCoreDiagnosticsInterop: 296e2c5f5314500a850ad0b83e9e7c10b011a850 524 | FirebaseFirestore: 8c158bdde010fa397386333a74570eaef033e62d 525 | FirebaseInstallations: 6f5f680e65dc374397a483c32d1799ba822a395b 526 | FirebaseInstanceID: cef67c4967c7cecb56ea65d8acbb4834825c587b 527 | FirebaseMessaging: 29543feb343b09546ab3aa04d008ee8595b43c44 528 | FirebaseStorage: f4f39ae834a7145963b913f54e2f24a9db1d8fac 529 | Flutter: 0e3d915762c693b495b44d77113d4970485de6ec 530 | flutter_facebook_auth: 85c86b1f574faa5eaacd6de0db6b416fa94b326c 531 | flutter_image_compress: 082f8daaf6c1b0c9fe798251c750ef0ecd98d7ae 532 | google_sign_in: 6bd214b9c154f881422f5fe27b66aaa7bbd580cc 533 | GoogleDataTransport: 672fb0ce96fe7f7f31d43672fca62ad2c9c86f7b 534 | GoogleSignIn: 7137d297ddc022a7e0aa4619c86d72c909fa7213 535 | GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3 536 | "gRPC-C++": 13d8ccef97d5c3c441b7e3c529ef28ebee86fad2 537 | gRPC-Core: 4afa11bfbedf7cdecd04de535a9e046893404ed5 538 | GTMAppAuth: 197a8dabfea5d665224aa00d17f164fc2248dab9 539 | GTMSessionFetcher: 6f5c8abbab8a9bce4bb3f057e317728ec6182b10 540 | image_picker: 9c3312491f862b28d21ecd8fdf0ee14e601b3f09 541 | leveldb-library: 55d93ee664b4007aac644a782d11da33fba316f7 542 | libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3 543 | Mantle: 4c0ed6ce47c96eccc4dc3bb071deb3def0e2c3be 544 | nanopb: c43f40fadfe79e8b8db116583945847910cbabc9 545 | path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c 546 | permission_handler: eac8e15b4a1a3fba55b761d19f3f4e6b005d15b6 547 | PromisesObjC: 8c196f5a328c2cba3e74624585467a557dcb482f 548 | Protobuf: 3dac39b34a08151c6d949560efe3f86134a3f748 549 | SDWebImage: a31ee8e90a97303529e03fb0c333eae0eacb88e9 550 | SDWebImageWebPCoder: d0dac55073088d24b2ac1b191a71a8f8d0adac21 551 | sign_in_with_apple: 34f3f5456a45fd7ac5fb42905e2ad31dae061b4a 552 | 553 | PODFILE CHECKSUM: 7368163408c647b7eb699d0d788ba6718e18fb8d 554 | 555 | COCOAPODS: 1.9.3 556 | -------------------------------------------------------------------------------- /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 | 1A002F1325502EB6006905AA /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 1A002F1225502EB6006905AA /* 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 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 15 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 16 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 17 | E84AAD398A9C573A9B9B625C /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 036B127D606018CCA6000331 /* Pods_Runner.framework */; }; 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 | 036B127D606018CCA6000331 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 36 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 37 | 1A002F1225502EB6006905AA /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 38 | 1A49C2B825517AC300421ABB /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; 39 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 40 | 4292FBDDFFC0146FCE7CCAC3 /* 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 = ""; }; 41 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 42 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 43 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 44 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 45 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 46 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 48 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 49 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 50 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 51 | 9F03826D4E37AC736C4FEF01 /* 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 = ""; }; 52 | A4368E8CD5C23A3966F43655 /* 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 | E84AAD398A9C573A9B9B625C /* Pods_Runner.framework in Frameworks */, 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | /* End PBXFrameworksBuildPhase section */ 65 | 66 | /* Begin PBXGroup section */ 67 | 9740EEB11CF90186004384FC /* Flutter */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 71 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 72 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 73 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 74 | ); 75 | name = Flutter; 76 | sourceTree = ""; 77 | }; 78 | 97C146E51CF9000F007C117D = { 79 | isa = PBXGroup; 80 | children = ( 81 | 9740EEB11CF90186004384FC /* Flutter */, 82 | 97C146F01CF9000F007C117D /* Runner */, 83 | 97C146EF1CF9000F007C117D /* Products */, 84 | CF765A2754216E131ABC0089 /* Pods */, 85 | CF2974DCF16F8776A7B7DCD0 /* Frameworks */, 86 | ); 87 | sourceTree = ""; 88 | }; 89 | 97C146EF1CF9000F007C117D /* Products */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | 97C146EE1CF9000F007C117D /* Runner.app */, 93 | ); 94 | name = Products; 95 | sourceTree = ""; 96 | }; 97 | 97C146F01CF9000F007C117D /* Runner */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 1A49C2B825517AC300421ABB /* Runner.entitlements */, 101 | 1A002F1225502EB6006905AA /* GoogleService-Info.plist */, 102 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 103 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 104 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 105 | 97C147021CF9000F007C117D /* Info.plist */, 106 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 107 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 108 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 109 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 110 | ); 111 | path = Runner; 112 | sourceTree = ""; 113 | }; 114 | CF2974DCF16F8776A7B7DCD0 /* Frameworks */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 036B127D606018CCA6000331 /* Pods_Runner.framework */, 118 | ); 119 | name = Frameworks; 120 | sourceTree = ""; 121 | }; 122 | CF765A2754216E131ABC0089 /* Pods */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 4292FBDDFFC0146FCE7CCAC3 /* Pods-Runner.debug.xcconfig */, 126 | A4368E8CD5C23A3966F43655 /* Pods-Runner.release.xcconfig */, 127 | 9F03826D4E37AC736C4FEF01 /* Pods-Runner.profile.xcconfig */, 128 | ); 129 | path = Pods; 130 | sourceTree = ""; 131 | }; 132 | /* End PBXGroup section */ 133 | 134 | /* Begin PBXNativeTarget section */ 135 | 97C146ED1CF9000F007C117D /* Runner */ = { 136 | isa = PBXNativeTarget; 137 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 138 | buildPhases = ( 139 | 682FB7A730ADEA641680CA2E /* [CP] Check Pods Manifest.lock */, 140 | 9740EEB61CF901F6004384FC /* Run Script */, 141 | 97C146EA1CF9000F007C117D /* Sources */, 142 | 97C146EB1CF9000F007C117D /* Frameworks */, 143 | 97C146EC1CF9000F007C117D /* Resources */, 144 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 145 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 146 | D32E8104304111AC5530D0EC /* [CP] Embed Pods Frameworks */, 147 | 10A48C7CA78000EFB6E3B47A /* [CP] Copy Pods Resources */, 148 | ); 149 | buildRules = ( 150 | ); 151 | dependencies = ( 152 | ); 153 | name = Runner; 154 | productName = Runner; 155 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 156 | productType = "com.apple.product-type.application"; 157 | }; 158 | /* End PBXNativeTarget section */ 159 | 160 | /* Begin PBXProject section */ 161 | 97C146E61CF9000F007C117D /* Project object */ = { 162 | isa = PBXProject; 163 | attributes = { 164 | LastUpgradeCheck = 1020; 165 | ORGANIZATIONNAME = ""; 166 | TargetAttributes = { 167 | 97C146ED1CF9000F007C117D = { 168 | CreatedOnToolsVersion = 7.3.1; 169 | LastSwiftMigration = 1100; 170 | }; 171 | }; 172 | }; 173 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 174 | compatibilityVersion = "Xcode 9.3"; 175 | developmentRegion = en; 176 | hasScannedForEncodings = 0; 177 | knownRegions = ( 178 | en, 179 | Base, 180 | ); 181 | mainGroup = 97C146E51CF9000F007C117D; 182 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 183 | projectDirPath = ""; 184 | projectRoot = ""; 185 | targets = ( 186 | 97C146ED1CF9000F007C117D /* Runner */, 187 | ); 188 | }; 189 | /* End PBXProject section */ 190 | 191 | /* Begin PBXResourcesBuildPhase section */ 192 | 97C146EC1CF9000F007C117D /* Resources */ = { 193 | isa = PBXResourcesBuildPhase; 194 | buildActionMask = 2147483647; 195 | files = ( 196 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 197 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 198 | 1A002F1325502EB6006905AA /* GoogleService-Info.plist in Resources */, 199 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 200 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 201 | ); 202 | runOnlyForDeploymentPostprocessing = 0; 203 | }; 204 | /* End PBXResourcesBuildPhase section */ 205 | 206 | /* Begin PBXShellScriptBuildPhase section */ 207 | 10A48C7CA78000EFB6E3B47A /* [CP] Copy Pods Resources */ = { 208 | isa = PBXShellScriptBuildPhase; 209 | buildActionMask = 2147483647; 210 | files = ( 211 | ); 212 | inputFileListPaths = ( 213 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", 214 | ); 215 | name = "[CP] Copy Pods Resources"; 216 | outputFileListPaths = ( 217 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", 218 | ); 219 | runOnlyForDeploymentPostprocessing = 0; 220 | shellPath = /bin/sh; 221 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; 222 | showEnvVarsInLog = 0; 223 | }; 224 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 225 | isa = PBXShellScriptBuildPhase; 226 | buildActionMask = 2147483647; 227 | files = ( 228 | ); 229 | inputPaths = ( 230 | ); 231 | name = "Thin Binary"; 232 | outputPaths = ( 233 | ); 234 | runOnlyForDeploymentPostprocessing = 0; 235 | shellPath = /bin/sh; 236 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 237 | }; 238 | 682FB7A730ADEA641680CA2E /* [CP] Check Pods Manifest.lock */ = { 239 | isa = PBXShellScriptBuildPhase; 240 | buildActionMask = 2147483647; 241 | files = ( 242 | ); 243 | inputFileListPaths = ( 244 | ); 245 | inputPaths = ( 246 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 247 | "${PODS_ROOT}/Manifest.lock", 248 | ); 249 | name = "[CP] Check Pods Manifest.lock"; 250 | outputFileListPaths = ( 251 | ); 252 | outputPaths = ( 253 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 254 | ); 255 | runOnlyForDeploymentPostprocessing = 0; 256 | shellPath = /bin/sh; 257 | 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"; 258 | showEnvVarsInLog = 0; 259 | }; 260 | 9740EEB61CF901F6004384FC /* Run Script */ = { 261 | isa = PBXShellScriptBuildPhase; 262 | buildActionMask = 2147483647; 263 | files = ( 264 | ); 265 | inputPaths = ( 266 | ); 267 | name = "Run Script"; 268 | outputPaths = ( 269 | ); 270 | runOnlyForDeploymentPostprocessing = 0; 271 | shellPath = /bin/sh; 272 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 273 | }; 274 | D32E8104304111AC5530D0EC /* [CP] Embed Pods Frameworks */ = { 275 | isa = PBXShellScriptBuildPhase; 276 | buildActionMask = 2147483647; 277 | files = ( 278 | ); 279 | inputFileListPaths = ( 280 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", 281 | ); 282 | name = "[CP] Embed Pods Frameworks"; 283 | outputFileListPaths = ( 284 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", 285 | ); 286 | runOnlyForDeploymentPostprocessing = 0; 287 | shellPath = /bin/sh; 288 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 289 | showEnvVarsInLog = 0; 290 | }; 291 | /* End PBXShellScriptBuildPhase section */ 292 | 293 | /* Begin PBXSourcesBuildPhase section */ 294 | 97C146EA1CF9000F007C117D /* Sources */ = { 295 | isa = PBXSourcesBuildPhase; 296 | buildActionMask = 2147483647; 297 | files = ( 298 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 299 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 300 | ); 301 | runOnlyForDeploymentPostprocessing = 0; 302 | }; 303 | /* End PBXSourcesBuildPhase section */ 304 | 305 | /* Begin PBXVariantGroup section */ 306 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 307 | isa = PBXVariantGroup; 308 | children = ( 309 | 97C146FB1CF9000F007C117D /* Base */, 310 | ); 311 | name = Main.storyboard; 312 | sourceTree = ""; 313 | }; 314 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 315 | isa = PBXVariantGroup; 316 | children = ( 317 | 97C147001CF9000F007C117D /* Base */, 318 | ); 319 | name = LaunchScreen.storyboard; 320 | sourceTree = ""; 321 | }; 322 | /* End PBXVariantGroup section */ 323 | 324 | /* Begin XCBuildConfiguration section */ 325 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 326 | isa = XCBuildConfiguration; 327 | buildSettings = { 328 | ALWAYS_SEARCH_USER_PATHS = NO; 329 | CLANG_ANALYZER_NONNULL = YES; 330 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 331 | CLANG_CXX_LIBRARY = "libc++"; 332 | CLANG_ENABLE_MODULES = YES; 333 | CLANG_ENABLE_OBJC_ARC = YES; 334 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 335 | CLANG_WARN_BOOL_CONVERSION = YES; 336 | CLANG_WARN_COMMA = YES; 337 | CLANG_WARN_CONSTANT_CONVERSION = YES; 338 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 339 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 340 | CLANG_WARN_EMPTY_BODY = YES; 341 | CLANG_WARN_ENUM_CONVERSION = YES; 342 | CLANG_WARN_INFINITE_RECURSION = YES; 343 | CLANG_WARN_INT_CONVERSION = YES; 344 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 345 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 346 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 347 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 348 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 349 | CLANG_WARN_STRICT_PROTOTYPES = YES; 350 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 351 | CLANG_WARN_UNREACHABLE_CODE = YES; 352 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 353 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 354 | COPY_PHASE_STRIP = NO; 355 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 356 | ENABLE_NS_ASSERTIONS = NO; 357 | ENABLE_STRICT_OBJC_MSGSEND = YES; 358 | GCC_C_LANGUAGE_STANDARD = gnu99; 359 | GCC_NO_COMMON_BLOCKS = YES; 360 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 361 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 362 | GCC_WARN_UNDECLARED_SELECTOR = YES; 363 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 364 | GCC_WARN_UNUSED_FUNCTION = YES; 365 | GCC_WARN_UNUSED_VARIABLE = YES; 366 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 367 | MTL_ENABLE_DEBUG_INFO = NO; 368 | SDKROOT = iphoneos; 369 | SUPPORTED_PLATFORMS = iphoneos; 370 | TARGETED_DEVICE_FAMILY = "1,2"; 371 | VALIDATE_PRODUCT = YES; 372 | }; 373 | name = Profile; 374 | }; 375 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 376 | isa = XCBuildConfiguration; 377 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 378 | buildSettings = { 379 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 380 | CLANG_ENABLE_MODULES = YES; 381 | CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; 382 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 383 | DEVELOPMENT_TEAM = AX352BQR6K; 384 | ENABLE_BITCODE = NO; 385 | FRAMEWORK_SEARCH_PATHS = ( 386 | "$(inherited)", 387 | "$(PROJECT_DIR)/Flutter", 388 | ); 389 | INFOPLIST_FILE = Runner/Info.plist; 390 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 391 | LD_RUNPATH_SEARCH_PATHS = ( 392 | "$(inherited)", 393 | "@executable_path/Frameworks", 394 | ); 395 | LIBRARY_SEARCH_PATHS = ( 396 | "$(inherited)", 397 | "$(PROJECT_DIR)/Flutter", 398 | ); 399 | PRODUCT_BUNDLE_IDENTIFIER = com.sonub.fireflutter; 400 | PRODUCT_NAME = "$(TARGET_NAME)"; 401 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 402 | SWIFT_VERSION = 5.0; 403 | VERSIONING_SYSTEM = "apple-generic"; 404 | }; 405 | name = Profile; 406 | }; 407 | 97C147031CF9000F007C117D /* Debug */ = { 408 | isa = XCBuildConfiguration; 409 | buildSettings = { 410 | ALWAYS_SEARCH_USER_PATHS = NO; 411 | CLANG_ANALYZER_NONNULL = YES; 412 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 413 | CLANG_CXX_LIBRARY = "libc++"; 414 | CLANG_ENABLE_MODULES = YES; 415 | CLANG_ENABLE_OBJC_ARC = YES; 416 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 417 | CLANG_WARN_BOOL_CONVERSION = YES; 418 | CLANG_WARN_COMMA = YES; 419 | CLANG_WARN_CONSTANT_CONVERSION = YES; 420 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 421 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 422 | CLANG_WARN_EMPTY_BODY = YES; 423 | CLANG_WARN_ENUM_CONVERSION = YES; 424 | CLANG_WARN_INFINITE_RECURSION = YES; 425 | CLANG_WARN_INT_CONVERSION = YES; 426 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 427 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 428 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 429 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 430 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 431 | CLANG_WARN_STRICT_PROTOTYPES = YES; 432 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 433 | CLANG_WARN_UNREACHABLE_CODE = YES; 434 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 435 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 436 | COPY_PHASE_STRIP = NO; 437 | DEBUG_INFORMATION_FORMAT = dwarf; 438 | ENABLE_STRICT_OBJC_MSGSEND = YES; 439 | ENABLE_TESTABILITY = YES; 440 | GCC_C_LANGUAGE_STANDARD = gnu99; 441 | GCC_DYNAMIC_NO_PIC = NO; 442 | GCC_NO_COMMON_BLOCKS = YES; 443 | GCC_OPTIMIZATION_LEVEL = 0; 444 | GCC_PREPROCESSOR_DEFINITIONS = ( 445 | "DEBUG=1", 446 | "$(inherited)", 447 | ); 448 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 449 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 450 | GCC_WARN_UNDECLARED_SELECTOR = YES; 451 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 452 | GCC_WARN_UNUSED_FUNCTION = YES; 453 | GCC_WARN_UNUSED_VARIABLE = YES; 454 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 455 | MTL_ENABLE_DEBUG_INFO = YES; 456 | ONLY_ACTIVE_ARCH = YES; 457 | SDKROOT = iphoneos; 458 | TARGETED_DEVICE_FAMILY = "1,2"; 459 | }; 460 | name = Debug; 461 | }; 462 | 97C147041CF9000F007C117D /* Release */ = { 463 | isa = XCBuildConfiguration; 464 | buildSettings = { 465 | ALWAYS_SEARCH_USER_PATHS = NO; 466 | CLANG_ANALYZER_NONNULL = YES; 467 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 468 | CLANG_CXX_LIBRARY = "libc++"; 469 | CLANG_ENABLE_MODULES = YES; 470 | CLANG_ENABLE_OBJC_ARC = YES; 471 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 472 | CLANG_WARN_BOOL_CONVERSION = YES; 473 | CLANG_WARN_COMMA = YES; 474 | CLANG_WARN_CONSTANT_CONVERSION = YES; 475 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 476 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 477 | CLANG_WARN_EMPTY_BODY = YES; 478 | CLANG_WARN_ENUM_CONVERSION = YES; 479 | CLANG_WARN_INFINITE_RECURSION = YES; 480 | CLANG_WARN_INT_CONVERSION = YES; 481 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 482 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 483 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 484 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 485 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 486 | CLANG_WARN_STRICT_PROTOTYPES = YES; 487 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 488 | CLANG_WARN_UNREACHABLE_CODE = YES; 489 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 490 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 491 | COPY_PHASE_STRIP = NO; 492 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 493 | ENABLE_NS_ASSERTIONS = NO; 494 | ENABLE_STRICT_OBJC_MSGSEND = YES; 495 | GCC_C_LANGUAGE_STANDARD = gnu99; 496 | GCC_NO_COMMON_BLOCKS = YES; 497 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 498 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 499 | GCC_WARN_UNDECLARED_SELECTOR = YES; 500 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 501 | GCC_WARN_UNUSED_FUNCTION = YES; 502 | GCC_WARN_UNUSED_VARIABLE = YES; 503 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 504 | MTL_ENABLE_DEBUG_INFO = NO; 505 | SDKROOT = iphoneos; 506 | SUPPORTED_PLATFORMS = iphoneos; 507 | SWIFT_COMPILATION_MODE = wholemodule; 508 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 509 | TARGETED_DEVICE_FAMILY = "1,2"; 510 | VALIDATE_PRODUCT = YES; 511 | }; 512 | name = Release; 513 | }; 514 | 97C147061CF9000F007C117D /* Debug */ = { 515 | isa = XCBuildConfiguration; 516 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 517 | buildSettings = { 518 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 519 | CLANG_ENABLE_MODULES = YES; 520 | CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; 521 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 522 | DEVELOPMENT_TEAM = AX352BQR6K; 523 | ENABLE_BITCODE = NO; 524 | FRAMEWORK_SEARCH_PATHS = ( 525 | "$(inherited)", 526 | "$(PROJECT_DIR)/Flutter", 527 | ); 528 | INFOPLIST_FILE = Runner/Info.plist; 529 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 530 | LD_RUNPATH_SEARCH_PATHS = ( 531 | "$(inherited)", 532 | "@executable_path/Frameworks", 533 | ); 534 | LIBRARY_SEARCH_PATHS = ( 535 | "$(inherited)", 536 | "$(PROJECT_DIR)/Flutter", 537 | ); 538 | PRODUCT_BUNDLE_IDENTIFIER = com.sonub.fireflutter; 539 | PRODUCT_NAME = "$(TARGET_NAME)"; 540 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 541 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 542 | SWIFT_VERSION = 5.0; 543 | VERSIONING_SYSTEM = "apple-generic"; 544 | }; 545 | name = Debug; 546 | }; 547 | 97C147071CF9000F007C117D /* Release */ = { 548 | isa = XCBuildConfiguration; 549 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 550 | buildSettings = { 551 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 552 | CLANG_ENABLE_MODULES = YES; 553 | CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; 554 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 555 | DEVELOPMENT_TEAM = AX352BQR6K; 556 | ENABLE_BITCODE = NO; 557 | FRAMEWORK_SEARCH_PATHS = ( 558 | "$(inherited)", 559 | "$(PROJECT_DIR)/Flutter", 560 | ); 561 | INFOPLIST_FILE = Runner/Info.plist; 562 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 563 | LD_RUNPATH_SEARCH_PATHS = ( 564 | "$(inherited)", 565 | "@executable_path/Frameworks", 566 | ); 567 | LIBRARY_SEARCH_PATHS = ( 568 | "$(inherited)", 569 | "$(PROJECT_DIR)/Flutter", 570 | ); 571 | PRODUCT_BUNDLE_IDENTIFIER = com.sonub.fireflutter; 572 | PRODUCT_NAME = "$(TARGET_NAME)"; 573 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 574 | SWIFT_VERSION = 5.0; 575 | VERSIONING_SYSTEM = "apple-generic"; 576 | }; 577 | name = Release; 578 | }; 579 | /* End XCBuildConfiguration section */ 580 | 581 | /* Begin XCConfigurationList section */ 582 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 583 | isa = XCConfigurationList; 584 | buildConfigurations = ( 585 | 97C147031CF9000F007C117D /* Debug */, 586 | 97C147041CF9000F007C117D /* Release */, 587 | 249021D3217E4FDB00AE95B9 /* Profile */, 588 | ); 589 | defaultConfigurationIsVisible = 0; 590 | defaultConfigurationName = Release; 591 | }; 592 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 593 | isa = XCConfigurationList; 594 | buildConfigurations = ( 595 | 97C147061CF9000F007C117D /* Debug */, 596 | 97C147071CF9000F007C117D /* Release */, 597 | 249021D4217E4FDB00AE95B9 /* Profile */, 598 | ); 599 | defaultConfigurationIsVisible = 0; 600 | defaultConfigurationName = Release; 601 | }; 602 | /* End XCConfigurationList section */ 603 | }; 604 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 605 | } 606 | -------------------------------------------------------------------------------- /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 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /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/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/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/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/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/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/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/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/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/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/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/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/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/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/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/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/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/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/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/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/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/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/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/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/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/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/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/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/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/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/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/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/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 | 389649476574-3pp2tstq9l5k8ecotn228v6b0gl905vp.apps.googleusercontent.com 7 | REVERSED_CLIENT_ID 8 | com.googleusercontent.apps.389649476574-3pp2tstq9l5k8ecotn228v6b0gl905vp 9 | API_KEY 10 | AIzaSyCe2BoolOEIpBvLGexp9QTyF7qStRE_cR0 11 | GCM_SENDER_ID 12 | 389649476574 13 | PLIST_VERSION 14 | 1 15 | BUNDLE_ID 16 | com.sonub.fireflutter 17 | PROJECT_ID 18 | fireflutter-test 19 | STORAGE_BUCKET 20 | fireflutter-test.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:389649476574:ios:23cd30a938db5c2156cb4f 33 | DATABASE_URL 34 | https://fireflutter-test.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 | CFBundleLocalizations 14 | 15 | en 16 | ch 17 | ja 18 | ko 19 | 20 | CFBundleName 21 | fireflutter_sample_app 22 | CFBundlePackageType 23 | APPL 24 | CFBundleShortVersionString 25 | $(FLUTTER_BUILD_NAME) 26 | CFBundleSignature 27 | ???? 28 | CFBundleURLTypes 29 | 30 | 31 | CFBundleURLSchemes 32 | 33 | fb2760886707561630 34 | 35 | 36 | 37 | CFBundleTypeRole 38 | Editor 39 | CFBundleURLSchemes 40 | 41 | com.googleusercontent.apps.389649476574-3pp2tstq9l5k8ecotn228v6b0gl905vp 42 | 43 | 44 | 45 | CFBundleVersion 46 | $(FLUTTER_BUILD_NUMBER) 47 | FacebookAppID 48 | 2760886707561630 49 | FacebookDisplayName 50 | Name FireFlutter Test 51 | LSApplicationQueriesSchemes 52 | 53 | fbapi 54 | fb-messenger-share-api 55 | fbauth2 56 | fbshareextension 57 | 58 | LSRequiresIPhoneOS 59 | 60 | NSCameraUsageDescription 61 | $(EXECUTABLE_NAME) need access to Camera. 62 | NSMicrophoneUsageDescription 63 | $(EXECUTABLE_NAME) need access to Microphone. 64 | NSPhotoLibraryUsageDescription 65 | $(EXECUTABLE_NAME) need access to Photo Library. 66 | UILaunchStoryboardName 67 | LaunchScreen 68 | UIMainStoryboardFile 69 | Main 70 | UISupportedInterfaceOrientations 71 | 72 | UIInterfaceOrientationPortrait 73 | UIInterfaceOrientationLandscapeLeft 74 | UIInterfaceOrientationLandscapeRight 75 | 76 | UISupportedInterfaceOrientations~ipad 77 | 78 | UIInterfaceOrientationPortrait 79 | UIInterfaceOrientationPortraitUpsideDown 80 | UIInterfaceOrientationLandscapeLeft 81 | UIInterfaceOrientationLandscapeRight 82 | 83 | UIViewControllerBasedStatusBarAppearance 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /keystore.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thruthesky/fireflutter_sample_app/64e7ab50ca775629fd2d4f6ad5f75d5691e8d3d2/keystore.key -------------------------------------------------------------------------------- /lib/global_variables.dart: -------------------------------------------------------------------------------- 1 | import 'package:fireflutter/fireflutter.dart'; 2 | 3 | FireFlutter ff = FireFlutter(); 4 | -------------------------------------------------------------------------------- /lib/keys.dart: -------------------------------------------------------------------------------- 1 | class Keys { 2 | static const homeScreen = 'home_screen'; 3 | static const hPleaseLogin = 'home_screen_please_login'; 4 | static const hLogoutButton = 'home_screen_logout_buton'; 5 | static const hLoginButton = 'home_screen_login_buton'; 6 | static const hInfo = 'home_screen_info'; 7 | static const rButton = 'register_button'; 8 | static const rsButton = 'register_submit_button'; 9 | static const riEmail = 'register_input_email'; 10 | static const riPassword = 'register_input_password'; 11 | static const riDisplayName = 'register_input_displayname'; 12 | static const riColor = 'register_input_color'; 13 | static const hEmail = 'home_screen_email'; 14 | static const hColor = 'home_screen_color'; 15 | static const lfEmail = 'login_form_email'; 16 | static const lfPassword = 'login_form_password'; 17 | static const lfSubmitButton = 'login_form_submit_button'; 18 | static const hProfileButotn = 'home_screen_profile_button'; 19 | static const pfSubmitButton = 'profile_form_submit_button'; 20 | static const pfDisplayName = 'profile_form_display_name'; 21 | static const pfColor = 'profile_form_color'; 22 | } 23 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:fireflutter/fireflutter.dart'; 4 | import 'package:fireflutter_sample_app/screens/admin/admin.screen.dart'; 5 | import 'package:fireflutter_sample_app/screens/admin/admin.category.screen.dart'; 6 | import 'package:fireflutter_sample_app/screens/forum/post.edit.screen.dart'; 7 | import 'package:fireflutter_sample_app/screens/forum/post.list.screen.dart'; 8 | import 'package:fireflutter_sample_app/screens/home/home.screen.dart'; 9 | import 'package:fireflutter_sample_app/screens/login/login.screen.dart'; 10 | import 'package:fireflutter_sample_app/screens/phone_auth/phone_auth.screen.dart'; 11 | import 'package:fireflutter_sample_app/screens/phone_auth/phone_auth_verification_code.screen.dart'; 12 | import 'package:fireflutter_sample_app/screens/profile/profile.screen.dart'; 13 | import 'package:fireflutter_sample_app/screens/push-notification/push-notification.screen.dart'; 14 | import 'package:fireflutter_sample_app/screens/register/register.screen.dart'; 15 | import 'package:fireflutter_sample_app/screens/search/search.screen.dart'; 16 | import 'package:fireflutter_sample_app/screens/settings/settings.screen.dart'; 17 | import 'package:fireflutter_sample_app/translations.dart'; 18 | import 'package:flutter/material.dart'; 19 | import 'package:get/get.dart'; 20 | import './global_variables.dart'; 21 | 22 | void main() async { 23 | WidgetsFlutterBinding.ensureInitialized(); 24 | await ff.init( 25 | settings: { 26 | 'app': { 27 | 'default-language': 'ko', 28 | 'verify-after-register': false, 29 | 'verify-after-login': false, 30 | 'force-verification': false, 31 | 'block-non-verified-users-to-create': false, 32 | 'ALGOLIA_APP_ID': "W42X6RIXO5", 33 | 'ALGOLIA_SEARCH_KEY': "710ce6c481caf890163ba0c24573130f", 34 | 'ALGOLIA_INDEX_NAME': "Dev" 35 | }, 36 | }, 37 | translations: translations, 38 | enableNotification: true, 39 | firebaseServerToken: 40 | 'AAAAWrjrK94:APA91bGJuMd80xlpz1m8W61PxCS_2Ir_5y4mUcjPMUlNi-wGGaFoXQL9XiUTjBSv8fCSBBWa9-GTsuFNPWfrCF9TFOCmeJgzxtXfuS5EgH1NWEuEmlerbFAz-XIa2DYEpyQWkWwhFQJa', 41 | ); 42 | runApp(MainApp()); 43 | } 44 | 45 | class MainApp extends StatefulWidget { 46 | @override 47 | _MainAppState createState() => _MainAppState(); 48 | } 49 | 50 | class _MainAppState extends State { 51 | @override 52 | void initState() { 53 | super.initState(); 54 | ff.translationsChange.listen((x) => setState(() => updateTranslations(x))); 55 | ff.userChange.listen((x) { 56 | setState(() { 57 | Get.updateLocale(Locale(ff.userLanguage)); 58 | }); 59 | }); 60 | ff.settingsChange.listen((settings) { 61 | setState(() {}); 62 | }); 63 | Timer(Duration(milliseconds: 200), () { 64 | // Get.toNamed( 65 | // 'forum-list', 66 | // arguments: {'category': 'qna'}, 67 | // ); 68 | // Get.toNamed('phone-auth'); 69 | 70 | // () async { 71 | // await ff.login(email: 'user@gmail.com', password: '12345a'); 72 | // print(ff.user.uid); 73 | // print(ff.user.email); 74 | // }(); 75 | // Get.toNamed('settings'); 76 | }); 77 | 78 | ff.notification.listen((x) { 79 | Map notification = x['notification']; 80 | Map data = x['data']; 81 | NotificationType type = x['type']; 82 | // print('NotificationType: $type'); 83 | // print('notification: $notification'); 84 | // print('data: $data'); 85 | 86 | /// Ignore message from myself. 87 | if (data['senderUid'] == ff.user.uid) { 88 | return; 89 | } 90 | if (type == NotificationType.onMessage) { 91 | Get.snackbar( 92 | notification['title'].toString(), 93 | notification['body'].toString(), 94 | onTap: (_) { 95 | if (data != null && data['screen'] != null) { 96 | Get.toNamed(data['screen'], arguments: {'id': data['id'] ?? ''}); 97 | } 98 | }, 99 | mainButton: (data != null && data['screen'] != null) 100 | ? FlatButton( 101 | child: Text('Open'), 102 | onPressed: () { 103 | Get.toNamed(data['screen'], 104 | arguments: {'id': data['id'] ?? ''}); 105 | }, 106 | ) 107 | : Container(), 108 | ); 109 | } else { 110 | /// App will come here when the user open the app by tapping a push notification on the system tray. 111 | if (data != null && data['screen'] != null) { 112 | Get.toNamed(data['screen'], 113 | arguments: {'id': data['id'] ?? '', 'data': data}); 114 | } 115 | } 116 | }); 117 | } 118 | 119 | @override 120 | Widget build(BuildContext context) { 121 | return GetMaterialApp( 122 | initialRoute: 'home', 123 | locale: Locale(ff.userLanguage), 124 | translations: AppTranslations(), 125 | getPages: [ 126 | GetPage(name: 'home', page: () => HomeScreen()), 127 | GetPage(name: 'register', page: () => RegisterScreen()), 128 | GetPage(name: 'login', page: () => LoginScreen()), 129 | GetPage(name: 'profile', page: () => ProfileScreen()), 130 | GetPage(name: 'admin', page: () => AdminScreen()), 131 | GetPage(name: 'admin-category', page: () => AdminCategoryScreen()), 132 | GetPage(name: 'forum-edit', page: () => ForumEditScreen()), 133 | GetPage(name: 'forum-list', page: () => ForumListScreen()), 134 | GetPage(name: 'phone-auth', page: () => PhoneAuthScreen()), 135 | GetPage( 136 | name: 'phone-auth-code-verification', 137 | page: () => PhoneAuthCodeVerificationScreen()), 138 | GetPage(name: 'push-notification', page: () => PushNotification()), 139 | GetPage(name: 'settings', page: () => SettingsScreen()), 140 | GetPage(name: 'search', page: () => SearchScreen()), 141 | ], 142 | routingCallback: (routing) { 143 | if (ff.loggedIn) { 144 | if (ff.user.phoneNumber.isNullOrBlank && 145 | ff.appSetting('force-verification') == true) { 146 | if (routing.current != 'home') { 147 | WidgetsBinding.instance 148 | .addPostFrameCallback((_) => Get.offNamed('phone-auth')); 149 | } 150 | } 151 | } 152 | }, 153 | ); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /lib/screens/admin/admin.category.screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:fireflutter_sample_app/global_variables.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:get/get.dart'; 5 | 6 | enum Mode { create, update, delete } 7 | 8 | class AdminCategoryScreen extends StatefulWidget { 9 | @override 10 | _AdminCategoryScreenState createState() => _AdminCategoryScreenState(); 11 | } 12 | 13 | class _AdminCategoryScreenState extends State { 14 | final idController = TextEditingController(); 15 | final titleController = TextEditingController(); 16 | final descriptionController = TextEditingController(); 17 | 18 | final db = FirebaseFirestore.instance; 19 | CollectionReference get categories => db.collection('categories'); 20 | 21 | Mode mode = Mode.create; 22 | 23 | reset() { 24 | setState(() { 25 | idController.text = ''; 26 | mode = Mode.create; 27 | titleController.text = ''; 28 | descriptionController.text = ''; 29 | }); 30 | } 31 | 32 | @override 33 | Widget build(BuildContext context) { 34 | return Scaffold( 35 | appBar: AppBar( 36 | title: Text('Forum Category'), 37 | ), 38 | body: Container( 39 | padding: EdgeInsets.all(16), 40 | child: ff.isAdmin 41 | ? Container( 42 | child: Column( 43 | children: [ 44 | TextFormField( 45 | controller: idController, 46 | decoration: InputDecoration(labelText: "Category ID"), 47 | enabled: mode == Mode.create, 48 | ), 49 | TextFormField( 50 | controller: titleController, 51 | decoration: InputDecoration(labelText: "Title"), 52 | ), 53 | TextFormField( 54 | controller: descriptionController, 55 | decoration: InputDecoration(labelText: "Description"), 56 | ), 57 | Row( 58 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 59 | children: [ 60 | RaisedButton( 61 | onPressed: reset, 62 | child: Text('Reset'), 63 | ), 64 | RaisedButton( 65 | onPressed: () async { 66 | String id = idController.text; 67 | String title = titleController.text; 68 | String description = descriptionController.text; 69 | try { 70 | if (mode == Mode.create) { 71 | DocumentSnapshot doc = 72 | await categories.doc(id).get(); 73 | if (doc.exists) { 74 | return Get.snackbar( 75 | 'Error', 'Category exists'); 76 | } 77 | } 78 | await categories.doc(id).set({ 79 | 'id': id, 80 | 'title': title, 81 | 'description': description, 82 | }, SetOptions(merge: true)); 83 | reset(); 84 | } catch (e) { 85 | Get.snackbar('Error', e.toString()); 86 | } 87 | }, 88 | child: Text('Submit'), 89 | ) 90 | ], 91 | ), 92 | StreamBuilder( 93 | stream: categories.snapshots(), 94 | builder: (c, AsyncSnapshot snapshot) { 95 | if (snapshot.hasError) { 96 | return Text('Something went wrong'); 97 | } 98 | 99 | if (snapshot.connectionState == 100 | ConnectionState.waiting) { 101 | return SizedBox.shrink(); 102 | } 103 | 104 | return ListView.builder( 105 | shrinkWrap: true, 106 | itemCount: snapshot.data.docs.length, 107 | itemBuilder: (cc, i) { 108 | QueryDocumentSnapshot doc = 109 | snapshot.data.docs.elementAt(i); 110 | final Map data = doc.data(); 111 | 112 | return ListTile( 113 | title: Text(data['id'] + ' : ' + data['title']), 114 | subtitle: Text(data['description']), 115 | trailing: PopupMenuButton( 116 | icon: Icon(Icons.more_vert), 117 | onSelected: (Mode result) { 118 | if (result == Mode.update) { 119 | mode = Mode.update; 120 | idController.text = data['id']; 121 | titleController.text = data['title']; 122 | descriptionController.text = 123 | data['description']; 124 | } else if (result == Mode.delete) { 125 | categories.doc(data['id']).delete(); 126 | } 127 | }, 128 | itemBuilder: (BuildContext context) => 129 | >[ 130 | const PopupMenuItem( 131 | value: Mode.update, 132 | child: Text('Update'), 133 | ), 134 | const PopupMenuItem( 135 | value: Mode.delete, 136 | child: Text('Delete'), 137 | ), 138 | ], 139 | ), 140 | ); 141 | }, 142 | ); 143 | }, 144 | ), 145 | ], 146 | ), 147 | ) 148 | : Text('You are not admin!'), 149 | ), 150 | ); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /lib/screens/admin/admin.screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | 4 | class AdminScreen extends StatefulWidget { 5 | @override 6 | _AdminScreenState createState() => _AdminScreenState(); 7 | } 8 | 9 | class _AdminScreenState extends State { 10 | @override 11 | Widget build(BuildContext context) { 12 | return Scaffold( 13 | appBar: AppBar( 14 | title: Text('Admin Screen'), 15 | ), 16 | body: Column( 17 | children: [ 18 | RaisedButton( 19 | onPressed: () => Get.toNamed('admin-category'), 20 | child: Text('Forum Category'), 21 | ), 22 | ], 23 | ), 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/screens/forum/comment.dart: -------------------------------------------------------------------------------- 1 | import 'package:fireflutter/fireflutter.dart'; 2 | import 'package:fireflutter_sample_app/global_variables.dart'; 3 | import 'package:fireflutter_sample_app/screens/forum/comment.form.dart'; 4 | import 'package:fireflutter_sample_app/screens/forum/display_photos.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:get/route_manager.dart'; 7 | 8 | class Comment extends StatefulWidget { 9 | const Comment({ 10 | Key key, 11 | @required this.post, 12 | this.index, 13 | }) : super(key: key); 14 | 15 | final Map post; 16 | final int index; 17 | 18 | @override 19 | _CommentState createState() => _CommentState(); 20 | } 21 | 22 | class _CommentState extends State { 23 | bool edit = false; 24 | Map comment; 25 | @override 26 | Widget build(BuildContext context) { 27 | comment = widget.post['comments'][widget.index]; 28 | return Column( 29 | crossAxisAlignment: CrossAxisAlignment.start, 30 | children: [ 31 | Text( 32 | comment['id'], 33 | style: TextStyle( 34 | fontSize: 10, 35 | ), 36 | ), 37 | edit 38 | ? CommentForm( 39 | post: widget.post, 40 | comment: comment, 41 | onCancel: () => setState(() => edit = false), 42 | onSuccess: () => setState(() => edit = false), 43 | ) 44 | : Column( 45 | children: [ 46 | Container( 47 | margin: 48 | EdgeInsets.only(top: 16, left: comment['depth'] * 32.0), 49 | padding: EdgeInsets.all(16), 50 | width: double.infinity, 51 | color: Colors.grey[200], 52 | child: Text(comment['content'], 53 | style: TextStyle(fontSize: 16)), 54 | ), 55 | DisplayPhotos(files: comment['files']), 56 | Row( 57 | children: [ 58 | TextButton( 59 | onPressed: () => setState(() => edit = true), 60 | child: Text('Edit'), 61 | ), 62 | TextButton( 63 | onPressed: () async { 64 | try { 65 | await ff.deleteComment( 66 | widget.post['id'], 67 | comment['id'], 68 | ); 69 | } catch (e) { 70 | Get.snackbar('Error', e.toString()); 71 | } 72 | }, 73 | child: Text('Delete'), 74 | ), 75 | TextButton( 76 | onPressed: () async { 77 | try { 78 | await ff.vote( 79 | postId: widget.post['id'], 80 | commentId: comment['id'], 81 | choice: VoteChoice.like, 82 | ); 83 | } catch (e) { 84 | Get.snackbar('Error', e.toString()); 85 | } 86 | }, 87 | child: Text('Like ${comment['likes'] ?? ''}'), 88 | ), 89 | TextButton( 90 | onPressed: () async { 91 | try { 92 | await ff.vote( 93 | postId: widget.post['id'], 94 | commentId: comment['id'], 95 | choice: VoteChoice.dislike, 96 | ); 97 | } catch (e) { 98 | Get.snackbar('Error', e.toString()); 99 | } 100 | }, 101 | child: Text('Dislike ${comment['dislikes'] ?? ''}'), 102 | ), 103 | ], 104 | ), 105 | CommentForm( 106 | post: widget.post, 107 | parentIndex: widget.index, 108 | ) 109 | ], 110 | ), 111 | ], 112 | ); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /lib/screens/forum/comment.form.dart: -------------------------------------------------------------------------------- 1 | import 'package:fireflutter_sample_app/global_variables.dart'; 2 | import 'package:fireflutter_sample_app/screens/forum/edit_photos.dart'; 3 | import 'package:fireflutter_sample_app/screens/forum/photo_upload.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:get/get.dart'; 6 | 7 | class CommentForm extends StatefulWidget { 8 | const CommentForm({ 9 | Key key, 10 | @required this.post, 11 | this.comment, 12 | this.parentIndex, 13 | this.onCancel, 14 | this.onSuccess, 15 | }) : super(key: key); 16 | 17 | /// the post that this comments belongs to. 18 | final Map post; 19 | 20 | /// comment to edit. 21 | /// 22 | /// If it is not edit, then it must be null. 23 | final Map comment; 24 | 25 | /// [parentIndex] is the position of the comment that the new comment is 26 | /// going to be attached to in post.comments array 27 | /// 28 | final int parentIndex; 29 | 30 | final Function onCancel; 31 | final Function onSuccess; 32 | 33 | @override 34 | _CommentFormState createState() => _CommentFormState(); 35 | } 36 | 37 | class _CommentFormState extends State { 38 | final contentController = TextEditingController(); 39 | 40 | List files = []; 41 | double uploadProgress = 0; 42 | @override 43 | initState() { 44 | if (widget.comment != null) { 45 | files = widget.comment['files'] ?? []; 46 | contentController.text = widget.comment['content']; 47 | } 48 | super.initState(); 49 | } 50 | 51 | @override 52 | Widget build(BuildContext context) { 53 | return Column( 54 | children: [ 55 | Row( 56 | children: [ 57 | PhotoUpload( 58 | files: files, 59 | onProgress: (p) => setState(() => uploadProgress = p), 60 | ), 61 | Expanded( 62 | child: TextFormField(controller: contentController), 63 | ), 64 | ], 65 | ), 66 | if (uploadProgress > 0) 67 | LinearProgressIndicator( 68 | value: uploadProgress, 69 | ), 70 | EditPotos( 71 | files: files, 72 | ), 73 | Row( 74 | mainAxisAlignment: MainAxisAlignment.end, 75 | children: [ 76 | if (widget.onCancel != null) 77 | TextButton(onPressed: widget.onCancel, child: Text('Cancel')), 78 | RaisedButton( 79 | onPressed: () async { 80 | if (ff.user.phoneNumber.isNullOrBlank && 81 | ff.appSetting('block-non-verified-users-to-create') == 82 | true) { 83 | Get.defaultDialog( 84 | middleText: 'Please verify your phone number first!', 85 | textConfirm: 'Ok', 86 | confirmTextColor: Colors.white, 87 | onConfirm: () => Get.back(), 88 | ); 89 | return; 90 | } 91 | final data = { 92 | 'content': contentController.text, 93 | 'files': files 94 | }; 95 | if (widget.comment != null) { 96 | data['id'] = widget.comment['id']; 97 | } 98 | try { 99 | await ff.editComment(data, widget.post, 100 | parentIndex: widget.parentIndex); 101 | contentController.text = ''; 102 | files = []; 103 | if (widget.onSuccess != null) widget.onSuccess(); 104 | } catch (e) { 105 | Get.snackbar('Error', e.toString()); 106 | } 107 | }, 108 | child: Text('Submit'), 109 | ), 110 | ], 111 | ) 112 | ], 113 | ); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /lib/screens/forum/display_photos.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class DisplayPhotos extends StatelessWidget { 4 | DisplayPhotos({ 5 | Key key, 6 | @required this.files, 7 | }) : super(key: key); 8 | 9 | final List files; 10 | @override 11 | Widget build(BuildContext context) { 12 | /// Display uploaded images. 13 | if (files == null) { 14 | return Container(); 15 | } else { 16 | return Column( 17 | children: [ 18 | for (String url in files) Image.network(url), 19 | ], 20 | ); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/screens/forum/edit_photos.dart: -------------------------------------------------------------------------------- 1 | import 'package:fireflutter_sample_app/global_variables.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | class EditPotos extends StatefulWidget { 6 | EditPotos({this.files}); 7 | final List files; 8 | @override 9 | _EditPotosState createState() => _EditPotosState(); 10 | } 11 | 12 | class _EditPotosState extends State { 13 | @override 14 | Widget build(BuildContext context) { 15 | return Container( 16 | child: Wrap( 17 | children: [ 18 | for (String url in widget.files) 19 | Stack( 20 | children: [ 21 | SizedBox( 22 | child: Image.network(url), 23 | width: 100, 24 | height: 100, 25 | ), 26 | MaterialButton( 27 | onPressed: () async { 28 | try { 29 | await ff.deleteFile(url); 30 | setState(() => widget.files.remove(url)); 31 | } catch (e) { 32 | Get.snackbar('Error', e.toString()); 33 | } 34 | }, 35 | color: Colors.blue, 36 | textColor: Colors.white, 37 | child: Icon( 38 | Icons.delete, 39 | size: 24, 40 | ), 41 | padding: EdgeInsets.all(4.0), 42 | shape: CircleBorder(), 43 | ), 44 | ], 45 | ), 46 | ], 47 | ), 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/screens/forum/photo_upload.dart: -------------------------------------------------------------------------------- 1 | import 'package:fireflutter_sample_app/global_variables.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:image_picker/image_picker.dart'; 5 | 6 | class PhotoUpload extends StatelessWidget { 7 | PhotoUpload({@required this.files, @required this.onProgress}); 8 | final List files; 9 | final Function onProgress; 10 | Widget build(BuildContext context) { 11 | return IconButton( 12 | icon: Icon(Icons.camera_alt), 13 | onPressed: () async { 14 | /// Get source of photo 15 | ImageSource source = await showDialog( 16 | context: context, 17 | builder: (_) => AlertDialog( 18 | title: Text('Choose Camera or Gallery'), 19 | content: Column( 20 | mainAxisSize: MainAxisSize.min, 21 | children: [ 22 | TextButton( 23 | onPressed: () => Get.back(result: ImageSource.camera), 24 | child: Text('Camera'), 25 | ), 26 | TextButton( 27 | onPressed: () => Get.back(result: ImageSource.gallery), 28 | child: Text('Gallery'), 29 | ), 30 | ], 31 | ), 32 | ), 33 | ); 34 | 35 | if (source == null) return null; 36 | 37 | /// Upload photo 38 | try { 39 | final url = await ff.uploadFile( 40 | folder: 'forum-photos', 41 | source: source, 42 | progress: onProgress, 43 | ); 44 | 45 | files.add(url); 46 | onProgress(0.0); 47 | } catch (e) { 48 | Get.snackbar('Error', e.toString()); 49 | } 50 | }, 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/screens/forum/post.edit.screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:fireflutter_sample_app/global_variables.dart'; 2 | import 'package:fireflutter_sample_app/screens/forum/edit_photos.dart'; 3 | import 'package:fireflutter_sample_app/screens/forum/photo_upload.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:get/get.dart'; 6 | 7 | class ForumEditScreen extends StatefulWidget { 8 | @override 9 | _ForumEditScreenState createState() => _ForumEditScreenState(); 10 | } 11 | 12 | class _ForumEditScreenState extends State { 13 | /// Text box controllers 14 | final titleController = TextEditingController(); 15 | final contentController = TextEditingController(); 16 | 17 | /// Category ID 18 | String category; 19 | 20 | /// Post to edit 21 | Map post; 22 | 23 | /// Container for uploaded files 24 | List files = []; 25 | 26 | /// Progress bar percentage 27 | double uploadProgress = 0; 28 | 29 | @override 30 | void initState() { 31 | super.initState(); 32 | 33 | /// If `category` is passed, then it is post create. 34 | if (Get.arguments['category'] != null) { 35 | category = Get.arguments['category']; 36 | } else { 37 | /// Or it's post update. 38 | post = Get.arguments['post']; 39 | titleController.text = post['title']; 40 | contentController.text = post['content']; 41 | category = post['category']; 42 | files = post['files'] ?? []; 43 | } 44 | } 45 | 46 | @override 47 | void dispose() { 48 | titleController.dispose(); 49 | contentController.dispose(); 50 | super.dispose(); 51 | } 52 | 53 | bool get formInvalid => false; 54 | 55 | @override 56 | Widget build(BuildContext context) { 57 | return Scaffold( 58 | appBar: AppBar( 59 | title: Text('Forum Edit'), 60 | ), 61 | body: Column( 62 | children: [ 63 | TextFormField( 64 | controller: titleController, 65 | decoration: InputDecoration(hintText: 'title'), 66 | ), 67 | TextFormField( 68 | controller: contentController, 69 | decoration: InputDecoration(hintText: 'content'), 70 | ), 71 | Row( 72 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 73 | children: [ 74 | PhotoUpload( 75 | files: files, 76 | onProgress: (p) => setState(() => uploadProgress = p), 77 | ), 78 | RaisedButton( 79 | onPressed: () async { 80 | if (formInvalid) { 81 | return Get.snackbar('title', 'message'); 82 | } 83 | try { 84 | await ff.editPost({ 85 | 'id': post == null ? null : post['id'], 86 | 'category': category, 87 | 'title': titleController.text, 88 | 'content': contentController.text, 89 | 'uid': ff.user.uid, 90 | 'files': files, 91 | }); 92 | 93 | /// Should go back since new post will be updated in real time. 94 | Get.back(); 95 | } catch (e) { 96 | Get.snackbar('Error', e.toString()); 97 | } 98 | }, 99 | child: Text('submit'.tr), 100 | ), 101 | ], 102 | ), 103 | if (uploadProgress > 0) 104 | LinearProgressIndicator( 105 | value: uploadProgress, 106 | ), 107 | EditPotos( 108 | files: files, 109 | ), 110 | ], 111 | ), 112 | ); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /lib/screens/forum/post.list.screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:fireflutter/fireflutter.dart'; 2 | import 'package:fireflutter_sample_app/global_variables.dart'; 3 | import 'package:fireflutter_sample_app/screens/forum/comment.dart'; 4 | import 'package:fireflutter_sample_app/screens/forum/comment.form.dart'; 5 | import 'package:fireflutter_sample_app/screens/forum/display_photos.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 8 | import 'package:get/get.dart'; 9 | import 'package:cloud_firestore/cloud_firestore.dart'; 10 | 11 | class ForumListScreen extends StatefulWidget { 12 | @override 13 | _ForumListScreenState createState() => _ForumListScreenState(); 14 | } 15 | 16 | class _ForumListScreenState extends State { 17 | String category; 18 | 19 | ForumData forum; 20 | ScrollController scrollController = ScrollController(); 21 | 22 | /// Check if the scroll is at the bottom of the page 23 | bool get atBottom => 24 | scrollController.offset > 25 | (scrollController.position.maxScrollExtent - 200); 26 | 27 | @override 28 | void initState() { 29 | super.initState(); 30 | category = Get.arguments['category']; 31 | forum = ForumData( 32 | category: category, 33 | render: (RenderType x) { 34 | if (mounted) setState(() {}); 35 | }, 36 | ); 37 | 38 | ff.fetchPosts(forum); 39 | scrollController.addListener(onScrollToBottom); 40 | } 41 | 42 | /// 43 | onScrollToBottom() { 44 | if (!atBottom) return; 45 | ff.fetchPosts(forum); 46 | } 47 | 48 | @override 49 | Widget build(BuildContext context) { 50 | return Scaffold( 51 | appBar: AppBar( 52 | title: Text(category), 53 | actions: [ 54 | IconButton( 55 | icon: Icon(Icons.add), 56 | onPressed: () { 57 | if (ff.notLoggedIn) { 58 | Get.snackbar('로그인', '로그인을 먼저 해 주세요.'); 59 | return; 60 | } 61 | if (ff.user.phoneNumber.isNullOrBlank && 62 | ff.appSetting('block-non-verified-users-to-create') == 63 | true) { 64 | Get.defaultDialog( 65 | middleText: 'Please verify your phone number first!', 66 | textConfirm: 'Ok', 67 | confirmTextColor: Colors.white, 68 | onConfirm: () => Get.back(), 69 | ); 70 | } else { 71 | Get.toNamed('forum-edit', arguments: {'category': category}); 72 | } 73 | }), 74 | if (ff.loggedIn) 75 | StreamBuilder( 76 | stream: ff.publicDoc.snapshots(), 77 | builder: (context, AsyncSnapshot snapshot) { 78 | if (snapshot.hasError) return Container(); 79 | if (snapshot.connectionState == ConnectionState.waiting) 80 | return Container(); 81 | print('data: ${snapshot.data.data()}'); 82 | Map data = snapshot.data.data(); 83 | bool postEnabled = 84 | data[NotificationOptions.post(category)] ?? false; 85 | bool commentEnabled = 86 | data[NotificationOptions.comment(category)] ?? false; 87 | return Row(children: [ 88 | IconButton( 89 | icon: FaIcon( 90 | postEnabled 91 | ? FontAwesomeIcons.solidBell 92 | : FontAwesomeIcons.solidBellSlash, 93 | size: 18, 94 | ), 95 | onPressed: () async { 96 | final String topic = NotificationOptions.post(category); 97 | try { 98 | if (postEnabled) { 99 | await ff.unsubscribeTopic(topic); 100 | } else { 101 | await ff.subscribeTopic(topic); 102 | } 103 | await ff.updateUserPublic(topic, !postEnabled); 104 | } catch (e) { 105 | Get.snackbar('Error', e.toString()); 106 | } 107 | }, 108 | ), 109 | IconButton( 110 | icon: FaIcon( 111 | commentEnabled 112 | ? FontAwesomeIcons.solidComment 113 | : FontAwesomeIcons.commentSlash, 114 | size: 18, 115 | ), 116 | onPressed: () async { 117 | final String topic = 118 | NotificationOptions.comment(category); 119 | try { 120 | if (commentEnabled) { 121 | await ff.unsubscribeTopic(topic); 122 | } else { 123 | await ff.subscribeTopic(topic); 124 | } 125 | await ff.updateUserPublic(topic, !commentEnabled); 126 | } catch (e) { 127 | Get.snackbar('Error', e.toString()); 128 | } 129 | }, 130 | ), 131 | ]); 132 | }), 133 | ], 134 | ), 135 | body: Container( 136 | padding: EdgeInsets.all(16), 137 | child: SingleChildScrollView( 138 | controller: scrollController, 139 | child: Column( 140 | children: [ 141 | ListView.builder( 142 | physics: NeverScrollableScrollPhysics(), 143 | shrinkWrap: true, 144 | itemCount: forum.posts.length, 145 | itemBuilder: (context, i) { 146 | Map post = forum.posts[i]; 147 | return Column( 148 | crossAxisAlignment: CrossAxisAlignment.start, 149 | children: [ 150 | PostSubject(post: post), 151 | PostContent(post: post), 152 | DisplayPhotos(files: post['files']), 153 | PostButtons(post: post), 154 | Divider(), 155 | CommentForm(post: post), 156 | if (post['comments'] != null) 157 | for (int j = 0; j < post['comments'].length; j++) 158 | Comment(post: post, index: j), 159 | ], 160 | ); 161 | }, 162 | ), 163 | if (forum.inLoading) CircularProgressIndicator(), 164 | if (forum.status == ForumStatus.noPosts) 165 | Text('No post exists in this forum.'), 166 | if (forum.status == ForumStatus.noMorePosts) 167 | Text('There is no more post.'), 168 | ], 169 | ), 170 | ), 171 | ), 172 | ); 173 | } 174 | } 175 | 176 | class PostButtons extends StatelessWidget { 177 | const PostButtons({ 178 | Key key, 179 | @required this.post, 180 | }) : super(key: key); 181 | 182 | final Map post; 183 | 184 | @override 185 | Widget build(BuildContext context) { 186 | return Row( 187 | children: [ 188 | PostEditButton(post: post), 189 | RaisedButton( 190 | onPressed: () async { 191 | try { 192 | await ff.deletePost(post['id']); 193 | } catch (e) { 194 | Get.snackbar('Error', e.toString()); 195 | } 196 | }, 197 | child: Text('Delete'), 198 | ), 199 | if (ff.voteSetting(post['category'], VoteChoice.like)) 200 | RaisedButton( 201 | onPressed: () async { 202 | try { 203 | await ff.vote( 204 | postId: post['id'], 205 | choice: VoteChoice.like, 206 | ); 207 | } catch (e) { 208 | Get.snackbar('Error', e.toString()); 209 | } 210 | }, 211 | child: Text('Like ${post['likes'] ?? ''}'), 212 | ), 213 | if (ff.voteSetting(post['category'], VoteChoice.dislike)) 214 | TextButton( 215 | onPressed: () async { 216 | try { 217 | await ff.vote( 218 | postId: post['id'], 219 | choice: VoteChoice.dislike, 220 | ); 221 | } catch (e) { 222 | Get.snackbar('Error', e.toString()); 223 | } 224 | }, 225 | child: Text('Dislike ${post['dislikes'] ?? ''}'), 226 | ), 227 | ], 228 | ); 229 | } 230 | } 231 | 232 | class PostEditButton extends StatelessWidget { 233 | const PostEditButton({ 234 | Key key, 235 | @required this.post, 236 | }) : super(key: key); 237 | 238 | final Map post; 239 | 240 | @override 241 | Widget build(BuildContext context) { 242 | return RaisedButton( 243 | onPressed: () => Get.toNamed( 244 | 'forum-edit', 245 | arguments: {'post': post}, 246 | ), 247 | child: Text('Edit'), 248 | ); 249 | } 250 | } 251 | 252 | class PostContent extends StatelessWidget { 253 | const PostContent({ 254 | Key key, 255 | @required this.post, 256 | }) : super(key: key); 257 | 258 | final Map post; 259 | 260 | @override 261 | Widget build(BuildContext context) { 262 | return Container( 263 | child: Text(post['content']), 264 | color: Colors.grey[200], 265 | margin: EdgeInsets.only(top: 16), 266 | padding: EdgeInsets.all(16), 267 | width: double.infinity, 268 | ); 269 | } 270 | } 271 | 272 | class PostSubject extends StatelessWidget { 273 | const PostSubject({ 274 | Key key, 275 | @required this.post, 276 | }) : super(key: key); 277 | 278 | final Map post; 279 | 280 | @override 281 | Widget build(BuildContext context) { 282 | return Column( 283 | crossAxisAlignment: CrossAxisAlignment.start, 284 | children: [ 285 | Text( 286 | post['title'], 287 | style: TextStyle(fontSize: 22), 288 | ), 289 | Text( 290 | post['id'], 291 | style: TextStyle(fontSize: 10), 292 | ), 293 | ], 294 | ); 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /lib/screens/home/home.screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:fireflutter_sample_app/global_variables.dart'; 2 | import 'package:fireflutter_sample_app/keys.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:get/get.dart'; 5 | 6 | class HomeScreen extends StatefulWidget { 7 | @override 8 | _HomeScreenState createState() => _HomeScreenState(); 9 | } 10 | 11 | class _HomeScreenState extends State { 12 | @override 13 | void initState() { 14 | super.initState(); 15 | } 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | key: ValueKey(Keys.homeScreen), 21 | appBar: AppBar( 22 | title: Text('app-name'.tr), 23 | ), 24 | body: Column( 25 | children: [ 26 | Text('User Information'), 27 | StreamBuilder( 28 | stream: ff.userChange, 29 | builder: (context, snapshot) { 30 | if (ff.loggedIn) { 31 | return Text( 32 | 'Email: ${ff.user.email}, Color: ${ff.userData['favoriteColor']}, UID: ${ff.user.uid}, displayName: ${ff.user.displayName}, Phone: ${ff.user.phoneNumber},', 33 | key: ValueKey(Keys.hInfo), 34 | ); 35 | } else { 36 | return Text( 37 | 'Please login', 38 | key: ValueKey(Keys.hPleaseLogin), 39 | ); 40 | } 41 | }), 42 | Divider(), 43 | Text('User Buttons'), 44 | Wrap( 45 | children: [ 46 | RaisedButton( 47 | key: ValueKey(Keys.rButton), 48 | onPressed: () => Get.toNamed('register'), 49 | child: Text('Register'), 50 | ), 51 | RaisedButton( 52 | key: ValueKey(Keys.hLoginButton), 53 | onPressed: () => Get.toNamed('login'), 54 | child: Text('Login'), 55 | ), 56 | RaisedButton( 57 | key: ValueKey(Keys.hProfileButotn), 58 | onPressed: () => Get.toNamed('profile'), 59 | child: Text('Profile'), 60 | ), 61 | RaisedButton( 62 | key: ValueKey(Keys.hLogoutButton), 63 | onPressed: ff.logout, 64 | child: Text('Logout'), 65 | ), 66 | RaisedButton( 67 | onPressed: () => Get.toNamed('phone-auth'), 68 | child: Text('Phone Verificatoin'), 69 | ), 70 | RaisedButton( 71 | onPressed: () => Get.toNamed('settings'), 72 | child: Text('Settings'), 73 | ), 74 | DropdownButton( 75 | value: ff.userLanguage, 76 | items: [ 77 | DropdownMenuItem(value: 'ko', child: Text('Korean')), 78 | DropdownMenuItem(value: 'en', child: Text('English')), 79 | ], 80 | onChanged: (String value) { 81 | ff.updateProfile({'language': value}); 82 | }, 83 | ), 84 | ], 85 | ), 86 | if (ff.isAdmin) ...[ 87 | Divider(), 88 | RaisedButton( 89 | onPressed: () => Get.toNamed('admin'), 90 | child: Text('Admin Screen'), 91 | ), 92 | ], 93 | Divider(), 94 | Wrap( 95 | children: [ 96 | RaisedButton( 97 | onPressed: () => 98 | Get.toNamed('forum-edit', arguments: {'category': 'qna'}), 99 | child: Text('Create a Post'), 100 | ), 101 | RaisedButton( 102 | onPressed: () => 103 | Get.toNamed('forum-list', arguments: {'category': 'qna'}), 104 | child: Text('QnA Forum'), 105 | ), 106 | RaisedButton( 107 | onPressed: () => Get.toNamed('forum-list', 108 | arguments: {'category': 'discussion'}), 109 | child: Text('Discussion Forum'), 110 | ), 111 | RaisedButton( 112 | onPressed: () => Get.toNamed('search'), 113 | child: Text('Search'), 114 | ), 115 | ], 116 | ), 117 | Divider(), 118 | RaisedButton( 119 | onPressed: () => Get.toNamed('push-notification'), 120 | child: Text('Push Notification'), 121 | ), 122 | ], 123 | ), 124 | ); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /lib/screens/login/login.screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:fireflutter_sample_app/keys.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:sign_in_with_apple/sign_in_with_apple.dart'; 5 | import '../../global_variables.dart'; 6 | 7 | class LoginScreen extends StatefulWidget { 8 | @override 9 | _RegisterScreenState createState() => _RegisterScreenState(); 10 | } 11 | 12 | class _RegisterScreenState extends State { 13 | final emailController = TextEditingController(); 14 | final passwordController = TextEditingController(); 15 | bool loading = false; 16 | @override 17 | Widget build(BuildContext context) { 18 | return Scaffold( 19 | appBar: AppBar( 20 | title: Text('LoginScreen'), 21 | ), 22 | body: Column( 23 | children: [ 24 | Text('Social Login'), 25 | Divider(), 26 | RaisedButton( 27 | child: Text('Google Sign-in'), 28 | onPressed: () async { 29 | try { 30 | await ff.signInWithGoogle(); 31 | Get.toNamed('home'); 32 | } catch (e) { 33 | Get.snackbar('Error', e.toString()); 34 | } 35 | }, 36 | ), 37 | RaisedButton( 38 | child: Text('Facebook Sign-in'), 39 | onPressed: () async { 40 | try { 41 | await ff.signInWithFacebook(); 42 | Get.toNamed('home'); 43 | } catch (e) { 44 | Get.snackbar('Error', e.toString()); 45 | } 46 | }, 47 | ), 48 | if (GetPlatform.isIOS) 49 | SignInWithAppleButton( 50 | onPressed: () async { 51 | try { 52 | await ff.signInWithApple(); 53 | Get.toNamed('home'); 54 | } catch (e) { 55 | Get.snackbar('Error', e.toString()); 56 | } 57 | }, 58 | ), 59 | SizedBox( 60 | height: 64, 61 | ), 62 | Text('Eamil & Password Login'), 63 | Divider(), 64 | TextFormField( 65 | key: ValueKey(Keys.lfEmail), 66 | controller: emailController, 67 | decoration: InputDecoration(hintText: 'Email address'), 68 | ), 69 | TextFormField( 70 | key: ValueKey(Keys.lfPassword), 71 | controller: passwordController, 72 | decoration: InputDecoration(hintText: 'Password'), 73 | ), 74 | RaisedButton( 75 | key: ValueKey(Keys.lfSubmitButton), 76 | onPressed: () async { 77 | setState(() => loading = true); 78 | try { 79 | await ff.login( 80 | email: emailController.text, 81 | password: passwordController.text, 82 | ); 83 | if (ff.user.phoneNumber.isNullOrBlank && 84 | ff.appSetting('verify-after-login') == true) { 85 | Get.toNamed('phone-auth'); 86 | } else { 87 | Get.toNamed('home'); 88 | } 89 | } catch (e) { 90 | Get.snackbar('Error', e.toString()); 91 | } finally { 92 | setState(() => loading = false); 93 | } 94 | }, 95 | child: loading ? CircularProgressIndicator() : Text('Login'), 96 | ), 97 | ], 98 | ), 99 | ); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /lib/screens/phone_auth/phone_auth.screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:country_code_picker/country_code_picker.dart'; 2 | import 'package:fireflutter_sample_app/global_variables.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:get/get.dart'; 5 | 6 | class PhoneAuthScreen extends StatefulWidget { 7 | @override 8 | _PhoneAuthScreenState createState() => _PhoneAuthScreenState(); 9 | } 10 | 11 | class _PhoneAuthScreenState extends State { 12 | final mobileNumberController = TextEditingController(); 13 | String countryCode = "+82"; 14 | bool loading = false; 15 | 16 | String get internationalNo => countryCode + mobileNumberController.text; 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: AppBar( 21 | title: Text('Phone Verification'), 22 | ), 23 | body: Padding( 24 | padding: const EdgeInsets.all(16.0), 25 | child: Column( 26 | crossAxisAlignment: CrossAxisAlignment.stretch, 27 | children: [ 28 | Text( 29 | 'Choose your country', 30 | style: TextStyle(fontSize: 10), 31 | ), 32 | CountryCodePicker( 33 | onChanged: (country) => countryCode = country.dialCode, 34 | initialSelection: 'KR', 35 | favorite: ['KR', 'PH', 'JP', 'ZH'], 36 | showCountryOnly: false, 37 | showOnlyCountryWhenClosed: true, 38 | alignLeft: true, 39 | ), 40 | Text( 41 | 'Input your mobile number', 42 | style: TextStyle(fontSize: 10), 43 | ), 44 | TextFormField( 45 | controller: mobileNumberController, 46 | keyboardType: TextInputType.number, 47 | style: TextStyle( 48 | fontSize: 32, 49 | ), 50 | decoration: InputDecoration( 51 | hintText: '000-000-000', 52 | hintStyle: TextStyle( 53 | fontSize: 32, 54 | color: Color(0x95959599), 55 | ), 56 | ), 57 | ), 58 | RaisedButton( 59 | onPressed: () async { 60 | if (loading) return; 61 | setState(() => loading = true); 62 | 63 | /// TODO: validate phone number format. 64 | print('no: $internationalNo'); 65 | ff.mobileAuthSendCode(internationalNo, 66 | onCodeSent: (verificationID, codeResendToken) { 67 | setState(() => loading = false); 68 | Get.toNamed( 69 | 'phone-auth-code-verification', 70 | arguments: { 71 | 'verificationID': verificationID, 72 | 'codeResendToken': codeResendToken, 73 | 'internationalNo': internationalNo 74 | }, 75 | ); 76 | }, onError: (e) { 77 | setState(() => loading = false); 78 | Get.snackbar('Error', e.toString()); 79 | }); 80 | }, 81 | child: loading ? CircularProgressIndicator() : Text('SEND CODE'), 82 | ), 83 | if (ff.appSetting('force-verification') != true) 84 | RaisedButton( 85 | onPressed: () => Get.toNamed('home'), 86 | child: Text('SKIP'), 87 | ) 88 | ], 89 | ), 90 | ), 91 | ); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lib/screens/phone_auth/phone_auth_verification_code.screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:fireflutter_sample_app/global_variables.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | class PhoneAuthCodeVerificationScreen extends StatefulWidget { 6 | @override 7 | _PhoneAuthCodeVerificationScreenState createState() => 8 | _PhoneAuthCodeVerificationScreenState(); 9 | } 10 | 11 | class _PhoneAuthCodeVerificationScreenState 12 | extends State { 13 | final codeController = TextEditingController(); 14 | bool loading = false; 15 | 16 | String verificationID; 17 | int codeResendToken; 18 | String internationalNo; 19 | 20 | @override 21 | void initState() { 22 | dynamic args = Get.arguments; 23 | verificationID = args['verificationID']; 24 | internationalNo = args['internationalNo']; 25 | codeResendToken = args['codeResendToken']; 26 | super.initState(); 27 | } 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return Scaffold( 32 | appBar: AppBar( 33 | title: Text('Phone Auth Code Verification'), 34 | ), 35 | body: Column( 36 | children: [ 37 | Text('Enter code'), 38 | TextFormField(controller: codeController), 39 | RaisedButton( 40 | child: Text( 41 | "VERIFY", 42 | ), 43 | onPressed: () async { 44 | if (loading) return; 45 | setState(() => loading = true); 46 | try { 47 | await ff.mobileAuthVerifyCode( 48 | code: codeController.text, 49 | verificationId: verificationID, 50 | ); 51 | setState(() => loading = false); 52 | Get.toNamed('home'); 53 | } catch (e) { 54 | setState(() => loading = false); 55 | Get.snackbar('Error', e.toString()); 56 | } 57 | }, 58 | ), 59 | 60 | // change number & resend code button. 61 | Row( 62 | children: [ 63 | FlatButton( 64 | padding: EdgeInsets.all(0), 65 | child: Text( 66 | 'Resend Code'.tr, 67 | style: TextStyle( 68 | color: Color(0xFF032674), 69 | fontWeight: FontWeight.w400, 70 | ), 71 | ), 72 | onPressed: () { 73 | ff.mobileAuthSendCode( 74 | internationalNo, 75 | resendToken: codeResendToken, 76 | onCodeSent: (_verificationID, resendToken) { 77 | setState(() { 78 | verificationID = _verificationID; 79 | codeResendToken = resendToken; 80 | }); 81 | }, 82 | onError: (e) => Get.snackbar('Error', e.toString()), 83 | ); 84 | }, 85 | ), 86 | Spacer(), 87 | FlatButton( 88 | padding: EdgeInsets.all(0), 89 | child: Text( 90 | 'Change Number'.tr, 91 | style: TextStyle( 92 | color: Color(0xFF032674), 93 | fontWeight: FontWeight.w400, 94 | ), 95 | ), 96 | onPressed: () { 97 | Get.back(); 98 | }, 99 | ), 100 | ], 101 | ), 102 | ], 103 | ), 104 | ); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /lib/screens/profile/profile.screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:fireflutter/fireflutter.dart'; 2 | import 'package:fireflutter_sample_app/global_variables.dart'; 3 | import 'package:fireflutter_sample_app/keys.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | import 'package:get/get.dart'; 7 | import 'package:image_picker/image_picker.dart'; 8 | 9 | class ProfileScreen extends StatefulWidget { 10 | @override 11 | _RegisterScreenState createState() => _RegisterScreenState(); 12 | } 13 | 14 | class _RegisterScreenState extends State { 15 | TextEditingController displayNameController = 16 | TextEditingController(text: ff.user?.displayName ?? ''); 17 | TextEditingController favoriteColorController = 18 | TextEditingController(text: ff.userData['favoriteColor']); 19 | 20 | bool loading = false; 21 | double uploadProgress = 0; 22 | @override 23 | Widget build(BuildContext context) { 24 | return Scaffold( 25 | appBar: AppBar( 26 | title: Text('ProfileScreen'), 27 | ), 28 | body: ff.notLoggedIn 29 | ? Text('Please login') 30 | : Column( 31 | children: [ 32 | StreamBuilder( 33 | stream: ff.userChange, 34 | builder: (context, snapshot) { 35 | if (ff.notLoggedIn || ff.user.photoURL == null) 36 | return Container(); 37 | return SizedBox( 38 | width: 120, 39 | height: 120, 40 | child: Image.network(ff.user.photoURL)); 41 | }), 42 | RaisedButton( 43 | onPressed: () async { 44 | ImageSource source = await showDialog( 45 | context: context, 46 | builder: (_) => AlertDialog( 47 | title: Text('Choose Camera or Gallery'), 48 | content: Column( 49 | mainAxisSize: MainAxisSize.min, 50 | children: [ 51 | TextButton( 52 | onPressed: () => 53 | Get.back(result: ImageSource.camera), 54 | child: Text('Camera'), 55 | ), 56 | TextButton( 57 | onPressed: () => 58 | Get.back(result: ImageSource.gallery), 59 | child: Text('Gallery'), 60 | ), 61 | ], 62 | ), 63 | ), 64 | ); 65 | 66 | if (source == null) return null; 67 | 68 | try { 69 | if (!ff.user.photoURL.isNullOrBlank) { 70 | await ff.deleteFile(ff.user.photoURL); 71 | } 72 | final url = await ff.uploadFile( 73 | folder: 'user-profile-photos', 74 | source: source, 75 | progress: (p) => 76 | setState(() => this.uploadProgress = p), 77 | ); 78 | await ff.updatePhoto(url); 79 | setState(() => uploadProgress = 0); 80 | } catch (e) { 81 | Get.snackbar('Error', e.toString()); 82 | } 83 | }, 84 | child: Text('Upload Profile Photo'), 85 | ), 86 | if (ff.user.photoURL.isNullOrBlank) 87 | RaisedButton( 88 | onPressed: () async { 89 | if (!ff.user.photoURL.isNullOrBlank) { 90 | await ff.deleteFile(ff.user.photoURL); 91 | await ff.updatePhoto(''); 92 | } 93 | }, 94 | child: Text('Delete Profile Photo')), 95 | if (uploadProgress != 0) Text('$uploadProgress%'), 96 | Text('My Email: ${ff.user.email}'), 97 | TextFormField( 98 | key: ValueKey(Keys.pfDisplayName), 99 | controller: displayNameController, 100 | decoration: InputDecoration(hintText: 'displayName'), 101 | ), 102 | TextFormField( 103 | key: ValueKey(Keys.pfColor), 104 | controller: favoriteColorController, 105 | decoration: 106 | InputDecoration(hintText: 'What is your favorite color?'), 107 | ), 108 | RaisedButton( 109 | key: ValueKey(Keys.pfSubmitButton), 110 | onPressed: () async { 111 | setState(() => loading = true); 112 | try { 113 | await ff.updateProfile({ 114 | 'displayName': displayNameController.text, 115 | 'favoriteColor': favoriteColorController.text 116 | }, public: { 117 | notifyPost: true, 118 | notifyComment: true, 119 | }); 120 | setState(() => loading = false); 121 | 122 | Get.snackbar('Success', 'Profile has been updated!'); 123 | } catch (e) { 124 | setState(() => loading = false); 125 | Get.snackbar('Error', e.toString()); 126 | } 127 | }, 128 | child: loading ? CircularProgressIndicator() : Text('Update'), 129 | ), 130 | ], 131 | ), 132 | ); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /lib/screens/push-notification/push-notification.screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:fireflutter_sample_app/global_variables.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | class PushNotification extends StatefulWidget { 6 | @override 7 | _PushNotificationState createState() => _PushNotificationState(); 8 | } 9 | 10 | class _PushNotificationState extends State { 11 | @override 12 | Widget build(BuildContext context) { 13 | return Scaffold( 14 | appBar: AppBar( 15 | title: Text('Push Notification'.tr), 16 | ), 17 | body: Column( 18 | children: [ 19 | StreamBuilder( 20 | stream: ff.userChange, 21 | builder: (context, snapshot) { 22 | if (ff.userIsLoggedIn) { 23 | return Text( 24 | 'Email: ${ff.user.email}, displayName: ${ff.user.displayName}'); 25 | } else { 26 | return Text('You are not logged in.'); 27 | } 28 | }), 29 | Divider(), 30 | Text('Device Token: '), 31 | Text(ff.firebaseMessagingToken), 32 | RaisedButton( 33 | onPressed: () async { 34 | ff.sendNotification( 35 | 'Sample push notification to topic', 36 | 'This is the content of push to topic', 37 | screen: 'home', 38 | topic: ff.allTopic, 39 | test: true, 40 | ); 41 | }, 42 | child: Text('Send notification to all uers(allTopic)'), 43 | ), 44 | RaisedButton( 45 | onPressed: () async { 46 | ff.sendNotification( 47 | 'Sample push notification to me', 48 | 'This is the content of push to token', 49 | screen: 'home', 50 | token: ff.firebaseMessagingToken, 51 | test: true, 52 | ); 53 | }, 54 | child: Text('Send notification to me(my token)'), 55 | ), 56 | RaisedButton( 57 | onPressed: () async { 58 | ff.sendNotification( 59 | 'Sample push notification to my devices', 60 | 'This is the content of push to tokens', 61 | screen: 'home', 62 | tokens: [ff.firebaseMessagingToken], 63 | test: true, 64 | ); 65 | }, 66 | child: Text('Send notification to my devices(multi tokens)'), 67 | ), 68 | ], 69 | ), 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/screens/register/register.screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:fireflutter/fireflutter.dart'; 2 | import 'package:fireflutter_sample_app/global_variables.dart'; 3 | import 'package:fireflutter_sample_app/keys.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:get/get.dart'; 6 | 7 | class RegisterScreen extends StatefulWidget { 8 | @override 9 | _RegisterScreenState createState() => _RegisterScreenState(); 10 | } 11 | 12 | class _RegisterScreenState extends State { 13 | TextEditingController emailController = TextEditingController(); 14 | TextEditingController passwordController = TextEditingController(); 15 | TextEditingController displayNameController = TextEditingController(); 16 | TextEditingController favoriteColorController = TextEditingController(); 17 | 18 | bool loading = false; 19 | @override 20 | Widget build(BuildContext context) { 21 | return Scaffold( 22 | key: ValueKey('registerScreen'), 23 | appBar: AppBar( 24 | title: Text('Register'), 25 | ), 26 | body: Column( 27 | children: [ 28 | TextFormField( 29 | key: ValueKey(Keys.riEmail), 30 | controller: emailController, 31 | decoration: InputDecoration(hintText: 'Email Address'), 32 | ), 33 | TextFormField( 34 | key: ValueKey(Keys.riPassword), 35 | controller: passwordController, 36 | decoration: InputDecoration(hintText: 'Password'), 37 | ), 38 | TextFormField( 39 | key: ValueKey(Keys.riDisplayName), 40 | controller: displayNameController, 41 | decoration: InputDecoration(hintText: 'displayName'), 42 | ), 43 | TextFormField( 44 | key: ValueKey(Keys.riColor), 45 | controller: favoriteColorController, 46 | decoration: 47 | InputDecoration(hintText: 'What is your favorite color?'), 48 | ), 49 | RaisedButton( 50 | key: ValueKey(Keys.rsButton), 51 | onPressed: () async { 52 | setState(() => loading = true); 53 | try { 54 | await ff.register({ 55 | 'email': emailController.text, 56 | 'password': passwordController.text, 57 | 'displayName': displayNameController.text, 58 | 'favoriteColor': favoriteColorController.text 59 | }, public: { 60 | notifyPost: true, 61 | notifyComment: true, 62 | }); 63 | 64 | setState(() => loading = false); 65 | if (ff.appSetting('verify-after-register') == true) { 66 | Get.toNamed('phone-auth'); 67 | } else { 68 | Get.toNamed('home'); 69 | } 70 | } catch (e) { 71 | setState(() => loading = false); 72 | Get.snackbar('Error', e.toString()); 73 | } 74 | }, 75 | child: loading ? CircularProgressIndicator() : Text('Register'), 76 | ), 77 | ], 78 | ), 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /lib/screens/search/search.screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:fireflutter_sample_app/global_variables.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | class SearchScreen extends StatefulWidget { 6 | @override 7 | _SearchScreenState createState() => _SearchScreenState(); 8 | } 9 | 10 | class _SearchScreenState extends State { 11 | TextEditingController searchController = TextEditingController(); 12 | List> results = []; 13 | 14 | ScrollController scrollController = 15 | ScrollController(initialScrollOffset: 0.0, keepScrollOffset: true); 16 | 17 | /// TODO make it as settings 18 | int hitsPerPage = 15; 19 | 20 | int pageNo = 0; 21 | 22 | bool loading = false; 23 | bool noMorePosts = false; 24 | @override 25 | void initState() { 26 | super.initState(); 27 | 28 | scrollController.addListener(() { 29 | var isEnd = scrollController.offset > 30 | (scrollController.position.maxScrollExtent - 200); 31 | if (isEnd) { 32 | search(); 33 | } 34 | }); 35 | } 36 | 37 | search() async { 38 | if (loading || noMorePosts) return; 39 | loading = true; 40 | 41 | List hits; 42 | try { 43 | hits = await ff.search(searchController.text, 44 | hitsPerPage: hitsPerPage, pageNo: pageNo); 45 | } catch (e) { 46 | Get.snackbar('Error', e.toString()); 47 | return; 48 | } 49 | if (hits == null || hits.length < hitsPerPage) { 50 | noMorePosts = true; 51 | } 52 | results = [...results, ...hits]; 53 | loading = false; 54 | pageNo++; 55 | setState(() {}); 56 | } 57 | 58 | @override 59 | Widget build(BuildContext context) { 60 | return Scaffold( 61 | appBar: AppBar( 62 | title: Text('Search'), 63 | ), 64 | body: SingleChildScrollView( 65 | controller: scrollController, 66 | child: Container( 67 | padding: EdgeInsets.all(16), 68 | child: Column( 69 | children: [ 70 | TextFormField( 71 | controller: searchController, 72 | ), 73 | RaisedButton( 74 | onPressed: () { 75 | pageNo = 0; 76 | results = []; 77 | noMorePosts = false; 78 | loading = false; 79 | search(); 80 | }, 81 | child: Text('Search'), 82 | ), 83 | ListView.builder( 84 | physics: NeverScrollableScrollPhysics(), 85 | shrinkWrap: true, 86 | itemCount: results.length, 87 | itemBuilder: (context, i) { 88 | return Column( 89 | children: [ 90 | ListTile( 91 | title: Text(results[i]['title'] ?? ''), 92 | subtitle: Text(results[i]['content'] ?? ''), 93 | onTap: () { 94 | String path = results[i]['path']; 95 | if (path == null) { 96 | Get.snackbar('Error', 'path does not exists'.tr); 97 | } 98 | 99 | String postId = path.split('/')[1]; 100 | 101 | print('postid: $postId from $path'); 102 | 103 | Get.snackbar('Post view page', postId); 104 | }, 105 | ), 106 | Divider(), 107 | ], 108 | ); 109 | }, 110 | ), 111 | if (loading) CircularProgressIndicator(), 112 | if (noMorePosts) 113 | Padding( 114 | padding: EdgeInsets.all(32), 115 | child: Text('No more posts'.tr), 116 | ), 117 | ], 118 | ), 119 | ), 120 | ), 121 | ); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /lib/screens/settings/settings.screen.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:fireflutter/fireflutter.dart'; 4 | import 'package:fireflutter_sample_app/global_variables.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:get/get.dart'; 7 | 8 | class SettingsScreen extends StatefulWidget { 9 | @override 10 | _SettingsScreenState createState() => _SettingsScreenState(); 11 | } 12 | 13 | class _SettingsScreenState extends State { 14 | bool loadingFirebase = true; 15 | StreamSubscription firebaseSubscription; 16 | Map public; 17 | @override 18 | void initState() { 19 | super.initState(); 20 | 21 | firebaseSubscription = ff.firebaseInitialized.listen((re) async { 22 | public = await ff.userPublicData(); 23 | setState(() => loadingFirebase = false); 24 | }); 25 | } 26 | 27 | @override 28 | void dispose() { 29 | super.dispose(); 30 | firebaseSubscription.cancel(); 31 | } 32 | 33 | @override 34 | Widget build(BuildContext context) { 35 | return Scaffold( 36 | appBar: AppBar( 37 | title: Text('Settings'), 38 | ), 39 | body: Column( 40 | crossAxisAlignment: CrossAxisAlignment.start, 41 | children: [ 42 | Text('Choose your languages:'), 43 | DropdownButton( 44 | value: ff.userLanguage, 45 | items: [ 46 | DropdownMenuItem(value: 'ko', child: Text('Korean')), 47 | DropdownMenuItem(value: 'en', child: Text('English')), 48 | ], 49 | onChanged: (String value) { 50 | ff.updateProfile({'language': value}); 51 | }, 52 | ), 53 | Text('Notify me new comments under my post'), 54 | loadingFirebase 55 | ? CircularProgressIndicator() 56 | : Switch( 57 | value: public[notifyPost] ?? false, 58 | onChanged: (value) async { 59 | try { 60 | /// @attention update screen first, then save it firestore later. 61 | setState(() => public[notifyPost] = value); 62 | ff.updateUserPublic(notifyPost, value); 63 | Get.snackbar('Update', 'Settings updated!'); 64 | } catch (e) { 65 | Get.snackbar('Error', e.toString()); 66 | } 67 | }, 68 | ), 69 | Text('Notify me new comments under my comments'), 70 | loadingFirebase 71 | ? CircularProgressIndicator() 72 | : Switch( 73 | value: public[notifyComment] ?? false, 74 | onChanged: (value) async { 75 | try { 76 | /// @attention update screen first, then save it firestore later. 77 | setState(() => public[notifyComment] = value); 78 | ff.updateUserPublic(notifyComment, value); 79 | Get.snackbar('Update', 'Settings updated!'); 80 | } catch (e) { 81 | Get.snackbar('Error', e.toString()); 82 | } 83 | }, 84 | ), 85 | ], 86 | ), 87 | ); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/translations.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | /// Default translated texts. 4 | /// 5 | /// This will be available immediately after app boots before downloading from 6 | /// Firestore. 7 | Map> translations = { 8 | "en": { 9 | "app-name": "App Name", 10 | "home": "Home", 11 | }, 12 | "ko": { 13 | "app-name": "앱 이름", 14 | "home": "홈", 15 | } 16 | }; 17 | 18 | /// Update translation document data from Firestore into `GetX locale format`. 19 | updateTranslations(Map data) { 20 | data.forEach((ln, texts) { 21 | for (var name in texts.keys) { 22 | translations[ln][name] = texts[name]; 23 | } 24 | }); 25 | } 26 | 27 | /// GetX locale text translations. 28 | class AppTranslations extends Translations { 29 | @override 30 | Map> get keys => translations; 31 | } 32 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _fe_analyzer_shared: 5 | dependency: transitive 6 | description: 7 | name: _fe_analyzer_shared 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "12.0.0" 11 | algolia: 12 | dependency: transitive 13 | description: 14 | name: algolia 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "0.1.7" 18 | analyzer: 19 | dependency: transitive 20 | description: 21 | name: analyzer 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "0.40.6" 25 | archive: 26 | dependency: transitive 27 | description: 28 | name: archive 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "2.0.13" 32 | args: 33 | dependency: transitive 34 | description: 35 | name: args 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.6.0" 39 | async: 40 | dependency: transitive 41 | description: 42 | name: async 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "2.5.0-nullsafety.1" 46 | boolean_selector: 47 | dependency: transitive 48 | description: 49 | name: boolean_selector 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "2.1.0-nullsafety.1" 53 | characters: 54 | dependency: transitive 55 | description: 56 | name: characters 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.1.0-nullsafety.3" 60 | charcode: 61 | dependency: transitive 62 | description: 63 | name: charcode 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "1.2.0-nullsafety.1" 67 | cli_util: 68 | dependency: transitive 69 | description: 70 | name: cli_util 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "0.2.0" 74 | clock: 75 | dependency: transitive 76 | description: 77 | name: clock 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "1.1.0-nullsafety.1" 81 | cloud_firestore: 82 | dependency: transitive 83 | description: 84 | name: cloud_firestore 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "0.14.1+3" 88 | cloud_firestore_platform_interface: 89 | dependency: transitive 90 | description: 91 | name: cloud_firestore_platform_interface 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "2.1.2" 95 | cloud_firestore_web: 96 | dependency: transitive 97 | description: 98 | name: cloud_firestore_web 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "0.2.0+4" 102 | collection: 103 | dependency: transitive 104 | description: 105 | name: collection 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "1.15.0-nullsafety.3" 109 | convert: 110 | dependency: transitive 111 | description: 112 | name: convert 113 | url: "https://pub.dartlang.org" 114 | source: hosted 115 | version: "2.1.1" 116 | country_code_picker: 117 | dependency: "direct main" 118 | description: 119 | name: country_code_picker 120 | url: "https://pub.dartlang.org" 121 | source: hosted 122 | version: "1.6.2" 123 | coverage: 124 | dependency: transitive 125 | description: 126 | name: coverage 127 | url: "https://pub.dartlang.org" 128 | source: hosted 129 | version: "0.14.2" 130 | crypto: 131 | dependency: transitive 132 | description: 133 | name: crypto 134 | url: "https://pub.dartlang.org" 135 | source: hosted 136 | version: "2.1.5" 137 | cupertino_icons: 138 | dependency: "direct main" 139 | description: 140 | name: cupertino_icons 141 | url: "https://pub.dartlang.org" 142 | source: hosted 143 | version: "1.0.0" 144 | dio: 145 | dependency: transitive 146 | description: 147 | name: dio 148 | url: "https://pub.dartlang.org" 149 | source: hosted 150 | version: "3.0.10" 151 | fake_async: 152 | dependency: transitive 153 | description: 154 | name: fake_async 155 | url: "https://pub.dartlang.org" 156 | source: hosted 157 | version: "1.2.0-nullsafety.1" 158 | faker: 159 | dependency: "direct main" 160 | description: 161 | name: faker 162 | url: "https://pub.dartlang.org" 163 | source: hosted 164 | version: "1.2.1" 165 | ffi: 166 | dependency: transitive 167 | description: 168 | name: ffi 169 | url: "https://pub.dartlang.org" 170 | source: hosted 171 | version: "0.1.3" 172 | file: 173 | dependency: transitive 174 | description: 175 | name: file 176 | url: "https://pub.dartlang.org" 177 | source: hosted 178 | version: "6.0.0-nullsafety.2" 179 | firebase: 180 | dependency: transitive 181 | description: 182 | name: firebase 183 | url: "https://pub.dartlang.org" 184 | source: hosted 185 | version: "7.3.2" 186 | firebase_auth: 187 | dependency: transitive 188 | description: 189 | name: firebase_auth 190 | url: "https://pub.dartlang.org" 191 | source: hosted 192 | version: "0.18.1+2" 193 | firebase_auth_platform_interface: 194 | dependency: transitive 195 | description: 196 | name: firebase_auth_platform_interface 197 | url: "https://pub.dartlang.org" 198 | source: hosted 199 | version: "2.1.1" 200 | firebase_auth_web: 201 | dependency: transitive 202 | description: 203 | name: firebase_auth_web 204 | url: "https://pub.dartlang.org" 205 | source: hosted 206 | version: "0.3.1+1" 207 | firebase_core: 208 | dependency: transitive 209 | description: 210 | name: firebase_core 211 | url: "https://pub.dartlang.org" 212 | source: hosted 213 | version: "0.5.0+1" 214 | firebase_core_platform_interface: 215 | dependency: transitive 216 | description: 217 | name: firebase_core_platform_interface 218 | url: "https://pub.dartlang.org" 219 | source: hosted 220 | version: "2.0.0" 221 | firebase_core_web: 222 | dependency: transitive 223 | description: 224 | name: firebase_core_web 225 | url: "https://pub.dartlang.org" 226 | source: hosted 227 | version: "0.2.1" 228 | firebase_messaging: 229 | dependency: transitive 230 | description: 231 | name: firebase_messaging 232 | url: "https://pub.dartlang.org" 233 | source: hosted 234 | version: "7.0.3" 235 | firebase_storage: 236 | dependency: transitive 237 | description: 238 | name: firebase_storage 239 | url: "https://pub.dartlang.org" 240 | source: hosted 241 | version: "5.0.0-dev.3" 242 | firebase_storage_platform_interface: 243 | dependency: transitive 244 | description: 245 | name: firebase_storage_platform_interface 246 | url: "https://pub.dartlang.org" 247 | source: hosted 248 | version: "1.0.0-dev.2" 249 | fireflutter: 250 | dependency: "direct main" 251 | description: 252 | name: fireflutter 253 | url: "https://pub.dartlang.org" 254 | source: hosted 255 | version: "0.0.27" 256 | flutter: 257 | dependency: "direct main" 258 | description: flutter 259 | source: sdk 260 | version: "0.0.0" 261 | flutter_driver: 262 | dependency: "direct dev" 263 | description: flutter 264 | source: sdk 265 | version: "0.0.0" 266 | flutter_facebook_auth: 267 | dependency: transitive 268 | description: 269 | name: flutter_facebook_auth 270 | url: "https://pub.dartlang.org" 271 | source: hosted 272 | version: "0.3.3" 273 | flutter_image_compress: 274 | dependency: transitive 275 | description: 276 | name: flutter_image_compress 277 | url: "https://pub.dartlang.org" 278 | source: hosted 279 | version: "0.7.0" 280 | flutter_plugin_android_lifecycle: 281 | dependency: transitive 282 | description: 283 | name: flutter_plugin_android_lifecycle 284 | url: "https://pub.dartlang.org" 285 | source: hosted 286 | version: "1.0.11" 287 | flutter_test: 288 | dependency: "direct dev" 289 | description: flutter 290 | source: sdk 291 | version: "0.0.0" 292 | flutter_web_plugins: 293 | dependency: transitive 294 | description: flutter 295 | source: sdk 296 | version: "0.0.0" 297 | font_awesome_flutter: 298 | dependency: "direct main" 299 | description: 300 | name: font_awesome_flutter 301 | url: "https://pub.dartlang.org" 302 | source: hosted 303 | version: "8.10.0" 304 | fuchsia_remote_debug_protocol: 305 | dependency: transitive 306 | description: flutter 307 | source: sdk 308 | version: "0.0.0" 309 | get: 310 | dependency: "direct main" 311 | description: 312 | name: get 313 | url: "https://pub.dartlang.org" 314 | source: hosted 315 | version: "3.16.1" 316 | glob: 317 | dependency: transitive 318 | description: 319 | name: glob 320 | url: "https://pub.dartlang.org" 321 | source: hosted 322 | version: "1.2.0" 323 | google_sign_in: 324 | dependency: transitive 325 | description: 326 | name: google_sign_in 327 | url: "https://pub.dartlang.org" 328 | source: hosted 329 | version: "4.5.6" 330 | google_sign_in_platform_interface: 331 | dependency: transitive 332 | description: 333 | name: google_sign_in_platform_interface 334 | url: "https://pub.dartlang.org" 335 | source: hosted 336 | version: "1.1.2" 337 | google_sign_in_web: 338 | dependency: transitive 339 | description: 340 | name: google_sign_in_web 341 | url: "https://pub.dartlang.org" 342 | source: hosted 343 | version: "0.9.2" 344 | http: 345 | dependency: transitive 346 | description: 347 | name: http 348 | url: "https://pub.dartlang.org" 349 | source: hosted 350 | version: "0.12.2" 351 | http_multi_server: 352 | dependency: transitive 353 | description: 354 | name: http_multi_server 355 | url: "https://pub.dartlang.org" 356 | source: hosted 357 | version: "2.2.0" 358 | http_parser: 359 | dependency: transitive 360 | description: 361 | name: http_parser 362 | url: "https://pub.dartlang.org" 363 | source: hosted 364 | version: "3.1.4" 365 | image_picker: 366 | dependency: transitive 367 | description: 368 | name: image_picker 369 | url: "https://pub.dartlang.org" 370 | source: hosted 371 | version: "0.6.7+14" 372 | image_picker_platform_interface: 373 | dependency: transitive 374 | description: 375 | name: image_picker_platform_interface 376 | url: "https://pub.dartlang.org" 377 | source: hosted 378 | version: "1.1.1" 379 | intl: 380 | dependency: transitive 381 | description: 382 | name: intl 383 | url: "https://pub.dartlang.org" 384 | source: hosted 385 | version: "0.16.1" 386 | io: 387 | dependency: transitive 388 | description: 389 | name: io 390 | url: "https://pub.dartlang.org" 391 | source: hosted 392 | version: "0.3.4" 393 | js: 394 | dependency: transitive 395 | description: 396 | name: js 397 | url: "https://pub.dartlang.org" 398 | source: hosted 399 | version: "0.6.3-nullsafety.2" 400 | json_rpc_2: 401 | dependency: transitive 402 | description: 403 | name: json_rpc_2 404 | url: "https://pub.dartlang.org" 405 | source: hosted 406 | version: "2.2.2" 407 | logging: 408 | dependency: transitive 409 | description: 410 | name: logging 411 | url: "https://pub.dartlang.org" 412 | source: hosted 413 | version: "0.11.4" 414 | matcher: 415 | dependency: transitive 416 | description: 417 | name: matcher 418 | url: "https://pub.dartlang.org" 419 | source: hosted 420 | version: "0.12.10-nullsafety.1" 421 | merge_map: 422 | dependency: transitive 423 | description: 424 | name: merge_map 425 | url: "https://pub.dartlang.org" 426 | source: hosted 427 | version: "1.0.2" 428 | meta: 429 | dependency: transitive 430 | description: 431 | name: meta 432 | url: "https://pub.dartlang.org" 433 | source: hosted 434 | version: "1.3.0-nullsafety.3" 435 | mime: 436 | dependency: transitive 437 | description: 438 | name: mime 439 | url: "https://pub.dartlang.org" 440 | source: hosted 441 | version: "0.9.7" 442 | modal_bottom_sheet: 443 | dependency: transitive 444 | description: 445 | name: modal_bottom_sheet 446 | url: "https://pub.dartlang.org" 447 | source: hosted 448 | version: "1.0.1-dev" 449 | node_interop: 450 | dependency: transitive 451 | description: 452 | name: node_interop 453 | url: "https://pub.dartlang.org" 454 | source: hosted 455 | version: "1.2.0" 456 | node_io: 457 | dependency: transitive 458 | description: 459 | name: node_io 460 | url: "https://pub.dartlang.org" 461 | source: hosted 462 | version: "1.1.1" 463 | node_preamble: 464 | dependency: transitive 465 | description: 466 | name: node_preamble 467 | url: "https://pub.dartlang.org" 468 | source: hosted 469 | version: "1.4.12" 470 | package_config: 471 | dependency: transitive 472 | description: 473 | name: package_config 474 | url: "https://pub.dartlang.org" 475 | source: hosted 476 | version: "1.9.3" 477 | path: 478 | dependency: transitive 479 | description: 480 | name: path 481 | url: "https://pub.dartlang.org" 482 | source: hosted 483 | version: "1.8.0-nullsafety.1" 484 | path_provider: 485 | dependency: transitive 486 | description: 487 | name: path_provider 488 | url: "https://pub.dartlang.org" 489 | source: hosted 490 | version: "1.6.24" 491 | path_provider_linux: 492 | dependency: transitive 493 | description: 494 | name: path_provider_linux 495 | url: "https://pub.dartlang.org" 496 | source: hosted 497 | version: "0.0.1+2" 498 | path_provider_macos: 499 | dependency: transitive 500 | description: 501 | name: path_provider_macos 502 | url: "https://pub.dartlang.org" 503 | source: hosted 504 | version: "0.0.4+6" 505 | path_provider_platform_interface: 506 | dependency: transitive 507 | description: 508 | name: path_provider_platform_interface 509 | url: "https://pub.dartlang.org" 510 | source: hosted 511 | version: "1.0.4" 512 | path_provider_windows: 513 | dependency: transitive 514 | description: 515 | name: path_provider_windows 516 | url: "https://pub.dartlang.org" 517 | source: hosted 518 | version: "0.0.4+3" 519 | pedantic: 520 | dependency: transitive 521 | description: 522 | name: pedantic 523 | url: "https://pub.dartlang.org" 524 | source: hosted 525 | version: "1.10.0-nullsafety.2" 526 | permission_handler: 527 | dependency: transitive 528 | description: 529 | name: permission_handler 530 | url: "https://pub.dartlang.org" 531 | source: hosted 532 | version: "5.0.1+1" 533 | permission_handler_platform_interface: 534 | dependency: transitive 535 | description: 536 | name: permission_handler_platform_interface 537 | url: "https://pub.dartlang.org" 538 | source: hosted 539 | version: "2.0.1" 540 | platform: 541 | dependency: transitive 542 | description: 543 | name: platform 544 | url: "https://pub.dartlang.org" 545 | source: hosted 546 | version: "3.0.0-nullsafety.2" 547 | plugin_platform_interface: 548 | dependency: transitive 549 | description: 550 | name: plugin_platform_interface 551 | url: "https://pub.dartlang.org" 552 | source: hosted 553 | version: "1.0.3" 554 | pool: 555 | dependency: transitive 556 | description: 557 | name: pool 558 | url: "https://pub.dartlang.org" 559 | source: hosted 560 | version: "1.5.0-nullsafety.2" 561 | process: 562 | dependency: transitive 563 | description: 564 | name: process 565 | url: "https://pub.dartlang.org" 566 | source: hosted 567 | version: "4.0.0-nullsafety.2" 568 | pub_semver: 569 | dependency: transitive 570 | description: 571 | name: pub_semver 572 | url: "https://pub.dartlang.org" 573 | source: hosted 574 | version: "1.4.4" 575 | quiver: 576 | dependency: transitive 577 | description: 578 | name: quiver 579 | url: "https://pub.dartlang.org" 580 | source: hosted 581 | version: "2.1.5" 582 | rxdart: 583 | dependency: transitive 584 | description: 585 | name: rxdart 586 | url: "https://pub.dartlang.org" 587 | source: hosted 588 | version: "0.24.1" 589 | shelf: 590 | dependency: transitive 591 | description: 592 | name: shelf 593 | url: "https://pub.dartlang.org" 594 | source: hosted 595 | version: "0.7.9" 596 | shelf_packages_handler: 597 | dependency: transitive 598 | description: 599 | name: shelf_packages_handler 600 | url: "https://pub.dartlang.org" 601 | source: hosted 602 | version: "2.0.0" 603 | shelf_static: 604 | dependency: transitive 605 | description: 606 | name: shelf_static 607 | url: "https://pub.dartlang.org" 608 | source: hosted 609 | version: "0.2.8" 610 | shelf_web_socket: 611 | dependency: transitive 612 | description: 613 | name: shelf_web_socket 614 | url: "https://pub.dartlang.org" 615 | source: hosted 616 | version: "0.2.3" 617 | sign_in_with_apple: 618 | dependency: transitive 619 | description: 620 | name: sign_in_with_apple 621 | url: "https://pub.dartlang.org" 622 | source: hosted 623 | version: "2.5.4" 624 | sky_engine: 625 | dependency: transitive 626 | description: flutter 627 | source: sdk 628 | version: "0.0.99" 629 | source_map_stack_trace: 630 | dependency: transitive 631 | description: 632 | name: source_map_stack_trace 633 | url: "https://pub.dartlang.org" 634 | source: hosted 635 | version: "2.1.0-nullsafety.3" 636 | source_maps: 637 | dependency: transitive 638 | description: 639 | name: source_maps 640 | url: "https://pub.dartlang.org" 641 | source: hosted 642 | version: "0.10.10-nullsafety.2" 643 | source_span: 644 | dependency: transitive 645 | description: 646 | name: source_span 647 | url: "https://pub.dartlang.org" 648 | source: hosted 649 | version: "1.8.0-nullsafety.2" 650 | stack_trace: 651 | dependency: transitive 652 | description: 653 | name: stack_trace 654 | url: "https://pub.dartlang.org" 655 | source: hosted 656 | version: "1.10.0-nullsafety.1" 657 | stream_channel: 658 | dependency: transitive 659 | description: 660 | name: stream_channel 661 | url: "https://pub.dartlang.org" 662 | source: hosted 663 | version: "2.1.0-nullsafety.1" 664 | string_scanner: 665 | dependency: transitive 666 | description: 667 | name: string_scanner 668 | url: "https://pub.dartlang.org" 669 | source: hosted 670 | version: "1.1.0-nullsafety.1" 671 | sync_http: 672 | dependency: transitive 673 | description: 674 | name: sync_http 675 | url: "https://pub.dartlang.org" 676 | source: hosted 677 | version: "0.2.0" 678 | term_glyph: 679 | dependency: transitive 680 | description: 681 | name: term_glyph 682 | url: "https://pub.dartlang.org" 683 | source: hosted 684 | version: "1.2.0-nullsafety.1" 685 | test: 686 | dependency: "direct dev" 687 | description: 688 | name: test 689 | url: "https://pub.dartlang.org" 690 | source: hosted 691 | version: "1.16.0-nullsafety.5" 692 | test_api: 693 | dependency: transitive 694 | description: 695 | name: test_api 696 | url: "https://pub.dartlang.org" 697 | source: hosted 698 | version: "0.2.19-nullsafety.2" 699 | test_core: 700 | dependency: transitive 701 | description: 702 | name: test_core 703 | url: "https://pub.dartlang.org" 704 | source: hosted 705 | version: "0.3.12-nullsafety.5" 706 | typed_data: 707 | dependency: transitive 708 | description: 709 | name: typed_data 710 | url: "https://pub.dartlang.org" 711 | source: hosted 712 | version: "1.3.0-nullsafety.3" 713 | uuid: 714 | dependency: transitive 715 | description: 716 | name: uuid 717 | url: "https://pub.dartlang.org" 718 | source: hosted 719 | version: "2.2.2" 720 | vector_math: 721 | dependency: transitive 722 | description: 723 | name: vector_math 724 | url: "https://pub.dartlang.org" 725 | source: hosted 726 | version: "2.1.0-nullsafety.3" 727 | vm_service: 728 | dependency: transitive 729 | description: 730 | name: vm_service 731 | url: "https://pub.dartlang.org" 732 | source: hosted 733 | version: "5.5.0" 734 | vm_service_client: 735 | dependency: transitive 736 | description: 737 | name: vm_service_client 738 | url: "https://pub.dartlang.org" 739 | source: hosted 740 | version: "0.2.6+2" 741 | watcher: 742 | dependency: transitive 743 | description: 744 | name: watcher 745 | url: "https://pub.dartlang.org" 746 | source: hosted 747 | version: "0.9.7+15" 748 | web_socket_channel: 749 | dependency: transitive 750 | description: 751 | name: web_socket_channel 752 | url: "https://pub.dartlang.org" 753 | source: hosted 754 | version: "1.1.0" 755 | webdriver: 756 | dependency: transitive 757 | description: 758 | name: webdriver 759 | url: "https://pub.dartlang.org" 760 | source: hosted 761 | version: "2.1.2" 762 | webkit_inspection_protocol: 763 | dependency: transitive 764 | description: 765 | name: webkit_inspection_protocol 766 | url: "https://pub.dartlang.org" 767 | source: hosted 768 | version: "0.7.4" 769 | win32: 770 | dependency: transitive 771 | description: 772 | name: win32 773 | url: "https://pub.dartlang.org" 774 | source: hosted 775 | version: "1.7.3" 776 | xdg_directories: 777 | dependency: transitive 778 | description: 779 | name: xdg_directories 780 | url: "https://pub.dartlang.org" 781 | source: hosted 782 | version: "0.1.2" 783 | yaml: 784 | dependency: transitive 785 | description: 786 | name: yaml 787 | url: "https://pub.dartlang.org" 788 | source: hosted 789 | version: "2.2.1" 790 | sdks: 791 | dart: ">=2.10.0 <2.11.0" 792 | flutter: ">=1.17.0 <2.0.0" 793 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: fireflutter_sample_app 2 | description: A new Flutter project. 3 | 4 | publish_to: "none" 5 | 6 | version: 1.0.0+1 7 | 8 | environment: 9 | sdk: ">=2.7.0 <3.0.0" 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | 15 | cupertino_icons: ^1.0.0 16 | 17 | get: ^3.15.0 18 | country_code_picker: ^1.6.2 19 | fireflutter: ^0.0.27 20 | 21 | faker: ^1.2.1 22 | 23 | font_awesome_flutter: ^8.10.0 24 | 25 | 26 | dev_dependencies: 27 | flutter_test: 28 | sdk: flutter 29 | flutter_driver: 30 | sdk: flutter 31 | test: ^1.14.4 32 | flutter: 33 | uses-material-design: true 34 | 35 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:fireflutter_sample_app/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MainApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /test_driver/app.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_driver/driver_extension.dart'; 2 | import 'package:fireflutter_sample_app/main.dart' as app; 3 | 4 | void main() { 5 | enableFlutterDriverExtension(); 6 | app.main(); 7 | } 8 | -------------------------------------------------------------------------------- /test_driver/app_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:faker/faker.dart'; 2 | import 'package:fireflutter_sample_app/keys.dart'; 3 | import 'package:flutter_driver/flutter_driver.dart'; 4 | import 'package:test/test.dart'; 5 | 6 | Faker faker = Faker(); 7 | String email, password, displayName, color; 8 | FlutterDriver driver; 9 | const Duration _timeout = kUnusuallyLongTimeout; 10 | 11 | /// Delay 12 | Future delay([int milliseconds = 250]) async { 13 | await Future.delayed(Duration(milliseconds: milliseconds)); 14 | } 15 | 16 | /// Check if a wiget exist. 17 | /// 18 | /// ```dart 19 | /// bool ex = await exists('homeScreen'); 20 | /// ``` 21 | Future exists(dynamic key) async { 22 | await delay(100); 23 | try { 24 | await driver.waitFor(find.byValueKey(key), 25 | timeout: Duration(milliseconds: 250)); 26 | return true; 27 | } catch (e) { 28 | return false; 29 | } 30 | } 31 | 32 | /// Helper class 33 | /// 34 | /// A simple alias function of `driver.tap` 35 | Future tap(dynamic key, [timeout]) async { 36 | await delay(100); 37 | await driver.tap(find.byValueKey(key), timeout: timeout ?? _timeout); 38 | } 39 | 40 | /// Helper class 41 | /// 42 | /// A simple alias of `driver.waitFor` 43 | Future waitFor(dynamic key) async { 44 | await delay(100); 45 | await driver.waitFor(find.byValueKey(key), timeout: _timeout); 46 | } 47 | 48 | /// Helper class 49 | /// 50 | /// A simple alias of `driver.enterText` 51 | Future enterText(dynamic key, String text) async { 52 | await delay(100); 53 | 54 | await driver.tap(find.byValueKey(key)); 55 | await driver.enterText(text); 56 | } 57 | 58 | /// Go back until a wiget of the key found. 59 | /// 60 | /// You may use it for going back to home page. 61 | /// ```dart 62 | /// await goBackUntil('homeScreen'); 63 | /// ``` 64 | Future goBackUntil(dynamic key) async { 65 | bool ex = await exists(key); 66 | if (ex) { 67 | return; 68 | } 69 | await driver.tap(find.pageBack()); 70 | await goBackUntil(key); 71 | } 72 | 73 | /// Check if the widget of the key has [search] string. 74 | /// 75 | /// expect(await has(Keys.hPleaseLogin, 'login'), true); 76 | Future has(dynamic key, String search) async { 77 | await delay(100); 78 | String text = await driver.getText(find.byValueKey(key)); 79 | return text.indexOf(search) != -1; 80 | } 81 | 82 | void main() { 83 | email = faker.internet.email(); 84 | password = '12345a'; 85 | displayName = faker.person.name(); 86 | color = faker.food.dish(); 87 | group('Testing App Performance Tests', () { 88 | setUpAll(() async { 89 | driver = await FlutterDriver.connect(); 90 | }); 91 | 92 | tearDownAll(() async { 93 | if (driver != null) { 94 | await driver.close(); 95 | } 96 | }); 97 | 98 | test('Go home screen', () async { 99 | await delay(); 100 | await goBackUntil(Keys.homeScreen); 101 | }); 102 | 103 | test('Go register screen', () async { 104 | await tap(Keys.rButton); 105 | await waitFor(Keys.rsButton); 106 | }); 107 | 108 | test('Fill registration form', () async { 109 | await enterText(Keys.riEmail, email); 110 | await enterText(Keys.riPassword, password); 111 | await enterText(Keys.riDisplayName, displayName); 112 | await enterText(Keys.riColor, color); 113 | }); 114 | 115 | test('Submit registration form', () async { 116 | await tap(Keys.rsButton, Duration(seconds: 10)); 117 | }); 118 | 119 | test('Test email and color', () async { 120 | await waitFor(Keys.homeScreen); 121 | String text = await driver.getText(find.byValueKey(Keys.hInfo)); 122 | expect(text.indexOf(email) >= 0, true); 123 | expect(text.indexOf(displayName) >= 0, true); 124 | expect(text.indexOf(color) >= 0, true); 125 | }); 126 | 127 | test('Logout', () async { 128 | await tap(Keys.hLogoutButton); 129 | expect(await has(Keys.hPleaseLogin, 'login'), true); 130 | }); 131 | 132 | test('Login', () async { 133 | await tap(Keys.hLoginButton); 134 | await waitFor(Keys.lfSubmitButton); 135 | await enterText(Keys.lfEmail, email); 136 | await enterText(Keys.lfPassword, password); 137 | await tap(Keys.lfSubmitButton); 138 | 139 | await waitFor(Keys.homeScreen); 140 | String text = await driver.getText(find.byValueKey(Keys.hInfo)); 141 | expect(text.indexOf(email) >= 0, true); 142 | expect(text.indexOf(displayName) >= 0, true); 143 | expect(text.indexOf(color) >= 0, true); 144 | }); 145 | 146 | test('Update Profile', () async { 147 | await tap(Keys.hProfileButotn); 148 | await waitFor(Keys.pfSubmitButton); 149 | await enterText(Keys.pfDisplayName, 'Nickname Updated(2)'); 150 | await enterText(Keys.pfColor, 'Orange'); 151 | await tap(Keys.pfSubmitButton); 152 | 153 | await goBackUntil(Keys.homeScreen); 154 | await delay(); 155 | String text = await driver.getText(find.byValueKey(Keys.hInfo)); 156 | expect(text.indexOf('Nickname Updated(2)') >= 0, true); 157 | 158 | expect(await has(Keys.hInfo, 'Blue'), false); 159 | expect(await has(Keys.hInfo, 'Orange'), true); 160 | }); 161 | }); 162 | } 163 | -------------------------------------------------------------------------------- /test_driver/test_runner.sh: -------------------------------------------------------------------------------- 1 | export VM_SERVICE_URL=http://127.0.0.1:12345/ && dart app_test.dart --------------------------------------------------------------------------------