├── .gitignore ├── .metadata ├── .vscode └── launch.json ├── README.md ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── flutter_chat │ │ │ │ └── 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 │ │ │ └── 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 │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── 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 │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib ├── app.dart ├── app_module.dart ├── components │ ├── badge │ │ └── badge.dart │ ├── button │ │ ├── attachment_button.dart │ │ └── button.dart │ ├── form │ │ └── input_text.dart │ ├── loading │ │ └── loading_view.dart │ └── search │ │ └── search_button.dart ├── extensions │ └── text_extension.dart ├── main.dart ├── model │ ├── author_model.dart │ ├── chat_model.dart │ ├── form │ │ ├── user_form_model.dart │ │ └── user_form_model.g.dart │ ├── message_model.dart │ └── user_model.dart ├── pages │ ├── chat_page.dart │ ├── chats_list_page.dart │ ├── home_page.dart │ ├── login_page.dart │ └── new_chat_page.dart ├── routes │ └── app_routes.dart ├── services │ ├── firebase_service.dart │ └── notifications_service.dart ├── stores │ ├── auth_store.dart │ ├── auth_store.g.dart │ ├── chat_store.dart │ ├── chat_store.g.dart │ ├── main_store.dart │ ├── settings_store.dart │ └── settings_store.g.dart └── utils │ ├── date_utils.dart │ ├── image_utils.dart │ ├── utils.dart │ └── validate_field_util.dart ├── pubspec.lock └── pubspec.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | /build/ 32 | 33 | # Web related 34 | lib/generated_plugin_registrant.dart 35 | 36 | # Exceptions to above rules. 37 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 38 | ios/Runner/GoogleService-Info.plist 39 | android/app/google-services.json 40 | -------------------------------------------------------------------------------- /.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: 9f5ff2306bb3e30b2b98eee79cd231b1336f41f4 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 | "name": "Flutter", 9 | "request": "launch", 10 | "type": "dart" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flutter Support Chat 2 | 3 | App Flutter para demonstrar o uso das seguintes tecnologias: 4 | 5 | - Firebase; 6 | - MobX; 7 | - Flutter Modular. 8 | 9 | > Projeto em desenvolvimento! 10 | 11 | ## Roadmap 12 | 13 | - [x] Interface da lista de chats; 14 | - [x] Interface do chat; 15 | - [x] Interface adicionar novo chat; 16 | - [ ] Interface perfil do usuário; 17 | - [x] Mensagem do tipo imagem; 18 | - [x] Buscar foto e aplicar crop; 19 | - [x] Integração com o Firebase; 20 | - [x] Login anônimo no Firebase; 21 | - [ ] Login por email e senha no Firebase; 22 | - [ ] Cadastrar novo usuário no Firebase; 23 | - [ ] Atualizar usuário no Firebase; 24 | - [ ] Recuperar chats e mensagens do Firebase; 25 | - [ ] Funcionalidade usuário está digitando; 26 | - [ ] Funcionalidade usuário está online; 27 | - [ ] Funcionalidade usuário está lendo; 28 | - [ ] Contagem de mensagens não lidas; 29 | - [ ] Salvar chats e mensagens no Firebase; 30 | - [ ] Salvar imagens no Firebase Storage; 31 | - [ ] Editar mensagens; 32 | - [ ] Excluir mensagens; 33 | - [ ] Excluir imagens do Firebase Storage; 34 | - [ ] Arquivar chat; 35 | - [ ] Reabrir chat arquivado; 36 | - [ ] Exibir histório de chats arquivados; 37 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 28 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.darlantc.flutterChat" 42 | minSdkVersion 16 43 | targetSdkVersion 28 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 47 | } 48 | 49 | buildTypes { 50 | release { 51 | // TODO: Add your own signing config for the release build. 52 | // Signing with the debug keys for now, so `flutter run --release` works. 53 | signingConfig signingConfigs.debug 54 | } 55 | } 56 | } 57 | 58 | flutter { 59 | source '../..' 60 | } 61 | 62 | dependencies { 63 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 64 | testImplementation 'junit:junit:4.12' 65 | androidTestImplementation 'androidx.test:runner:1.1.1' 66 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 67 | } 68 | 69 | apply plugin: 'com.google.gms.google-services' -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 12 | 19 | 20 | 21 | 22 | 23 | 24 | 29 | 31 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_chat/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_chat 2 | 3 | import androidx.annotation.NonNull; 4 | import io.flutter.embedding.android.FlutterActivity 5 | import io.flutter.embedding.engine.FlutterEngine 6 | import io.flutter.plugins.GeneratedPluginRegistrant 7 | 8 | class MainActivity: FlutterActivity() { 9 | override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { 10 | GeneratedPluginRegistrant.registerWith(flutterEngine); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /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/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | // Add the google services classpath 12 | classpath 'com.google.gms:google-services:4.3.3' 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | google() 19 | jcenter() 20 | } 21 | } 22 | 23 | rootProject.buildDir = '../build' 24 | subprojects { 25 | project.buildDir = "${rootProject.buildDir}/${project.name}" 26 | } 27 | subprojects { 28 | project.evaluationDependsOn(':app') 29 | } 30 | 31 | task clean(type: Delete) { 32 | delete rootProject.buildDir 33 | } 34 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /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 | 8.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, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | generated_key_values = {} 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) do |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | generated_key_values[podname] = podpath 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | end 32 | generated_key_values 33 | end 34 | 35 | target 'Runner' do 36 | use_frameworks! 37 | use_modular_headers! 38 | 39 | # Flutter Pod 40 | 41 | copied_flutter_dir = File.join(__dir__, 'Flutter') 42 | copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') 43 | copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') 44 | unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) 45 | # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. 46 | # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. 47 | # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. 48 | 49 | generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') 50 | unless File.exist?(generated_xcode_build_settings_path) 51 | raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" 52 | end 53 | generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) 54 | cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; 55 | 56 | unless File.exist?(copied_framework_path) 57 | FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) 58 | end 59 | unless File.exist?(copied_podspec_path) 60 | FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) 61 | end 62 | end 63 | 64 | # Keep pod path relative so it can be checked into Podfile.lock. 65 | pod 'Flutter', :path => 'Flutter' 66 | 67 | # Plugin Pods 68 | 69 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 70 | # referring to absolute paths on developers' machines. 71 | system('rm -rf .symlinks') 72 | system('mkdir -p .symlinks/plugins') 73 | plugin_pods = parse_KV_file('../.flutter-plugins') 74 | plugin_pods.each do |name, path| 75 | symlink = File.join('.symlinks', 'plugins', name) 76 | File.symlink(path, symlink) 77 | pod name, :path => File.join(symlink, 'ios') 78 | end 79 | end 80 | 81 | # Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. 82 | install! 'cocoapods', :disable_input_output_paths => true 83 | 84 | post_install do |installer| 85 | installer.pods_project.targets.each do |target| 86 | target.build_configurations.each do |config| 87 | config.build_settings['ENABLE_BITCODE'] = 'NO' 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Firebase/Auth (6.14.0): 3 | - Firebase/CoreOnly 4 | - FirebaseAuth (~> 6.4.1) 5 | - Firebase/Core (6.14.0): 6 | - Firebase/CoreOnly 7 | - FirebaseAnalytics (= 6.1.7) 8 | - Firebase/CoreOnly (6.14.0): 9 | - FirebaseCore (= 6.5.0) 10 | - Firebase/Database (6.14.0): 11 | - Firebase/CoreOnly 12 | - FirebaseDatabase (~> 6.1.3) 13 | - firebase_auth (0.0.1): 14 | - Firebase/Auth (~> 6.3) 15 | - Firebase/Core 16 | - Flutter 17 | - firebase_auth_web (0.1.0): 18 | - Flutter 19 | - firebase_core (0.0.1): 20 | - Firebase/Core 21 | - Flutter 22 | - firebase_core_web (0.1.0): 23 | - Flutter 24 | - firebase_database (0.0.1): 25 | - Firebase/Database 26 | - Flutter 27 | - FirebaseAnalytics (6.1.7): 28 | - FirebaseCore (~> 6.5) 29 | - FirebaseInstanceID (~> 4.2) 30 | - GoogleAppMeasurement (= 6.1.7) 31 | - GoogleUtilities/AppDelegateSwizzler (~> 6.0) 32 | - GoogleUtilities/MethodSwizzler (~> 6.0) 33 | - GoogleUtilities/Network (~> 6.0) 34 | - "GoogleUtilities/NSData+zlib (~> 6.0)" 35 | - nanopb (= 0.3.9011) 36 | - FirebaseAuth (6.4.1): 37 | - FirebaseAuthInterop (~> 1.0) 38 | - FirebaseCore (~> 6.2) 39 | - GoogleUtilities/AppDelegateSwizzler (~> 6.2) 40 | - GoogleUtilities/Environment (~> 6.2) 41 | - GTMSessionFetcher/Core (~> 1.1) 42 | - FirebaseAuthInterop (1.0.0) 43 | - FirebaseCore (6.5.0): 44 | - FirebaseCoreDiagnostics (~> 1.0) 45 | - FirebaseCoreDiagnosticsInterop (~> 1.0) 46 | - GoogleUtilities/Environment (~> 6.4) 47 | - GoogleUtilities/Logger (~> 6.4) 48 | - FirebaseCoreDiagnostics (1.1.2): 49 | - FirebaseCoreDiagnosticsInterop (~> 1.0) 50 | - GoogleDataTransportCCTSupport (~> 1.0) 51 | - GoogleUtilities/Environment (~> 6.2) 52 | - GoogleUtilities/Logger (~> 6.2) 53 | - nanopb (~> 0.3.901) 54 | - FirebaseCoreDiagnosticsInterop (1.1.0) 55 | - FirebaseDatabase (6.1.3): 56 | - FirebaseAuthInterop (~> 1.0) 57 | - FirebaseCore (~> 6.0) 58 | - leveldb-library (~> 1.22) 59 | - FirebaseInstanceID (4.2.8): 60 | - FirebaseCore (~> 6.5) 61 | - GoogleUtilities/Environment (~> 6.4) 62 | - GoogleUtilities/UserDefaults (~> 6.4) 63 | - Flutter (1.0.0) 64 | - flutter_plugin_android_lifecycle (0.0.1): 65 | - Flutter 66 | - GoogleAppMeasurement (6.1.7): 67 | - GoogleUtilities/AppDelegateSwizzler (~> 6.0) 68 | - GoogleUtilities/MethodSwizzler (~> 6.0) 69 | - GoogleUtilities/Network (~> 6.0) 70 | - "GoogleUtilities/NSData+zlib (~> 6.0)" 71 | - nanopb (= 0.3.9011) 72 | - GoogleDataTransport (3.2.0) 73 | - GoogleDataTransportCCTSupport (1.2.3): 74 | - GoogleDataTransport (~> 3.2) 75 | - nanopb (~> 0.3.901) 76 | - GoogleUtilities/AppDelegateSwizzler (6.4.0): 77 | - GoogleUtilities/Environment 78 | - GoogleUtilities/Logger 79 | - GoogleUtilities/Network 80 | - GoogleUtilities/Environment (6.4.0) 81 | - GoogleUtilities/Logger (6.4.0): 82 | - GoogleUtilities/Environment 83 | - GoogleUtilities/MethodSwizzler (6.4.0): 84 | - GoogleUtilities/Logger 85 | - GoogleUtilities/Network (6.4.0): 86 | - GoogleUtilities/Logger 87 | - "GoogleUtilities/NSData+zlib" 88 | - GoogleUtilities/Reachability 89 | - "GoogleUtilities/NSData+zlib (6.4.0)" 90 | - GoogleUtilities/Reachability (6.4.0): 91 | - GoogleUtilities/Logger 92 | - GoogleUtilities/UserDefaults (6.4.0): 93 | - GoogleUtilities/Logger 94 | - GTMSessionFetcher/Core (1.3.0) 95 | - image_cropper (0.0.2): 96 | - Flutter 97 | - TOCropViewController (~> 2.5.2) 98 | - image_picker (0.0.1): 99 | - Flutter 100 | - leveldb-library (1.22) 101 | - nanopb (0.3.9011): 102 | - nanopb/decode (= 0.3.9011) 103 | - nanopb/encode (= 0.3.9011) 104 | - nanopb/decode (0.3.9011) 105 | - nanopb/encode (0.3.9011) 106 | - TOCropViewController (2.5.2) 107 | 108 | DEPENDENCIES: 109 | - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) 110 | - firebase_auth_web (from `.symlinks/plugins/firebase_auth_web/ios`) 111 | - firebase_core (from `.symlinks/plugins/firebase_core/ios`) 112 | - firebase_core_web (from `.symlinks/plugins/firebase_core_web/ios`) 113 | - firebase_database (from `.symlinks/plugins/firebase_database/ios`) 114 | - Flutter (from `Flutter`) 115 | - flutter_plugin_android_lifecycle (from `.symlinks/plugins/flutter_plugin_android_lifecycle/ios`) 116 | - image_cropper (from `.symlinks/plugins/image_cropper/ios`) 117 | - image_picker (from `.symlinks/plugins/image_picker/ios`) 118 | 119 | SPEC REPOS: 120 | trunk: 121 | - Firebase 122 | - FirebaseAnalytics 123 | - FirebaseAuth 124 | - FirebaseAuthInterop 125 | - FirebaseCore 126 | - FirebaseCoreDiagnostics 127 | - FirebaseCoreDiagnosticsInterop 128 | - FirebaseDatabase 129 | - FirebaseInstanceID 130 | - GoogleAppMeasurement 131 | - GoogleDataTransport 132 | - GoogleDataTransportCCTSupport 133 | - GoogleUtilities 134 | - GTMSessionFetcher 135 | - leveldb-library 136 | - nanopb 137 | - TOCropViewController 138 | 139 | EXTERNAL SOURCES: 140 | firebase_auth: 141 | :path: ".symlinks/plugins/firebase_auth/ios" 142 | firebase_auth_web: 143 | :path: ".symlinks/plugins/firebase_auth_web/ios" 144 | firebase_core: 145 | :path: ".symlinks/plugins/firebase_core/ios" 146 | firebase_core_web: 147 | :path: ".symlinks/plugins/firebase_core_web/ios" 148 | firebase_database: 149 | :path: ".symlinks/plugins/firebase_database/ios" 150 | Flutter: 151 | :path: Flutter 152 | flutter_plugin_android_lifecycle: 153 | :path: ".symlinks/plugins/flutter_plugin_android_lifecycle/ios" 154 | image_cropper: 155 | :path: ".symlinks/plugins/image_cropper/ios" 156 | image_picker: 157 | :path: ".symlinks/plugins/image_picker/ios" 158 | 159 | SPEC CHECKSUMS: 160 | Firebase: 0219bb4782eb1406f1b9b0628a2e625484ce910d 161 | firebase_auth: 1f46484071219159ad6c64d304af425ad7373052 162 | firebase_auth_web: 0955c07bcc06e84af76b9d4e32e6f31518f2d7de 163 | firebase_core: 0c84455537bed6d6b5b8c2dd611ff1fbb1ea5cbd 164 | firebase_core_web: d501d8b946b60c8af265428ce483b0fff5ad52d1 165 | firebase_database: 25e756b41e85a2d9c07502b78f28fc5ae5973e0d 166 | FirebaseAnalytics: f68b9f3f1241385129ae0a83b63627fc420c05e5 167 | FirebaseAuth: 831577b184ecaba38bc886768c1c22a450cc44e3 168 | FirebaseAuthInterop: 0ffa57668be100582bb7643d4fcb7615496c41fc 169 | FirebaseCore: 632e05cc5e1199d9147122c16d92305eb04c34bd 170 | FirebaseCoreDiagnostics: 511f4f3ed7d440bb69127e8b97c2bc8befae639e 171 | FirebaseCoreDiagnosticsInterop: e9b1b023157e3a2fc6418b5cb601e79b9af7b3a0 172 | FirebaseDatabase: 9db0c9bb6d3308870cc831a8a6d70c82fd169f37 173 | FirebaseInstanceID: ce993a3c3670a8f5d47ce371ac5d143c560608c5 174 | Flutter: 0e3d915762c693b495b44d77113d4970485de6ec 175 | flutter_plugin_android_lifecycle: 47de533a02850f070f5696a623995e93eddcdb9b 176 | GoogleAppMeasurement: db118eb61a97dd8c4f7014e368d3c335cbbcf80a 177 | GoogleDataTransport: 8e9b210c97d55fbff306cc5468ff91b9cb32dcf5 178 | GoogleDataTransportCCTSupport: 202d7cdf9c4a7d81a2bb7f7e7e1ba6faa421b1f2 179 | GoogleUtilities: 29bd0d8f850efbd28cff6d99e8b7da1f8d236bcf 180 | GTMSessionFetcher: 43b8b64263023d4f32caa0b40f4c8bfa3c5f36d8 181 | image_cropper: 3c16d7651730ffe85897f5a1c4e2547e6b54989a 182 | image_picker: e3eacd46b94694dde7cf2705955cece853aa1a8f 183 | leveldb-library: 55d93ee664b4007aac644a782d11da33fba316f7 184 | nanopb: 18003b5e52dab79db540fe93fe9579f399bd1ccd 185 | TOCropViewController: e9da34f484aedd4e5d5a8ab230ba217cfe16c729 186 | 187 | PODFILE CHECKSUM: 1b66dae606f75376c5f2135a8290850eeb09ae83 188 | 189 | COCOAPODS: 1.8.4 190 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0CBFA79123F49EDD00F35F4A /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0CBFA79023F49EDD00F35F4A /* GoogleService-Info.plist */; }; 11 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 14 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 15 | 5681E43A18DE298CF6AAAA32 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DFF06971D6DD5709D32880E2 /* Pods_Runner.framework */; }; 16 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 17 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; 18 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 19 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 20 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 21 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXCopyFilesBuildPhase section */ 25 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 26 | isa = PBXCopyFilesBuildPhase; 27 | buildActionMask = 2147483647; 28 | dstPath = ""; 29 | dstSubfolderSpec = 10; 30 | files = ( 31 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, 32 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, 33 | ); 34 | name = "Embed Frameworks"; 35 | runOnlyForDeploymentPostprocessing = 0; 36 | }; 37 | /* End PBXCopyFilesBuildPhase section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 0CBFA79023F49EDD00F35F4A /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 41 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 42 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 43 | 16B69BA0BDC15982ADC476CD /* 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 = ""; }; 44 | 18792D5235A46203F546A8E4 /* 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 = ""; }; 45 | 327C2E32821AE0DA1D163699 /* 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 = ""; }; 46 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 47 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 48 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 49 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 50 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 51 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 52 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 53 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 54 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 56 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 57 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 58 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 59 | DFF06971D6DD5709D32880E2 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 60 | /* End PBXFileReference section */ 61 | 62 | /* Begin PBXFrameworksBuildPhase section */ 63 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 64 | isa = PBXFrameworksBuildPhase; 65 | buildActionMask = 2147483647; 66 | files = ( 67 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 68 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 69 | 5681E43A18DE298CF6AAAA32 /* Pods_Runner.framework in Frameworks */, 70 | ); 71 | runOnlyForDeploymentPostprocessing = 0; 72 | }; 73 | /* End PBXFrameworksBuildPhase section */ 74 | 75 | /* Begin PBXGroup section */ 76 | 9740EEB11CF90186004384FC /* Flutter */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | 3B80C3931E831B6300D905FE /* App.framework */, 80 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 81 | 9740EEBA1CF902C7004384FC /* Flutter.framework */, 82 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 83 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 84 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 85 | ); 86 | name = Flutter; 87 | sourceTree = ""; 88 | }; 89 | 97C146E51CF9000F007C117D = { 90 | isa = PBXGroup; 91 | children = ( 92 | 9740EEB11CF90186004384FC /* Flutter */, 93 | 97C146F01CF9000F007C117D /* Runner */, 94 | 97C146EF1CF9000F007C117D /* Products */, 95 | F26450C38D516FECD22CE9A5 /* Pods */, 96 | C9E235FC1C2CD27519B8FC8D /* Frameworks */, 97 | ); 98 | sourceTree = ""; 99 | }; 100 | 97C146EF1CF9000F007C117D /* Products */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 97C146EE1CF9000F007C117D /* Runner.app */, 104 | ); 105 | name = Products; 106 | sourceTree = ""; 107 | }; 108 | 97C146F01CF9000F007C117D /* Runner */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 112 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 113 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 114 | 0CBFA79023F49EDD00F35F4A /* GoogleService-Info.plist */, 115 | 97C147021CF9000F007C117D /* Info.plist */, 116 | 97C146F11CF9000F007C117D /* Supporting Files */, 117 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 118 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 119 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 120 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 121 | ); 122 | path = Runner; 123 | sourceTree = ""; 124 | }; 125 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | ); 129 | name = "Supporting Files"; 130 | sourceTree = ""; 131 | }; 132 | C9E235FC1C2CD27519B8FC8D /* Frameworks */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | DFF06971D6DD5709D32880E2 /* Pods_Runner.framework */, 136 | ); 137 | name = Frameworks; 138 | sourceTree = ""; 139 | }; 140 | F26450C38D516FECD22CE9A5 /* Pods */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | 327C2E32821AE0DA1D163699 /* Pods-Runner.debug.xcconfig */, 144 | 16B69BA0BDC15982ADC476CD /* Pods-Runner.release.xcconfig */, 145 | 18792D5235A46203F546A8E4 /* Pods-Runner.profile.xcconfig */, 146 | ); 147 | path = Pods; 148 | sourceTree = ""; 149 | }; 150 | /* End PBXGroup section */ 151 | 152 | /* Begin PBXNativeTarget section */ 153 | 97C146ED1CF9000F007C117D /* Runner */ = { 154 | isa = PBXNativeTarget; 155 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 156 | buildPhases = ( 157 | AB232F4C2710D422533C9E88 /* [CP] Check Pods Manifest.lock */, 158 | 9740EEB61CF901F6004384FC /* Run Script */, 159 | 97C146EA1CF9000F007C117D /* Sources */, 160 | 97C146EB1CF9000F007C117D /* Frameworks */, 161 | 97C146EC1CF9000F007C117D /* Resources */, 162 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 163 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 164 | E82AF807A0FD7C05FF27A5A3 /* [CP] Embed Pods Frameworks */, 165 | ); 166 | buildRules = ( 167 | ); 168 | dependencies = ( 169 | ); 170 | name = Runner; 171 | productName = Runner; 172 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 173 | productType = "com.apple.product-type.application"; 174 | }; 175 | /* End PBXNativeTarget section */ 176 | 177 | /* Begin PBXProject section */ 178 | 97C146E61CF9000F007C117D /* Project object */ = { 179 | isa = PBXProject; 180 | attributes = { 181 | LastUpgradeCheck = 1020; 182 | ORGANIZATIONNAME = "The Chromium Authors"; 183 | TargetAttributes = { 184 | 97C146ED1CF9000F007C117D = { 185 | CreatedOnToolsVersion = 7.3.1; 186 | LastSwiftMigration = 1100; 187 | }; 188 | }; 189 | }; 190 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 191 | compatibilityVersion = "Xcode 3.2"; 192 | developmentRegion = en; 193 | hasScannedForEncodings = 0; 194 | knownRegions = ( 195 | en, 196 | Base, 197 | ); 198 | mainGroup = 97C146E51CF9000F007C117D; 199 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 200 | projectDirPath = ""; 201 | projectRoot = ""; 202 | targets = ( 203 | 97C146ED1CF9000F007C117D /* Runner */, 204 | ); 205 | }; 206 | /* End PBXProject section */ 207 | 208 | /* Begin PBXResourcesBuildPhase section */ 209 | 97C146EC1CF9000F007C117D /* Resources */ = { 210 | isa = PBXResourcesBuildPhase; 211 | buildActionMask = 2147483647; 212 | files = ( 213 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 214 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 215 | 0CBFA79123F49EDD00F35F4A /* GoogleService-Info.plist in Resources */, 216 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 217 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 218 | ); 219 | runOnlyForDeploymentPostprocessing = 0; 220 | }; 221 | /* End PBXResourcesBuildPhase section */ 222 | 223 | /* Begin PBXShellScriptBuildPhase section */ 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\" thin"; 237 | }; 238 | 9740EEB61CF901F6004384FC /* Run Script */ = { 239 | isa = PBXShellScriptBuildPhase; 240 | buildActionMask = 2147483647; 241 | files = ( 242 | ); 243 | inputPaths = ( 244 | ); 245 | name = "Run Script"; 246 | outputPaths = ( 247 | ); 248 | runOnlyForDeploymentPostprocessing = 0; 249 | shellPath = /bin/sh; 250 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 251 | }; 252 | AB232F4C2710D422533C9E88 /* [CP] Check Pods Manifest.lock */ = { 253 | isa = PBXShellScriptBuildPhase; 254 | buildActionMask = 2147483647; 255 | files = ( 256 | ); 257 | inputFileListPaths = ( 258 | ); 259 | inputPaths = ( 260 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 261 | "${PODS_ROOT}/Manifest.lock", 262 | ); 263 | name = "[CP] Check Pods Manifest.lock"; 264 | outputFileListPaths = ( 265 | ); 266 | outputPaths = ( 267 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 268 | ); 269 | runOnlyForDeploymentPostprocessing = 0; 270 | shellPath = /bin/sh; 271 | 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"; 272 | showEnvVarsInLog = 0; 273 | }; 274 | E82AF807A0FD7C05FF27A5A3 /* [CP] Embed Pods Frameworks */ = { 275 | isa = PBXShellScriptBuildPhase; 276 | buildActionMask = 2147483647; 277 | files = ( 278 | ); 279 | inputPaths = ( 280 | ); 281 | name = "[CP] Embed Pods Frameworks"; 282 | outputPaths = ( 283 | ); 284 | runOnlyForDeploymentPostprocessing = 0; 285 | shellPath = /bin/sh; 286 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 287 | showEnvVarsInLog = 0; 288 | }; 289 | /* End PBXShellScriptBuildPhase section */ 290 | 291 | /* Begin PBXSourcesBuildPhase section */ 292 | 97C146EA1CF9000F007C117D /* Sources */ = { 293 | isa = PBXSourcesBuildPhase; 294 | buildActionMask = 2147483647; 295 | files = ( 296 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 297 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 298 | ); 299 | runOnlyForDeploymentPostprocessing = 0; 300 | }; 301 | /* End PBXSourcesBuildPhase section */ 302 | 303 | /* Begin PBXVariantGroup section */ 304 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 305 | isa = PBXVariantGroup; 306 | children = ( 307 | 97C146FB1CF9000F007C117D /* Base */, 308 | ); 309 | name = Main.storyboard; 310 | sourceTree = ""; 311 | }; 312 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 313 | isa = PBXVariantGroup; 314 | children = ( 315 | 97C147001CF9000F007C117D /* Base */, 316 | ); 317 | name = LaunchScreen.storyboard; 318 | sourceTree = ""; 319 | }; 320 | /* End PBXVariantGroup section */ 321 | 322 | /* Begin XCBuildConfiguration section */ 323 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 324 | isa = XCBuildConfiguration; 325 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 326 | buildSettings = { 327 | ALWAYS_SEARCH_USER_PATHS = NO; 328 | CLANG_ANALYZER_NONNULL = YES; 329 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 330 | CLANG_CXX_LIBRARY = "libc++"; 331 | CLANG_ENABLE_MODULES = YES; 332 | CLANG_ENABLE_OBJC_ARC = YES; 333 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 334 | CLANG_WARN_BOOL_CONVERSION = YES; 335 | CLANG_WARN_COMMA = YES; 336 | CLANG_WARN_CONSTANT_CONVERSION = YES; 337 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 338 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 339 | CLANG_WARN_EMPTY_BODY = YES; 340 | CLANG_WARN_ENUM_CONVERSION = YES; 341 | CLANG_WARN_INFINITE_RECURSION = YES; 342 | CLANG_WARN_INT_CONVERSION = YES; 343 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 344 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 345 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 346 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 347 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 348 | CLANG_WARN_STRICT_PROTOTYPES = YES; 349 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 350 | CLANG_WARN_UNREACHABLE_CODE = YES; 351 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 352 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 353 | COPY_PHASE_STRIP = NO; 354 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 355 | ENABLE_NS_ASSERTIONS = NO; 356 | ENABLE_STRICT_OBJC_MSGSEND = YES; 357 | GCC_C_LANGUAGE_STANDARD = gnu99; 358 | GCC_NO_COMMON_BLOCKS = YES; 359 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 360 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 361 | GCC_WARN_UNDECLARED_SELECTOR = YES; 362 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 363 | GCC_WARN_UNUSED_FUNCTION = YES; 364 | GCC_WARN_UNUSED_VARIABLE = YES; 365 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 366 | MTL_ENABLE_DEBUG_INFO = NO; 367 | SDKROOT = iphoneos; 368 | SUPPORTED_PLATFORMS = iphoneos; 369 | TARGETED_DEVICE_FAMILY = "1,2"; 370 | VALIDATE_PRODUCT = YES; 371 | }; 372 | name = Profile; 373 | }; 374 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 375 | isa = XCBuildConfiguration; 376 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 377 | buildSettings = { 378 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 379 | CLANG_ENABLE_MODULES = YES; 380 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 381 | ENABLE_BITCODE = NO; 382 | FRAMEWORK_SEARCH_PATHS = ( 383 | "$(inherited)", 384 | "$(PROJECT_DIR)/Flutter", 385 | ); 386 | INFOPLIST_FILE = Runner/Info.plist; 387 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 388 | LIBRARY_SEARCH_PATHS = ( 389 | "$(inherited)", 390 | "$(PROJECT_DIR)/Flutter", 391 | ); 392 | PRODUCT_BUNDLE_IDENTIFIER = com.darlantc.flutterChat; 393 | PRODUCT_NAME = "$(TARGET_NAME)"; 394 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 395 | SWIFT_VERSION = 5.0; 396 | VERSIONING_SYSTEM = "apple-generic"; 397 | }; 398 | name = Profile; 399 | }; 400 | 97C147031CF9000F007C117D /* Debug */ = { 401 | isa = XCBuildConfiguration; 402 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 403 | buildSettings = { 404 | ALWAYS_SEARCH_USER_PATHS = NO; 405 | CLANG_ANALYZER_NONNULL = YES; 406 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 407 | CLANG_CXX_LIBRARY = "libc++"; 408 | CLANG_ENABLE_MODULES = YES; 409 | CLANG_ENABLE_OBJC_ARC = YES; 410 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 411 | CLANG_WARN_BOOL_CONVERSION = YES; 412 | CLANG_WARN_COMMA = YES; 413 | CLANG_WARN_CONSTANT_CONVERSION = YES; 414 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 415 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 416 | CLANG_WARN_EMPTY_BODY = YES; 417 | CLANG_WARN_ENUM_CONVERSION = YES; 418 | CLANG_WARN_INFINITE_RECURSION = YES; 419 | CLANG_WARN_INT_CONVERSION = YES; 420 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 421 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 422 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 423 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 424 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 425 | CLANG_WARN_STRICT_PROTOTYPES = YES; 426 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 427 | CLANG_WARN_UNREACHABLE_CODE = YES; 428 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 429 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 430 | COPY_PHASE_STRIP = NO; 431 | DEBUG_INFORMATION_FORMAT = dwarf; 432 | ENABLE_STRICT_OBJC_MSGSEND = YES; 433 | ENABLE_TESTABILITY = YES; 434 | GCC_C_LANGUAGE_STANDARD = gnu99; 435 | GCC_DYNAMIC_NO_PIC = NO; 436 | GCC_NO_COMMON_BLOCKS = YES; 437 | GCC_OPTIMIZATION_LEVEL = 0; 438 | GCC_PREPROCESSOR_DEFINITIONS = ( 439 | "DEBUG=1", 440 | "$(inherited)", 441 | ); 442 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 443 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 444 | GCC_WARN_UNDECLARED_SELECTOR = YES; 445 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 446 | GCC_WARN_UNUSED_FUNCTION = YES; 447 | GCC_WARN_UNUSED_VARIABLE = YES; 448 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 449 | MTL_ENABLE_DEBUG_INFO = YES; 450 | ONLY_ACTIVE_ARCH = YES; 451 | SDKROOT = iphoneos; 452 | TARGETED_DEVICE_FAMILY = "1,2"; 453 | }; 454 | name = Debug; 455 | }; 456 | 97C147041CF9000F007C117D /* Release */ = { 457 | isa = XCBuildConfiguration; 458 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 459 | buildSettings = { 460 | ALWAYS_SEARCH_USER_PATHS = NO; 461 | CLANG_ANALYZER_NONNULL = YES; 462 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 463 | CLANG_CXX_LIBRARY = "libc++"; 464 | CLANG_ENABLE_MODULES = YES; 465 | CLANG_ENABLE_OBJC_ARC = YES; 466 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 467 | CLANG_WARN_BOOL_CONVERSION = YES; 468 | CLANG_WARN_COMMA = YES; 469 | CLANG_WARN_CONSTANT_CONVERSION = YES; 470 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 471 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 472 | CLANG_WARN_EMPTY_BODY = YES; 473 | CLANG_WARN_ENUM_CONVERSION = YES; 474 | CLANG_WARN_INFINITE_RECURSION = YES; 475 | CLANG_WARN_INT_CONVERSION = YES; 476 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 477 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 478 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 479 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 480 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 481 | CLANG_WARN_STRICT_PROTOTYPES = YES; 482 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 483 | CLANG_WARN_UNREACHABLE_CODE = YES; 484 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 485 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 486 | COPY_PHASE_STRIP = NO; 487 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 488 | ENABLE_NS_ASSERTIONS = NO; 489 | ENABLE_STRICT_OBJC_MSGSEND = YES; 490 | GCC_C_LANGUAGE_STANDARD = gnu99; 491 | GCC_NO_COMMON_BLOCKS = YES; 492 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 493 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 494 | GCC_WARN_UNDECLARED_SELECTOR = YES; 495 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 496 | GCC_WARN_UNUSED_FUNCTION = YES; 497 | GCC_WARN_UNUSED_VARIABLE = YES; 498 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 499 | MTL_ENABLE_DEBUG_INFO = NO; 500 | SDKROOT = iphoneos; 501 | SUPPORTED_PLATFORMS = iphoneos; 502 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 503 | TARGETED_DEVICE_FAMILY = "1,2"; 504 | VALIDATE_PRODUCT = YES; 505 | }; 506 | name = Release; 507 | }; 508 | 97C147061CF9000F007C117D /* Debug */ = { 509 | isa = XCBuildConfiguration; 510 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 511 | buildSettings = { 512 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 513 | CLANG_ENABLE_MODULES = YES; 514 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 515 | ENABLE_BITCODE = NO; 516 | FRAMEWORK_SEARCH_PATHS = ( 517 | "$(inherited)", 518 | "$(PROJECT_DIR)/Flutter", 519 | ); 520 | INFOPLIST_FILE = Runner/Info.plist; 521 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 522 | LIBRARY_SEARCH_PATHS = ( 523 | "$(inherited)", 524 | "$(PROJECT_DIR)/Flutter", 525 | ); 526 | PRODUCT_BUNDLE_IDENTIFIER = com.darlantc.flutterChat; 527 | PRODUCT_NAME = "$(TARGET_NAME)"; 528 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 529 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 530 | SWIFT_VERSION = 5.0; 531 | VERSIONING_SYSTEM = "apple-generic"; 532 | }; 533 | name = Debug; 534 | }; 535 | 97C147071CF9000F007C117D /* Release */ = { 536 | isa = XCBuildConfiguration; 537 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 538 | buildSettings = { 539 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 540 | CLANG_ENABLE_MODULES = YES; 541 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 542 | ENABLE_BITCODE = NO; 543 | FRAMEWORK_SEARCH_PATHS = ( 544 | "$(inherited)", 545 | "$(PROJECT_DIR)/Flutter", 546 | ); 547 | INFOPLIST_FILE = Runner/Info.plist; 548 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 549 | LIBRARY_SEARCH_PATHS = ( 550 | "$(inherited)", 551 | "$(PROJECT_DIR)/Flutter", 552 | ); 553 | PRODUCT_BUNDLE_IDENTIFIER = com.darlantc.flutterChat; 554 | PRODUCT_NAME = "$(TARGET_NAME)"; 555 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 556 | SWIFT_VERSION = 5.0; 557 | VERSIONING_SYSTEM = "apple-generic"; 558 | }; 559 | name = Release; 560 | }; 561 | /* End XCBuildConfiguration section */ 562 | 563 | /* Begin XCConfigurationList section */ 564 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 565 | isa = XCConfigurationList; 566 | buildConfigurations = ( 567 | 97C147031CF9000F007C117D /* Debug */, 568 | 97C147041CF9000F007C117D /* Release */, 569 | 249021D3217E4FDB00AE95B9 /* Profile */, 570 | ); 571 | defaultConfigurationIsVisible = 0; 572 | defaultConfigurationName = Release; 573 | }; 574 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 575 | isa = XCConfigurationList; 576 | buildConfigurations = ( 577 | 97C147061CF9000F007C117D /* Debug */, 578 | 97C147071CF9000F007C117D /* Release */, 579 | 249021D4217E4FDB00AE95B9 /* Profile */, 580 | ); 581 | defaultConfigurationIsVisible = 0; 582 | defaultConfigurationName = Release; 583 | }; 584 | /* End XCConfigurationList section */ 585 | }; 586 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 587 | } 588 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /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/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/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/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/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/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/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/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/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/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/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/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/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/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/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/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/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/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/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/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/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/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/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/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/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/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/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/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/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/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/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/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/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darlantc/FlutterSupportChat/1171a1df05cb01d3cc47f1c1f12a7e4e6e71c08b/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/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | flutter_chat 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | NSCameraUsageDescription 26 | Tirar novas fotos para enviar na conversa. 27 | NSPhotoLibraryUsageDescription 28 | Buscar fotos para enviar na conversa. 29 | UILaunchStoryboardName 30 | LaunchScreen 31 | UIMainStoryboardFile 32 | Main 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | UIViewControllerBasedStatusBarAppearance 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /lib/app.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_modular/flutter_modular.dart'; 3 | import 'package:oktoast/oktoast.dart'; 4 | 5 | class App extends StatelessWidget { 6 | @override 7 | Widget build(BuildContext context) { 8 | return OKToast( 9 | position: ToastPosition.bottom, 10 | handleTouth: true, 11 | dismissOtherOnShow: true, 12 | child: MaterialApp( 13 | title: 'Flutter chat', 14 | theme: ThemeData.dark(), 15 | debugShowCheckedModeBanner: false, 16 | initialRoute: "/", 17 | onGenerateRoute: Modular.generateRoute, 18 | navigatorKey: Modular.navigatorKey, 19 | navigatorObservers: [ 20 | // AppRouteObserver() 21 | ], 22 | ), 23 | ); 24 | } 25 | } 26 | 27 | // class AppRouteObserver extends RouteObserver> { 28 | // final SettingsStore settingsStore = Modular.get(); 29 | 30 | // @override 31 | // void didPush(Route route, Route previousRoute) { 32 | // super.didPush(route, previousRoute); 33 | // if (route is PageRoute) { 34 | // print("didpush route $route"); 35 | // settingsStore.setCurrentPage(APP_ROUTE.DASHBOARD); 36 | // } 37 | // } 38 | 39 | // @override 40 | // void didReplace({Route newRoute, Route oldRoute}) { 41 | // super.didReplace(newRoute: newRoute, oldRoute: oldRoute); 42 | // if (newRoute is PageRoute) { 43 | // print("replace route $newRoute"); 44 | // } 45 | // } 46 | 47 | // @override 48 | // void didPop(Route route, Route previousRoute) { 49 | // super.didPop(route, previousRoute); 50 | // if (previousRoute is PageRoute && route is PageRoute) { 51 | // print("didpop route $route"); 52 | // } 53 | // } 54 | // } 55 | -------------------------------------------------------------------------------- /lib/app_module.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_chat/pages/chat_page.dart'; 3 | import 'package:flutter_chat/pages/chats_list_page.dart'; 4 | import 'package:flutter_chat/pages/login_page.dart'; 5 | import 'package:flutter_chat/pages/home_page.dart'; 6 | import 'package:flutter_chat/pages/new_chat_page.dart'; 7 | import 'package:flutter_chat/routes/app_routes.dart'; 8 | import 'package:flutter_chat/services/firebase_service.dart'; 9 | import 'package:flutter_modular/flutter_modular.dart'; 10 | import 'package:flutter_chat/services/notifications_service.dart'; 11 | import 'package:flutter_chat/stores/main_store.dart'; 12 | 13 | import './app.dart'; 14 | 15 | class AppModule extends MainModule { 16 | NotificationsService notificationsService = NotificationsService(); 17 | 18 | MainStore mainStore = MainStore(FirebaseService()); 19 | 20 | @override 21 | List get binds => [ 22 | Bind((i) => notificationsService), 23 | Bind((i) => mainStore.authStore), 24 | Bind((i) => mainStore.chatStore), 25 | Bind((i) => mainStore.settingsStore), 26 | ]; 27 | 28 | @override 29 | List get routers => [ 30 | Router( 31 | pathForRoute(APP_ROUTE.LOGIN), 32 | child: (_, args) => LoginPage(), 33 | ), 34 | Router( 35 | pathForRoute(APP_ROUTE.HOME), 36 | child: (_, args) => HomePage(), 37 | ), 38 | Router( 39 | pathForRoute(APP_ROUTE.CHATS_LIST), 40 | child: (_, args) => ChatsListPage(), 41 | ), 42 | Router( 43 | pathForRoute(APP_ROUTE.NEW_CHAT), 44 | child: (_, args) => NewChatPage(), 45 | ), 46 | Router( 47 | "${pathForRoute(APP_ROUTE.CHAT)}:id", 48 | child: (_, args) => ChatPage(), 49 | ), 50 | ]; 51 | 52 | @override 53 | Widget get bootstrap => App(); 54 | } 55 | -------------------------------------------------------------------------------- /lib/components/badge/badge.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class Badge extends StatelessWidget { 4 | final int value; 5 | const Badge({Key key, this.value}) : super(key: key); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | var moreThan99 = this.value > 99; 10 | return ClipRRect( 11 | borderRadius: BorderRadius.circular(15), 12 | child: Container( 13 | color: Colors.green, 14 | height: 30, 15 | width: 30, 16 | child: Center( 17 | child: Text( 18 | moreThan99 ? "99+" : "$value", 19 | style: TextStyle(fontSize: moreThan99 ? 12 : 16), 20 | ), 21 | ), 22 | ), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/components/button/attachment_button.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_chat/utils/image_utils.dart'; 5 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 6 | import 'package:image_picker/image_picker.dart'; 7 | 8 | class AttachmentButton extends StatelessWidget { 9 | final Function(File) didSelectFile; 10 | const AttachmentButton({Key key, this.didSelectFile}) : super(key: key); 11 | 12 | PopupMenuItem attachmentMenuItem({ 13 | dynamic value, 14 | String label, 15 | IconData icon, 16 | }) => 17 | PopupMenuItem( 18 | value: value, 19 | child: Row(children: [ 20 | Icon(icon), 21 | Container(width: 16), 22 | Text(label), 23 | ]), 24 | ); 25 | 26 | didSelect(BuildContext context, _selectedValue) async { 27 | var source = ImageSource.gallery; 28 | if (_selectedValue == 0) { 29 | source = ImageSource.camera; 30 | } 31 | var file = await pickImage(context: context, source: source); 32 | this.didSelectFile(file); 33 | } 34 | 35 | @override 36 | Widget build(BuildContext context) { 37 | return PopupMenuButton( 38 | icon: Icon(FontAwesomeIcons.paperclip), 39 | onSelected: (_selectedValue) => didSelect(context, _selectedValue), 40 | itemBuilder: (BuildContext context) => [ 41 | attachmentMenuItem( 42 | icon: FontAwesomeIcons.camera, 43 | label: "Câmera", 44 | value: 0, 45 | ), 46 | attachmentMenuItem( 47 | icon: FontAwesomeIcons.images, 48 | label: "Biblioteca de fotos", 49 | value: 1, 50 | ), 51 | ], 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/components/button/button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 3 | 4 | class Button extends StatelessWidget { 5 | final String label; 6 | final IconData icon; 7 | final Color color; 8 | final Color labelColor; 9 | final Function onPressed; 10 | 11 | const Button({ 12 | Key key, 13 | this.onPressed, 14 | this.label, 15 | this.icon, 16 | this.color, 17 | this.labelColor, 18 | }) : super(key: key); 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return RaisedButton( 23 | color: color, 24 | child: Row( 25 | mainAxisAlignment: MainAxisAlignment.center, 26 | children: [ 27 | Text( 28 | this.label.toUpperCase(), 29 | style: TextStyle( 30 | color: this.labelColor, 31 | ), 32 | ), 33 | SizedBox(width: 4), 34 | Icon( 35 | this.icon, 36 | color: labelColor, 37 | size: 20, 38 | ), 39 | ], 40 | ), 41 | onPressed: this.onPressed, 42 | ); 43 | } 44 | } 45 | 46 | class PrimaryButton extends StatelessWidget { 47 | final String label; 48 | final IconData icon; 49 | final Function onPressed; 50 | 51 | const PrimaryButton({ 52 | Key key, 53 | this.onPressed, 54 | this.label, 55 | this.icon, 56 | }) : super(key: key); 57 | 58 | @override 59 | Widget build(BuildContext context) { 60 | return Button( 61 | label: this.label, 62 | icon: this.icon, 63 | color: Colors.blue, 64 | labelColor: Colors.white, 65 | onPressed: this.onPressed, 66 | ); 67 | } 68 | } 69 | 70 | class FormButton extends StatelessWidget { 71 | final String label; 72 | final bool isEditing; 73 | final Function onPressed; 74 | const FormButton({Key key, this.label, this.isEditing, this.onPressed}) 75 | : super(key: key); 76 | 77 | @override 78 | Widget build(BuildContext context) { 79 | return PrimaryButton( 80 | label: this.label?.isNotEmpty ?? false 81 | ? this.label 82 | : this.isEditing ? "ATUALIZAR" : "SALVAR", 83 | icon: FontAwesomeIcons.solidSave, 84 | onPressed: this.onPressed, 85 | ); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lib/components/form/input_text.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class InputText extends StatelessWidget { 4 | final IconData icon; 5 | final String label; 6 | final String value; 7 | final bool multiline; 8 | final int maxLength; 9 | final String Function(String) validator; 10 | final Function onSaved; 11 | 12 | const InputText({ 13 | Key key, 14 | this.value, 15 | this.validator, 16 | this.onSaved, 17 | this.icon, 18 | this.label, 19 | this.multiline = false, 20 | this.maxLength, 21 | }) : super(key: key); 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return ListTile( 26 | leading: Icon(icon), 27 | title: TextFormField( 28 | maxLength: this.maxLength, 29 | maxLines: this.multiline ? null : 1, 30 | decoration: InputDecoration( 31 | labelText: label, 32 | ), 33 | validator: this.validator, 34 | initialValue: this.value, 35 | onSaved: this.onSaved, 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/components/loading/loading_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class LoadingView extends StatelessWidget { 4 | @override 5 | Widget build(BuildContext context) { 6 | var theme = Theme.of(context); 7 | return Container( 8 | color: theme.scaffoldBackgroundColor, 9 | child: Center( 10 | child: CircularProgressIndicator(), 11 | ), 12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/components/search/search_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_mobx/flutter_mobx.dart'; 3 | 4 | class SearchButton extends StatelessWidget { 5 | final bool isSearching; 6 | final Function toggleIsSearching; 7 | 8 | const SearchButton({ 9 | Key key, 10 | this.isSearching, 11 | this.toggleIsSearching, 12 | }) : super(key: key); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Observer( 17 | builder: (_) => IconButton( 18 | icon: Icon( 19 | this.isSearching ? Icons.close : Icons.search, 20 | ), 21 | onPressed: this.toggleIsSearching, 22 | ), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/extensions/text_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | extension TextExtension on Text { 4 | Container withMargin({TextStyle style, EdgeInsets margin}) { 5 | EdgeInsets defaultMargin = EdgeInsets.only(top: 4, bottom: 8); 6 | return Container( 7 | margin: margin ?? defaultMargin, 8 | child: Text( 9 | this.data, 10 | style: style, 11 | ), 12 | ); 13 | } 14 | 15 | Widget h1({TextStyle style, EdgeInsets margin}) { 16 | TextStyle defaultStyle = TextStyle( 17 | fontSize: 36, 18 | fontWeight: FontWeight.w900, 19 | fontStyle: FontStyle.normal, 20 | letterSpacing: -0.02, 21 | ); 22 | return withMargin( 23 | style: (this.style ?? defaultStyle).merge( 24 | style ?? defaultStyle, 25 | ), 26 | margin: margin, 27 | ); 28 | } 29 | 30 | Widget h2({TextStyle style, EdgeInsets margin}) { 31 | TextStyle defaultStyle = TextStyle( 32 | fontSize: 22, 33 | fontWeight: FontWeight.w600, 34 | fontStyle: FontStyle.normal, 35 | letterSpacing: -0.02, 36 | ); 37 | 38 | return withMargin( 39 | style: (this.style ?? defaultStyle).merge( 40 | style ?? defaultStyle, 41 | ), 42 | margin: margin, 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_modular/flutter_modular.dart'; 3 | 4 | import "./app_module.dart"; 5 | 6 | void main() { 7 | WidgetsFlutterBinding.ensureInitialized(); 8 | 9 | runApp( 10 | ModularApp(module: AppModule()), 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /lib/model/author_model.dart: -------------------------------------------------------------------------------- 1 | enum AuthorType { 2 | admin, 3 | user, 4 | } 5 | 6 | class AuthorModel { 7 | AuthorType type; 8 | bool isConnected; 9 | bool isReading; 10 | bool isTyping; 11 | int unreadMessagesCount; 12 | 13 | AuthorModel({ 14 | this.type, 15 | this.isConnected, 16 | this.isReading, 17 | this.isTyping, 18 | this.unreadMessagesCount, 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /lib/model/chat_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_chat/model/author_model.dart'; 2 | 3 | class ChatModel { 4 | String id; 5 | AuthorModel admin; 6 | AuthorModel user; 7 | int createdAt; 8 | bool hasUserMessage; 9 | bool isClosed; 10 | String lastMessage; 11 | int lastMessageTimestamp; 12 | AuthorType lastMessageAuthor; 13 | int messagesCount; 14 | String subject; 15 | String userId; 16 | 17 | ChatModel({ 18 | this.id, 19 | this.admin, 20 | this.user, 21 | this.createdAt, 22 | this.hasUserMessage, 23 | this.isClosed, 24 | this.lastMessage, 25 | this.lastMessageTimestamp, 26 | this.lastMessageAuthor, 27 | this.messagesCount, 28 | this.subject, 29 | this.userId, 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /lib/model/form/user_form_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:mobx/mobx.dart'; 2 | import 'package:flutter_chat/model/user_model.dart'; 3 | part 'user_form_model.g.dart'; 4 | 5 | class UserFormModel = _UserFormModelBase with _$UserFormModel; 6 | 7 | abstract class _UserFormModelBase with Store { 8 | String token; 9 | 10 | _UserFormModelBase(UserModel currentUser) { 11 | this.token = currentUser.token; 12 | 13 | this.setName(currentUser.name); 14 | this.setEmail(currentUser.email); 15 | } 16 | 17 | @observable 18 | String name; 19 | @observable 20 | String email; 21 | @observable 22 | String password = ""; 23 | @observable 24 | String confirmPassword = ""; 25 | 26 | @observable 27 | bool isWaiting = false; 28 | 29 | @action 30 | setIsWaiting(bool newValue) => this.isWaiting = newValue; 31 | @action 32 | setName(String newValue) => this.name = newValue; 33 | @action 34 | setEmail(String newValue) => this.email = newValue; 35 | @action 36 | setPassword(String newValue) => this.password = newValue; 37 | @action 38 | setConfirmPassword(String newValue) => this.confirmPassword = newValue; 39 | 40 | @computed 41 | String get validateName { 42 | if (this.name.length < 3) { 43 | return "Digite pelo menos 3 caracteres"; 44 | } 45 | return null; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/model/form/user_form_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'user_form_model.dart'; 4 | 5 | // ************************************************************************** 6 | // StoreGenerator 7 | // ************************************************************************** 8 | 9 | // ignore_for_file: non_constant_identifier_names, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic 10 | 11 | mixin _$UserFormModel on _UserFormModelBase, Store { 12 | Computed _$validateNameComputed; 13 | 14 | @override 15 | String get validateName => 16 | (_$validateNameComputed ??= Computed(() => super.validateName)) 17 | .value; 18 | 19 | final _$nameAtom = Atom(name: '_UserFormModelBase.name'); 20 | 21 | @override 22 | String get name { 23 | _$nameAtom.context.enforceReadPolicy(_$nameAtom); 24 | _$nameAtom.reportObserved(); 25 | return super.name; 26 | } 27 | 28 | @override 29 | set name(String value) { 30 | _$nameAtom.context.conditionallyRunInAction(() { 31 | super.name = value; 32 | _$nameAtom.reportChanged(); 33 | }, _$nameAtom, name: '${_$nameAtom.name}_set'); 34 | } 35 | 36 | final _$emailAtom = Atom(name: '_UserFormModelBase.email'); 37 | 38 | @override 39 | String get email { 40 | _$emailAtom.context.enforceReadPolicy(_$emailAtom); 41 | _$emailAtom.reportObserved(); 42 | return super.email; 43 | } 44 | 45 | @override 46 | set email(String value) { 47 | _$emailAtom.context.conditionallyRunInAction(() { 48 | super.email = value; 49 | _$emailAtom.reportChanged(); 50 | }, _$emailAtom, name: '${_$emailAtom.name}_set'); 51 | } 52 | 53 | final _$passwordAtom = Atom(name: '_UserFormModelBase.password'); 54 | 55 | @override 56 | String get password { 57 | _$passwordAtom.context.enforceReadPolicy(_$passwordAtom); 58 | _$passwordAtom.reportObserved(); 59 | return super.password; 60 | } 61 | 62 | @override 63 | set password(String value) { 64 | _$passwordAtom.context.conditionallyRunInAction(() { 65 | super.password = value; 66 | _$passwordAtom.reportChanged(); 67 | }, _$passwordAtom, name: '${_$passwordAtom.name}_set'); 68 | } 69 | 70 | final _$confirmPasswordAtom = 71 | Atom(name: '_UserFormModelBase.confirmPassword'); 72 | 73 | @override 74 | String get confirmPassword { 75 | _$confirmPasswordAtom.context.enforceReadPolicy(_$confirmPasswordAtom); 76 | _$confirmPasswordAtom.reportObserved(); 77 | return super.confirmPassword; 78 | } 79 | 80 | @override 81 | set confirmPassword(String value) { 82 | _$confirmPasswordAtom.context.conditionallyRunInAction(() { 83 | super.confirmPassword = value; 84 | _$confirmPasswordAtom.reportChanged(); 85 | }, _$confirmPasswordAtom, name: '${_$confirmPasswordAtom.name}_set'); 86 | } 87 | 88 | final _$isWaitingAtom = Atom(name: '_UserFormModelBase.isWaiting'); 89 | 90 | @override 91 | bool get isWaiting { 92 | _$isWaitingAtom.context.enforceReadPolicy(_$isWaitingAtom); 93 | _$isWaitingAtom.reportObserved(); 94 | return super.isWaiting; 95 | } 96 | 97 | @override 98 | set isWaiting(bool value) { 99 | _$isWaitingAtom.context.conditionallyRunInAction(() { 100 | super.isWaiting = value; 101 | _$isWaitingAtom.reportChanged(); 102 | }, _$isWaitingAtom, name: '${_$isWaitingAtom.name}_set'); 103 | } 104 | 105 | final _$_UserFormModelBaseActionController = 106 | ActionController(name: '_UserFormModelBase'); 107 | 108 | @override 109 | dynamic setIsWaiting(bool newValue) { 110 | final _$actionInfo = _$_UserFormModelBaseActionController.startAction(); 111 | try { 112 | return super.setIsWaiting(newValue); 113 | } finally { 114 | _$_UserFormModelBaseActionController.endAction(_$actionInfo); 115 | } 116 | } 117 | 118 | @override 119 | dynamic setName(String newValue) { 120 | final _$actionInfo = _$_UserFormModelBaseActionController.startAction(); 121 | try { 122 | return super.setName(newValue); 123 | } finally { 124 | _$_UserFormModelBaseActionController.endAction(_$actionInfo); 125 | } 126 | } 127 | 128 | @override 129 | dynamic setEmail(String newValue) { 130 | final _$actionInfo = _$_UserFormModelBaseActionController.startAction(); 131 | try { 132 | return super.setEmail(newValue); 133 | } finally { 134 | _$_UserFormModelBaseActionController.endAction(_$actionInfo); 135 | } 136 | } 137 | 138 | @override 139 | dynamic setPassword(String newValue) { 140 | final _$actionInfo = _$_UserFormModelBaseActionController.startAction(); 141 | try { 142 | return super.setPassword(newValue); 143 | } finally { 144 | _$_UserFormModelBaseActionController.endAction(_$actionInfo); 145 | } 146 | } 147 | 148 | @override 149 | dynamic setConfirmPassword(String newValue) { 150 | final _$actionInfo = _$_UserFormModelBaseActionController.startAction(); 151 | try { 152 | return super.setConfirmPassword(newValue); 153 | } finally { 154 | _$_UserFormModelBaseActionController.endAction(_$actionInfo); 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /lib/model/message_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_chat/model/author_model.dart'; 3 | import 'package:flutter_chat/utils/date_utils.dart'; 4 | 5 | enum MessageType { 6 | text, 7 | image, 8 | } 9 | 10 | class MessageModel { 11 | String id; 12 | AuthorType author; 13 | bool isRead; 14 | String message; 15 | int timestamp; 16 | MessageType type; 17 | String adminName; 18 | String imageUrl; 19 | String fileExtension; 20 | 21 | MessageModel({ 22 | @required this.id, 23 | @required this.author, 24 | @required this.isRead, 25 | @required this.message, 26 | @required this.timestamp, 27 | @required this.type, 28 | this.adminName, 29 | this.imageUrl, 30 | this.fileExtension, 31 | }); 32 | 33 | String get formattedData { 34 | return formattedDayAndHourFromFirebaseTimestamp(this.timestamp); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/model/user_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | 3 | class UserModel { 4 | String name; 5 | String email; 6 | String token; 7 | bool isAdmin; 8 | bool isAnonymous; 9 | 10 | UserModel({ 11 | @required this.name, 12 | @required this.email, 13 | @required this.token, 14 | @required this.isAdmin, 15 | @required this.isAnonymous, 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /lib/pages/chat_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_chat/components/button/attachment_button.dart'; 5 | import 'package:flutter_chat/model/message_model.dart'; 6 | import 'package:flutter_chat/services/notifications_service.dart'; 7 | import 'package:flutter_chat/stores/chat_store.dart'; 8 | import 'package:flutter_chat/utils/utils.dart'; 9 | import 'package:flutter_mobx/flutter_mobx.dart'; 10 | import 'package:flutter_modular/flutter_modular.dart'; 11 | import 'package:flutter_slidable/flutter_slidable.dart'; 12 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 13 | 14 | import "package:flutter_chat/extensions/text_extension.dart"; 15 | 16 | enum MenuOption { delete, update } 17 | 18 | class ChatPage extends StatefulWidget { 19 | @override 20 | _ChatPageState createState() => _ChatPageState(); 21 | } 22 | 23 | class _ChatPageState extends State { 24 | final SlidableController slidableController = SlidableController(); 25 | 26 | Offset _tapPosition; 27 | 28 | _storePosition(TapDownDetails details) { 29 | _tapPosition = details.globalPosition; 30 | } 31 | 32 | _didWantToDelete(String messageId) { 33 | //final ChatStore chatStore = Modular.get(); 34 | } 35 | 36 | _didWantToEdit(String messageId) { 37 | final ChatStore chatStore = Modular.get(); 38 | chatStore.setSelectedMessageId(messageId); 39 | } 40 | 41 | Widget renderMessageAdminName( 42 | String adminName, 43 | bool isAdminView, 44 | ) { 45 | NotificationsService notificationsService = 46 | Modular.get(); 47 | if (isAdminView && adminName != null && adminName.isNotEmpty) { 48 | return IconButton( 49 | icon: Icon( 50 | FontAwesomeIcons.user, 51 | size: 12, 52 | ), 53 | onPressed: () => 54 | notificationsService.notifyInfo("Escrito por: $adminName"), 55 | ); 56 | } 57 | return Container(); 58 | } 59 | 60 | didSelectMessage(String messageId) async { 61 | final RenderBox overlay = Overlay.of(context).context.findRenderObject(); 62 | 63 | var selectedValue = await showMenu( 64 | context: context, 65 | items: [ 66 | PopupMenuItem( 67 | value: MenuOption.update, 68 | child: Row( 69 | mainAxisAlignment: MainAxisAlignment.spaceAround, 70 | children: [ 71 | Text("Editar"), 72 | Icon(FontAwesomeIcons.pen), 73 | ], 74 | ), 75 | ), 76 | PopupMenuItem( 77 | value: MenuOption.delete, 78 | child: Row( 79 | mainAxisAlignment: MainAxisAlignment.spaceAround, 80 | children: [ 81 | Text( 82 | "Apagar", 83 | style: TextStyle( 84 | color: Colors.red, 85 | ), 86 | ), 87 | Icon( 88 | FontAwesomeIcons.trashAlt, 89 | color: Colors.red, 90 | ), 91 | ], 92 | ), 93 | ) 94 | ], 95 | position: RelativeRect.fromRect( 96 | _tapPosition & Size(40, 40), // smaller rect, the touch area 97 | Offset.zero & overlay.size, // Bigger rect, the entire screen 98 | ), 99 | ); 100 | if (selectedValue == MenuOption.update) { 101 | _didWantToEdit(messageId); 102 | } else if (selectedValue == MenuOption.delete) { 103 | _didWantToDelete(messageId); 104 | } 105 | } 106 | 107 | Widget renderMessagesList(BuildContext context, ChatStore chatStore) { 108 | return Observer( 109 | builder: (_) { 110 | var isAdminView = chatStore.isAdminView; 111 | var messagesList = chatStore.selectedChatMessagesList; 112 | return Container( 113 | padding: EdgeInsets.only(top: 16, left: 16, right: 16), 114 | child: messagesList == null 115 | ? Center( 116 | child: Column( 117 | mainAxisAlignment: MainAxisAlignment.center, 118 | children: [ 119 | Text("Nenhuma mensagem nesta conversa."), 120 | Container(height: 20), 121 | Icon(FontAwesomeIcons.solidComments) 122 | ], 123 | ), 124 | ) 125 | : ListView.builder( 126 | itemCount: messagesList.length, 127 | itemBuilder: (BuildContext context, int index) { 128 | var message = messagesList[index]; 129 | var isMineMessage = 130 | messageIsMine(isAdminView, message.author); 131 | debugPrint("isMineMessage $isMineMessage"); 132 | 133 | var textAlign = 134 | isMineMessage ? TextAlign.right : TextAlign.left; 135 | return GestureDetector( 136 | onLongPress: () => didSelectMessage(message.id), 137 | onTapDown: _storePosition, 138 | child: renderSlidable( 139 | message, 140 | textAlign, 141 | isMineMessage, 142 | isAdminView, 143 | ), 144 | ); 145 | }, 146 | ), 147 | ); 148 | }, 149 | ); 150 | } 151 | 152 | Widget _slidableAction({IconData icon, Color color, Function onTap}) { 153 | return Center( 154 | child: ClipRRect( 155 | borderRadius: BorderRadius.circular(20), 156 | child: Container( 157 | height: 70, 158 | width: 70, 159 | decoration: BoxDecoration( 160 | borderRadius: BorderRadius.circular(16.0), 161 | ), 162 | child: IconSlideAction( 163 | icon: icon, 164 | color: color, 165 | onTap: onTap, 166 | ), 167 | ), 168 | ), 169 | ); 170 | } 171 | 172 | Widget renderSlidable( 173 | MessageModel message, 174 | TextAlign textAlign, 175 | bool isMineMessage, 176 | bool isAdminView, 177 | ) { 178 | return Container( 179 | margin: EdgeInsets.symmetric( 180 | horizontal: 4, 181 | vertical: 4, 182 | ), 183 | child: Slidable( 184 | controller: slidableController, 185 | child: Card( 186 | color: isMineMessage ? Colors.green.shade600 : null, 187 | child: this.renderMessageTile( 188 | message, 189 | textAlign, 190 | isMineMessage, 191 | isAdminView, 192 | )), 193 | actionPane: SlidableDrawerActionPane(), 194 | actionExtentRatio: 0.25, 195 | secondaryActions: [ 196 | _slidableAction( 197 | icon: FontAwesomeIcons.pen, 198 | color: Colors.blue, 199 | onTap: () => _didWantToEdit(message.id), 200 | ), 201 | _slidableAction( 202 | icon: FontAwesomeIcons.trashAlt, 203 | color: Colors.red, 204 | onTap: () => _didWantToDelete(message.id), 205 | ), 206 | ], 207 | ), 208 | ); 209 | } 210 | 211 | Widget renderMessageListTileTitle({ 212 | TextAlign textAlign, 213 | String message, 214 | String imageUrl, 215 | }) { 216 | var messageText = Text( 217 | message, 218 | textAlign: textAlign, 219 | ).withMargin(); 220 | 221 | if (imageUrl != null) { 222 | return Column( 223 | crossAxisAlignment: textAlign == TextAlign.left 224 | ? CrossAxisAlignment.start 225 | : CrossAxisAlignment.end, 226 | children: [ 227 | Container( 228 | height: 100, 229 | decoration: BoxDecoration( 230 | image: DecorationImage( 231 | alignment: Alignment(-.2, 0), 232 | image: NetworkImage( 233 | imageUrl, 234 | ), 235 | fit: BoxFit.cover, 236 | ), 237 | ), 238 | ), 239 | message.isNotEmpty ? messageText : Container(height: 8), 240 | ], 241 | ); 242 | } 243 | return messageText; 244 | } 245 | 246 | ListTile renderMessageTile( 247 | MessageModel message, 248 | TextAlign textAlign, 249 | bool isMineMessage, 250 | bool isAdminView, 251 | ) { 252 | return ListTile( 253 | contentPadding: EdgeInsets.symmetric(vertical: 2, horizontal: 8), 254 | title: renderMessageListTileTitle( 255 | textAlign: textAlign, 256 | message: message.message, 257 | imageUrl: message.imageUrl, 258 | ), 259 | subtitle: Row( 260 | mainAxisAlignment: 261 | isMineMessage ? MainAxisAlignment.end : MainAxisAlignment.start, 262 | children: [ 263 | this.renderMessageAdminName( 264 | message.adminName, 265 | isAdminView, 266 | ), 267 | Text( 268 | message.formattedData, 269 | textAlign: textAlign, 270 | style: TextStyle( 271 | fontStyle: FontStyle.italic, 272 | ), 273 | ), 274 | ], 275 | ), 276 | ); 277 | } 278 | 279 | didSelectFile(File file) { 280 | print("didSelectFile"); 281 | print(file); 282 | } 283 | 284 | Widget renderChatActions(ChatStore chatStore) { 285 | return Row( 286 | children: [ 287 | AttachmentButton(didSelectFile: this.didSelectFile), 288 | Expanded( 289 | child: Container( 290 | child: TextFormField( 291 | decoration: InputDecoration( 292 | labelText: 'Mensagem...', 293 | ), 294 | onSaved: chatStore.onChangeMessage, 295 | ), 296 | ), 297 | ), 298 | IconButton( 299 | icon: Icon(FontAwesomeIcons.solidPaperPlane), 300 | onPressed: chatStore.didSendMessage, 301 | ), 302 | ], 303 | ); 304 | } 305 | 306 | @override 307 | Widget build(BuildContext context) { 308 | final ChatStore chatStore = Modular.get(); 309 | 310 | return WillPopScope( 311 | onWillPop: () { 312 | chatStore.setSelectedChatId(null); 313 | return new Future(() => false); 314 | }, 315 | child: Scaffold( 316 | appBar: AppBar( 317 | title: Observer( 318 | builder: (_) { 319 | return Text(chatStore.selectedChat?.subject ?? ""); 320 | }, 321 | ), 322 | ), 323 | body: SafeArea(child: renderMessagesList(context, chatStore)), 324 | bottomNavigationBar: SafeArea( 325 | child: Card( 326 | margin: EdgeInsets.zero, 327 | elevation: 10, 328 | child: renderChatActions(chatStore), 329 | ), 330 | ), 331 | ), 332 | ); 333 | } 334 | } 335 | -------------------------------------------------------------------------------- /lib/pages/chats_list_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_chat/components/badge/badge.dart'; 3 | import 'package:flutter_chat/components/loading/loading_view.dart'; 4 | import 'package:flutter_chat/routes/app_routes.dart'; 5 | import 'package:flutter_chat/stores/auth_store.dart'; 6 | import 'package:flutter_chat/stores/chat_store.dart'; 7 | import 'package:flutter_chat/utils/date_utils.dart'; 8 | import 'package:flutter_mobx/flutter_mobx.dart'; 9 | import 'package:flutter_modular/flutter_modular.dart'; 10 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 11 | 12 | import "package:flutter_chat/utils/utils.dart"; 13 | import "package:flutter_chat/extensions/text_extension.dart"; 14 | 15 | class ChatsListPage extends StatelessWidget { 16 | Widget renderBody(AuthStore authStore) { 17 | final ChatStore chatStore = Modular.get(); 18 | return Observer(builder: (_) { 19 | if (!authStore.didVerifyIsLoggedIn) { 20 | return LoadingView(); 21 | } 22 | 23 | var isAdminView = chatStore.isAdminView; 24 | var chatsList = chatStore.chatsList; 25 | 26 | return ListView.builder( 27 | itemCount: chatsList.length, 28 | itemBuilder: (BuildContext context, int index) { 29 | var chat = chatsList[index]; 30 | 31 | var lastMessageIsMine = 32 | messageIsMine(isAdminView, chat.lastMessageAuthor); 33 | 34 | return Card( 35 | child: ListTile( 36 | onTap: () => chatStore.setSelectedChatId(chat.id), 37 | title: Text( 38 | chat.subject, 39 | ).h2(), 40 | subtitle: Column( 41 | crossAxisAlignment: CrossAxisAlignment.start, 42 | children: [ 43 | Text( 44 | chat.lastMessage, 45 | style: TextStyle(fontSize: 16), 46 | ), 47 | Container(height: 8), 48 | Text( 49 | formattedDayAndHourFromFirebaseTimestamp( 50 | chat.lastMessageTimestamp), 51 | style: TextStyle( 52 | fontStyle: FontStyle.italic, 53 | ), 54 | ) 55 | ], 56 | ), 57 | trailing: Column( 58 | mainAxisAlignment: MainAxisAlignment.center, 59 | children: [Badge(value: chat.messagesCount)], 60 | ), 61 | ), 62 | ); 63 | }, 64 | ); 65 | }); 66 | } 67 | 68 | @override 69 | Widget build(BuildContext context) { 70 | final AuthStore authStore = Modular.get(); 71 | return Scaffold( 72 | appBar: AppBar( 73 | leading: IconButton( 74 | icon: Icon(Icons.exit_to_app), 75 | onPressed: authStore.logout, 76 | ), 77 | title: Text("Suas conversas"), 78 | centerTitle: true, 79 | ), 80 | body: SafeArea( 81 | child: Container( 82 | padding: EdgeInsets.all(12), 83 | child: Column(children: [ 84 | Expanded( 85 | child: renderBody(authStore), 86 | ), 87 | ]), 88 | ), 89 | ), 90 | floatingActionButton: FloatingActionButton( 91 | child: Icon(FontAwesomeIcons.plus), 92 | onPressed: () => Modular.to.pushNamed(pathForRoute(APP_ROUTE.NEW_CHAT)), 93 | ), 94 | ); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /lib/pages/home_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_chat/components/button/button.dart'; 3 | import 'package:flutter_chat/components/loading/loading_view.dart'; 4 | import 'package:flutter_chat/services/notifications_service.dart'; 5 | import 'package:flutter_chat/stores/auth_store.dart'; 6 | import 'package:flutter_chat/stores/chat_store.dart'; 7 | import 'package:flutter_mobx/flutter_mobx.dart'; 8 | import 'package:flutter_modular/flutter_modular.dart'; 9 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 10 | 11 | class HomePage extends StatelessWidget { 12 | @override 13 | Widget build(BuildContext context) { 14 | AuthStore authStore = Modular.get(); 15 | ChatStore chatStore = Modular.get(); 16 | NotificationsService notificationsService = 17 | Modular.get(); 18 | 19 | ThemeData theme = Theme.of(context); 20 | 21 | return Scaffold( 22 | appBar: AppBar( 23 | title: Text("Flutter Chat"), 24 | ), 25 | body: Observer( 26 | builder: (_) { 27 | if (!authStore.didVerifyIsLoggedIn) { 28 | return LoadingView(); 29 | } 30 | 31 | return Center( 32 | child: Container( 33 | width: 300, 34 | child: Column( 35 | mainAxisAlignment: MainAxisAlignment.center, 36 | children: [ 37 | Text("Bem vindo!"), 38 | Container(height: 20), 39 | Button( 40 | color: Colors.blue, 41 | icon: FontAwesomeIcons.comments, 42 | label: "Abrir Chat", 43 | onPressed: () => chatStore.setSelectedChatId("chat1"), 44 | ), 45 | Container(height: 20), 46 | Button( 47 | color: Colors.lightBlue, 48 | icon: Icons.notifications, 49 | label: "Notificar informação", 50 | onPressed: () => notificationsService 51 | .notifyInfo("Mensagem de informação"), 52 | ), 53 | Button( 54 | color: Colors.green, 55 | icon: Icons.notifications, 56 | label: "Notificar sucesso", 57 | onPressed: () => notificationsService 58 | .notifySuccess("Mensagem de sucesso!"), 59 | ), 60 | Button( 61 | color: Colors.orange, 62 | icon: Icons.notifications, 63 | label: "Notificar alerta", 64 | onPressed: () => notificationsService 65 | .notifyWarning("Mensagem de alerta"), 66 | ), 67 | Button( 68 | color: Colors.red, 69 | icon: Icons.notifications, 70 | label: "Notificar erro", 71 | onPressed: () => 72 | notificationsService.notifyDanger("Mensagem de erro!"), 73 | ), 74 | Container(height: 80), 75 | Button( 76 | color: Colors.white, 77 | labelColor: theme.primaryColor, 78 | icon: Icons.exit_to_app, 79 | label: "Sair", 80 | onPressed: authStore.logout, 81 | ), 82 | ], 83 | ), 84 | ), 85 | ); 86 | }, 87 | ), 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /lib/pages/login_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_mobx/flutter_mobx.dart'; 3 | import 'package:flutter_modular/flutter_modular.dart'; 4 | import 'package:flutter_chat/components/button/button.dart'; 5 | 6 | import 'package:flutter_chat/components/loading/loading_view.dart'; 7 | import 'package:flutter_chat/stores/auth_store.dart'; 8 | import 'package:flutter_chat/utils/validate_field_util.dart'; 9 | 10 | class LoginPage extends StatefulWidget { 11 | @override 12 | State createState() => _LoginPageState(); 13 | } 14 | 15 | class _LoginPageState extends State { 16 | final GlobalKey _formKey = GlobalKey(); 17 | 18 | void submit(AuthStore authStore) async { 19 | // First validate form. 20 | if (_formKey.currentState.validate()) { 21 | _formKey.currentState.save(); // Save our form now. 22 | 23 | await authStore.submitLogin(); 24 | } 25 | } 26 | 27 | Widget renderButton({ 28 | Color color = Colors.blue, 29 | String label, 30 | IconData icon, 31 | Function onPressed, 32 | }) { 33 | final Size screenSize = MediaQuery.of(context).size; 34 | 35 | return ListTile( 36 | title: Container( 37 | width: screenSize.width, 38 | child: Button( 39 | color: color, 40 | icon: icon, 41 | label: label, 42 | onPressed: onPressed, 43 | ), 44 | )); 45 | } 46 | 47 | Widget _renderErrorMessage(AuthStore authStore) { 48 | if (authStore.errorMessage.isEmpty) { 49 | return Container(); 50 | } 51 | 52 | return ListTile( 53 | title: Container( 54 | margin: EdgeInsets.only(top: 25.0, left: 5.0, right: 5.0), 55 | child: Text( 56 | authStore.errorMessage, 57 | style: TextStyle( 58 | color: Colors.red, 59 | fontSize: 16.0, 60 | ), 61 | textAlign: TextAlign.center, 62 | ), 63 | ), 64 | ); 65 | } 66 | 67 | Widget renderBody(AuthStore authStore) { 68 | return Observer( 69 | builder: (_) { 70 | if (authStore.isFetching) { 71 | return LoadingView(); 72 | } 73 | return Container( 74 | padding: EdgeInsets.all(20.0), 75 | alignment: Alignment.center, 76 | child: Form( 77 | key: this._formKey, 78 | child: ListView( 79 | children: [ 80 | ListTile( 81 | title: TextFormField( 82 | keyboardType: TextInputType.emailAddress, 83 | autocorrect: false, 84 | // Use email input type for emails. 85 | decoration: InputDecoration( 86 | icon: Icon(Icons.email), 87 | labelText: 'Informe seu e-mail', 88 | ), 89 | initialValue: authStore.email, 90 | validator: ValidateFieldUtil.validateEmail, 91 | onSaved: authStore.setEmail, 92 | ), 93 | ), 94 | ListTile( 95 | title: TextFormField( 96 | obscureText: true, // Use secure text for passwords. 97 | decoration: InputDecoration( 98 | icon: Icon(Icons.lock), 99 | labelText: 'Informe sua senha', 100 | ), 101 | validator: ValidateFieldUtil.validatePassword, 102 | onSaved: authStore.setPassword, 103 | ), 104 | ), 105 | this._renderErrorMessage(authStore), 106 | Container(height: 20), 107 | this.renderButton( 108 | icon: Icons.person, 109 | label: "ENTRAR", 110 | onPressed: () => this.submit(authStore), 111 | ), 112 | this.renderButton( 113 | color: Colors.grey, 114 | icon: Icons.person_outline, 115 | label: "Continuar como Anônimo", 116 | onPressed: authStore.anonymousLogin, 117 | ), 118 | ], 119 | ), 120 | ), 121 | ); 122 | }, 123 | ); 124 | } 125 | 126 | @override 127 | Widget build(BuildContext context) { 128 | final AuthStore authStore = Modular.get(); 129 | 130 | return Scaffold( 131 | appBar: AppBar( 132 | automaticallyImplyLeading: false, 133 | title: Observer( 134 | builder: (_) => Text( 135 | authStore.isFetching ? "Aguarde..." : "Entre com seu login", 136 | ), 137 | ), 138 | ), 139 | body: renderBody(authStore), 140 | ); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /lib/pages/new_chat_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_chat/components/button/button.dart'; 3 | import 'package:flutter_chat/components/form/input_text.dart'; 4 | import 'package:flutter_chat/stores/chat_store.dart'; 5 | import 'package:flutter_modular/flutter_modular.dart'; 6 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 7 | import 'package:modal_progress_hud/modal_progress_hud.dart'; 8 | 9 | class NewChatPage extends StatelessWidget { 10 | final GlobalKey _formKey = new GlobalKey(); 11 | 12 | void submit(BuildContext context, ChatStore chatStore) async { 13 | // First validate form. 14 | if (this._formKey.currentState.validate()) { 15 | _formKey.currentState.save(); // Save our form now. 16 | FocusScope.of(context).requestFocus(new FocusNode()); 17 | 18 | chatStore.createNewChat(); 19 | } 20 | } 21 | 22 | Widget _renderFormButton(BuildContext context, ChatStore chatStore) { 23 | return FormButton( 24 | isEditing: false, 25 | onPressed: () => this.submit(context, chatStore), 26 | label: "Iniciar nova conversa", 27 | ); 28 | } 29 | 30 | List _renderFormFields(ChatStore chatStore) { 31 | return [ 32 | InputText( 33 | label: "Assunto", 34 | icon: FontAwesomeIcons.envelopeOpenText, 35 | value: "", 36 | maxLength: 40, 37 | onSaved: chatStore.onChangeNewChatSubject, 38 | ), 39 | Divider(), 40 | InputText( 41 | label: "Como podemos ajuda-lo?", 42 | icon: FontAwesomeIcons.textHeight, 43 | value: "", 44 | onSaved: chatStore.onChangeMessage, 45 | maxLength: 240, 46 | multiline: true, 47 | ), 48 | Divider(), 49 | ]; 50 | } 51 | 52 | Widget renderForm(BuildContext context, ChatStore chatStore) { 53 | return Form( 54 | key: this._formKey, 55 | child: Container( 56 | padding: EdgeInsets.symmetric( 57 | horizontal: 16.0, 58 | vertical: 30.0, 59 | ), 60 | child: Column(children: [ 61 | Expanded( 62 | child: ListView( 63 | children: _renderFormFields(chatStore), 64 | ), 65 | ), 66 | _renderFormButton(context, chatStore), 67 | ]), 68 | ), 69 | ); 70 | } 71 | 72 | @override 73 | Widget build(BuildContext context) { 74 | var chatStore = Modular.get(); 75 | 76 | return ModalProgressHUD( 77 | opacity: 0.8, 78 | inAsyncCall: chatStore.isWaitingForm, 79 | child: Scaffold( 80 | appBar: AppBar( 81 | title: Text("Nova conversa"), 82 | actions: [ 83 | IconButton( 84 | icon: Icon(FontAwesomeIcons.solidSave), 85 | onPressed: () => this.submit(context, chatStore), 86 | ), 87 | ], 88 | ), 89 | body: SafeArea( 90 | child: this.renderForm( 91 | context, 92 | chatStore, 93 | ), 94 | ), 95 | ), 96 | ); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lib/routes/app_routes.dart: -------------------------------------------------------------------------------- 1 | enum APP_ROUTE { LOGIN, HOME, USER_PROFILE, CHATS_LIST, CHAT, NEW_CHAT } 2 | 3 | String pathForRoute(APP_ROUTE route) { 4 | switch (route) { 5 | case APP_ROUTE.LOGIN: 6 | return "/login"; 7 | case APP_ROUTE.USER_PROFILE: 8 | return "/profile"; 9 | case APP_ROUTE.CHATS_LIST: 10 | return "/chats"; 11 | case APP_ROUTE.CHAT: 12 | return "/chats/"; 13 | case APP_ROUTE.NEW_CHAT: 14 | return "/chats/new"; 15 | case APP_ROUTE.HOME: 16 | default: 17 | return "/"; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/services/firebase_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_auth/firebase_auth.dart'; 2 | import 'package:firebase_database/firebase_database.dart'; 3 | 4 | class FirebaseService { 5 | final FirebaseAuth auth = FirebaseAuth.instance; 6 | final FirebaseDatabase database = FirebaseDatabase.instance; 7 | 8 | Map get serverTimestamp => ServerValue.timestamp; 9 | } 10 | -------------------------------------------------------------------------------- /lib/services/notifications_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 3 | import 'package:oktoast/oktoast.dart'; 4 | 5 | enum NOTIFICATION_TYPE { primary, info, success, danger, warning } 6 | 7 | class NotificationsService { 8 | Color _getColorForType(NOTIFICATION_TYPE type) { 9 | switch (type) { 10 | case NOTIFICATION_TYPE.info: 11 | return Colors.lightBlue; 12 | case NOTIFICATION_TYPE.success: 13 | return Colors.green; 14 | case NOTIFICATION_TYPE.warning: 15 | return Colors.orange; 16 | case NOTIFICATION_TYPE.danger: 17 | return Colors.red; 18 | 19 | case NOTIFICATION_TYPE.primary: 20 | default: 21 | return Colors.blue; 22 | } 23 | } 24 | 25 | _notify({ 26 | String title, 27 | String message, 28 | IconData icon = FontAwesomeIcons.bell, 29 | NOTIFICATION_TYPE type = NOTIFICATION_TYPE.primary, 30 | int timeout = 3, 31 | }) { 32 | var textColor = Colors.white; 33 | showToastWidget( 34 | Material( 35 | color: Colors.transparent, 36 | child: Container( 37 | color: Colors.transparent, 38 | margin: EdgeInsets.symmetric(horizontal: 16), 39 | child: ClipRRect( 40 | borderRadius: BorderRadius.circular(8.0), 41 | child: InkWell( 42 | onTap: () => dismissAllToast(showAnim: true), 43 | child: Container( 44 | color: _getColorForType(type), 45 | padding: EdgeInsets.all(12), 46 | height: 80, 47 | child: Row( 48 | mainAxisSize: MainAxisSize.max, 49 | children: [ 50 | Icon(icon ?? FontAwesomeIcons.bell, color: textColor), 51 | Container(width: 8), 52 | Expanded( 53 | child: Text( 54 | message, 55 | style: TextStyle( 56 | color: textColor, 57 | fontSize: 18, 58 | ), 59 | ), 60 | ), 61 | ], 62 | ), 63 | ), 64 | ), 65 | ), 66 | ), 67 | ), 68 | handleTouch: true, 69 | duration: Duration(seconds: timeout ?? 3), 70 | ); 71 | } 72 | 73 | notify( 74 | String message, { 75 | String title, 76 | IconData icon, 77 | int timeout, 78 | }) { 79 | this._notify( 80 | title: title, 81 | message: message, 82 | icon: icon, 83 | type: NOTIFICATION_TYPE.primary, 84 | timeout: timeout, 85 | ); 86 | } 87 | 88 | notifyInfo( 89 | String message, { 90 | String title, 91 | IconData icon, 92 | int timeout, 93 | }) { 94 | this._notify( 95 | title: title, 96 | message: message, 97 | icon: icon, 98 | type: NOTIFICATION_TYPE.info, 99 | timeout: timeout, 100 | ); 101 | } 102 | 103 | notifySuccess( 104 | String message, { 105 | String title, 106 | IconData icon, 107 | int timeout, 108 | }) { 109 | this._notify( 110 | title: title, 111 | message: message, 112 | icon: icon, 113 | type: NOTIFICATION_TYPE.success, 114 | timeout: timeout, 115 | ); 116 | } 117 | 118 | notifyWarning( 119 | String message, { 120 | String title, 121 | int timeout, 122 | }) { 123 | this._notify( 124 | title: title, 125 | message: message, 126 | icon: FontAwesomeIcons.exclamationTriangle, 127 | type: NOTIFICATION_TYPE.warning, 128 | timeout: timeout, 129 | ); 130 | } 131 | 132 | notifyDanger( 133 | String message, { 134 | String title, 135 | int timeout, 136 | }) { 137 | this._notify( 138 | title: title, 139 | message: message, 140 | icon: FontAwesomeIcons.exclamationTriangle, 141 | type: NOTIFICATION_TYPE.danger, 142 | timeout: timeout, 143 | ); 144 | } 145 | 146 | notifyError( 147 | String message, { 148 | String title, 149 | int timeout, 150 | }) { 151 | this.notifyDanger( 152 | message, 153 | title: title, 154 | timeout: timeout, 155 | ); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /lib/stores/auth_store.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_auth/firebase_auth.dart'; 2 | import 'package:firebase_database/firebase_database.dart'; 3 | import 'package:flutter_chat/model/user_model.dart'; 4 | import 'package:flutter_chat/services/firebase_service.dart'; 5 | import 'package:mobx/mobx.dart'; 6 | 7 | import 'package:flutter_chat/services/notifications_service.dart'; 8 | 9 | part "auth_store.g.dart"; 10 | 11 | class AuthStore = _AuthStore with _$AuthStore; 12 | 13 | abstract class _AuthStore with Store { 14 | final FirebaseService _firebaseService; 15 | final NotificationsService _notificationsService; 16 | 17 | @observable 18 | bool didVerifyIsLoggedIn = false; 19 | 20 | @observable 21 | bool isFetching = false; 22 | 23 | @observable 24 | String email = ""; 25 | @observable 26 | String password = ""; 27 | @observable 28 | String errorMessage = ""; 29 | 30 | @observable 31 | UserModel currentUser; 32 | 33 | _AuthStore( 34 | this._firebaseService, 35 | this._notificationsService, 36 | ) { 37 | this.verifyIsLoggedIn(); 38 | } 39 | 40 | @action 41 | setDidVerifyIsLoggedIn(bool newValue) => this.didVerifyIsLoggedIn = newValue; 42 | @action 43 | setIsFetching(bool newValue) => this.isFetching = newValue; 44 | 45 | @action 46 | setEmail(String newValue) => this.email = newValue; 47 | @action 48 | setPassword(String newValue) => this.password = newValue; 49 | @action 50 | setErrorMessage(String newValue) => this.errorMessage = newValue ?? ""; 51 | 52 | @action 53 | setCurrentUser(UserModel newValue) { 54 | this.currentUser = newValue; 55 | print("setCurrentUser ${newValue == null}"); 56 | if (newValue != null) { 57 | print("token: ${newValue.token}"); 58 | print("nome: ${newValue.name}"); 59 | print("email: ${newValue.email}"); 60 | print("isAdmin: ${newValue.isAdmin}"); 61 | print("isAnonymous: ${newValue.isAnonymous}"); 62 | } 63 | } 64 | 65 | @computed 66 | bool get isAuthenticated => this.currentUser != null; 67 | @computed 68 | String get currentUserToken => 69 | this.currentUser != null ? this.currentUser.token : null; 70 | 71 | clearStore() { 72 | this.setEmail(""); 73 | this.setPassword(""); 74 | this.setIsFetching(false); 75 | this.setCurrentUser(null); 76 | this.setErrorMessage(""); 77 | } 78 | 79 | appUserFromFirebaseUser(FirebaseUser firebaseUser) async { 80 | if (firebaseUser == null) return null; 81 | 82 | bool isAdmin = false; 83 | 84 | var ref = _firebaseService.database 85 | .reference() 86 | .child("users/${firebaseUser.uid}"); 87 | DataSnapshot snapshot = await ref.once(); 88 | print("snapshot.value ${snapshot.value}"); 89 | if (snapshot.value == null) { 90 | ref.set({ 91 | "isAnonymous": firebaseUser.isAnonymous, 92 | "isAdmin": false, 93 | "createdAt": _firebaseService.serverTimestamp 94 | }); 95 | } else { 96 | isAdmin = snapshot.value["isAdmin"] ?? false; 97 | } 98 | 99 | return UserModel( 100 | email: firebaseUser.email, 101 | name: firebaseUser.displayName, 102 | token: firebaseUser.uid, 103 | isAdmin: isAdmin, 104 | isAnonymous: firebaseUser.isAnonymous, 105 | ); 106 | } 107 | 108 | verifyIsLoggedIn() async { 109 | FirebaseUser firebaseUser = await this.getFirebaseUser(); 110 | var user = await appUserFromFirebaseUser(firebaseUser); 111 | this.setCurrentUser(user); 112 | this.setDidVerifyIsLoggedIn(true); 113 | } 114 | 115 | submitLogin() { 116 | this.setIsFetching(true); 117 | // TODO: substituir fake abaixo por autenticação do Firebase 118 | Future.delayed(Duration(seconds: 3), () { 119 | var user = UserModel( 120 | name: "Test user", 121 | email: "user@test.com", 122 | token: "user1", 123 | isAnonymous: false, 124 | isAdmin: true, 125 | ); 126 | 127 | this._notificationsService.notifySuccess("Bem vindo!"); 128 | this.setCurrentUser(user); 129 | this.setIsFetching(false); 130 | }); 131 | } 132 | 133 | Future getFirebaseUser() async { 134 | return await this._firebaseService.auth.currentUser(); 135 | } 136 | 137 | anonymousLogin() async { 138 | this.setIsFetching(true); 139 | FirebaseUser firebaseUser = await this.getFirebaseUser(); 140 | 141 | try { 142 | if (firebaseUser == null) { 143 | var authResult = await this._firebaseService.auth.signInAnonymously(); 144 | if (authResult.user != null) { 145 | firebaseUser = authResult.user; 146 | } 147 | } 148 | } catch (e) { 149 | print("error on anonymousLogin"); 150 | print(e); 151 | } 152 | 153 | this.setCurrentUser(await this.appUserFromFirebaseUser(firebaseUser)); 154 | this.setIsFetching(false); 155 | } 156 | 157 | void logout() { 158 | this.clearStore(); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /lib/stores/auth_store.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'auth_store.dart'; 4 | 5 | // ************************************************************************** 6 | // StoreGenerator 7 | // ************************************************************************** 8 | 9 | // ignore_for_file: non_constant_identifier_names, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic 10 | 11 | mixin _$AuthStore on _AuthStore, Store { 12 | Computed _$isAuthenticatedComputed; 13 | 14 | @override 15 | bool get isAuthenticated => (_$isAuthenticatedComputed ??= 16 | Computed(() => super.isAuthenticated)) 17 | .value; 18 | Computed _$currentUserTokenComputed; 19 | 20 | @override 21 | String get currentUserToken => (_$currentUserTokenComputed ??= 22 | Computed(() => super.currentUserToken)) 23 | .value; 24 | 25 | final _$didVerifyIsLoggedInAtom = 26 | Atom(name: '_AuthStore.didVerifyIsLoggedIn'); 27 | 28 | @override 29 | bool get didVerifyIsLoggedIn { 30 | _$didVerifyIsLoggedInAtom.context 31 | .enforceReadPolicy(_$didVerifyIsLoggedInAtom); 32 | _$didVerifyIsLoggedInAtom.reportObserved(); 33 | return super.didVerifyIsLoggedIn; 34 | } 35 | 36 | @override 37 | set didVerifyIsLoggedIn(bool value) { 38 | _$didVerifyIsLoggedInAtom.context.conditionallyRunInAction(() { 39 | super.didVerifyIsLoggedIn = value; 40 | _$didVerifyIsLoggedInAtom.reportChanged(); 41 | }, _$didVerifyIsLoggedInAtom, 42 | name: '${_$didVerifyIsLoggedInAtom.name}_set'); 43 | } 44 | 45 | final _$isFetchingAtom = Atom(name: '_AuthStore.isFetching'); 46 | 47 | @override 48 | bool get isFetching { 49 | _$isFetchingAtom.context.enforceReadPolicy(_$isFetchingAtom); 50 | _$isFetchingAtom.reportObserved(); 51 | return super.isFetching; 52 | } 53 | 54 | @override 55 | set isFetching(bool value) { 56 | _$isFetchingAtom.context.conditionallyRunInAction(() { 57 | super.isFetching = value; 58 | _$isFetchingAtom.reportChanged(); 59 | }, _$isFetchingAtom, name: '${_$isFetchingAtom.name}_set'); 60 | } 61 | 62 | final _$emailAtom = Atom(name: '_AuthStore.email'); 63 | 64 | @override 65 | String get email { 66 | _$emailAtom.context.enforceReadPolicy(_$emailAtom); 67 | _$emailAtom.reportObserved(); 68 | return super.email; 69 | } 70 | 71 | @override 72 | set email(String value) { 73 | _$emailAtom.context.conditionallyRunInAction(() { 74 | super.email = value; 75 | _$emailAtom.reportChanged(); 76 | }, _$emailAtom, name: '${_$emailAtom.name}_set'); 77 | } 78 | 79 | final _$passwordAtom = Atom(name: '_AuthStore.password'); 80 | 81 | @override 82 | String get password { 83 | _$passwordAtom.context.enforceReadPolicy(_$passwordAtom); 84 | _$passwordAtom.reportObserved(); 85 | return super.password; 86 | } 87 | 88 | @override 89 | set password(String value) { 90 | _$passwordAtom.context.conditionallyRunInAction(() { 91 | super.password = value; 92 | _$passwordAtom.reportChanged(); 93 | }, _$passwordAtom, name: '${_$passwordAtom.name}_set'); 94 | } 95 | 96 | final _$errorMessageAtom = Atom(name: '_AuthStore.errorMessage'); 97 | 98 | @override 99 | String get errorMessage { 100 | _$errorMessageAtom.context.enforceReadPolicy(_$errorMessageAtom); 101 | _$errorMessageAtom.reportObserved(); 102 | return super.errorMessage; 103 | } 104 | 105 | @override 106 | set errorMessage(String value) { 107 | _$errorMessageAtom.context.conditionallyRunInAction(() { 108 | super.errorMessage = value; 109 | _$errorMessageAtom.reportChanged(); 110 | }, _$errorMessageAtom, name: '${_$errorMessageAtom.name}_set'); 111 | } 112 | 113 | final _$currentUserAtom = Atom(name: '_AuthStore.currentUser'); 114 | 115 | @override 116 | UserModel get currentUser { 117 | _$currentUserAtom.context.enforceReadPolicy(_$currentUserAtom); 118 | _$currentUserAtom.reportObserved(); 119 | return super.currentUser; 120 | } 121 | 122 | @override 123 | set currentUser(UserModel value) { 124 | _$currentUserAtom.context.conditionallyRunInAction(() { 125 | super.currentUser = value; 126 | _$currentUserAtom.reportChanged(); 127 | }, _$currentUserAtom, name: '${_$currentUserAtom.name}_set'); 128 | } 129 | 130 | final _$_AuthStoreActionController = ActionController(name: '_AuthStore'); 131 | 132 | @override 133 | dynamic setDidVerifyIsLoggedIn(bool newValue) { 134 | final _$actionInfo = _$_AuthStoreActionController.startAction(); 135 | try { 136 | return super.setDidVerifyIsLoggedIn(newValue); 137 | } finally { 138 | _$_AuthStoreActionController.endAction(_$actionInfo); 139 | } 140 | } 141 | 142 | @override 143 | dynamic setIsFetching(bool newValue) { 144 | final _$actionInfo = _$_AuthStoreActionController.startAction(); 145 | try { 146 | return super.setIsFetching(newValue); 147 | } finally { 148 | _$_AuthStoreActionController.endAction(_$actionInfo); 149 | } 150 | } 151 | 152 | @override 153 | dynamic setEmail(String newValue) { 154 | final _$actionInfo = _$_AuthStoreActionController.startAction(); 155 | try { 156 | return super.setEmail(newValue); 157 | } finally { 158 | _$_AuthStoreActionController.endAction(_$actionInfo); 159 | } 160 | } 161 | 162 | @override 163 | dynamic setPassword(String newValue) { 164 | final _$actionInfo = _$_AuthStoreActionController.startAction(); 165 | try { 166 | return super.setPassword(newValue); 167 | } finally { 168 | _$_AuthStoreActionController.endAction(_$actionInfo); 169 | } 170 | } 171 | 172 | @override 173 | dynamic setErrorMessage(String newValue) { 174 | final _$actionInfo = _$_AuthStoreActionController.startAction(); 175 | try { 176 | return super.setErrorMessage(newValue); 177 | } finally { 178 | _$_AuthStoreActionController.endAction(_$actionInfo); 179 | } 180 | } 181 | 182 | @override 183 | dynamic setCurrentUser(UserModel newValue) { 184 | final _$actionInfo = _$_AuthStoreActionController.startAction(); 185 | try { 186 | return super.setCurrentUser(newValue); 187 | } finally { 188 | _$_AuthStoreActionController.endAction(_$actionInfo); 189 | } 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /lib/stores/chat_store.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_chat/model/author_model.dart'; 2 | import 'package:flutter_chat/model/chat_model.dart'; 3 | import 'package:flutter_chat/model/message_model.dart'; 4 | import 'package:mobx/mobx.dart'; 5 | part 'chat_store.g.dart'; 6 | 7 | class ChatStore = _ChatStoreBase with _$ChatStore; 8 | 9 | abstract class _ChatStoreBase with Store { 10 | _ChatStoreBase() { 11 | reaction((_) => selectedChatId, this.getSelectedChatMessages); 12 | } 13 | 14 | @observable 15 | bool isAdminView = false; 16 | @observable 17 | bool isFetching = false; 18 | @observable 19 | bool isWaitingForm = false; 20 | 21 | @observable 22 | String selectedChatId; 23 | 24 | @observable 25 | String newChatSubject = ""; 26 | @observable 27 | String message = ""; 28 | 29 | @observable 30 | String selectedMessageId; 31 | 32 | @observable 33 | ObservableList chatsList; 34 | @observable 35 | ObservableList selectedChatMessagesList; 36 | 37 | @action 38 | setIsAdminView(bool newValue) => this.isAdminView = newValue; 39 | @action 40 | setIsFetching(bool newValue) => this.isFetching = newValue; 41 | @action 42 | setIsWaitingForm(bool newValue) => this.isWaitingForm = newValue; 43 | @action 44 | setSelectedChatId(String newValue) => this.selectedChatId = newValue; 45 | 46 | @action 47 | onChangeNewChatSubject(String newValue) => this.newChatSubject = newValue; 48 | @action 49 | onChangeMessage(String newValue) => this.message = newValue; 50 | 51 | @action 52 | setSelectedMessageId(String newValue) => this.selectedMessageId = newValue; 53 | 54 | @action 55 | cancelEditingMessage() => this.selectedMessageId = null; 56 | 57 | @action 58 | setChatsList(List newValue) => 59 | this.chatsList = newValue.asObservable(); 60 | 61 | @action 62 | setSelectedChatMessagesList(List newValue) => 63 | this.selectedChatMessagesList = newValue.asObservable(); 64 | 65 | @computed 66 | ChatModel get selectedChat { 67 | if (this.selectedChatId != null) { 68 | return chatsList.firstWhere( 69 | (chat) => chat.id == this.selectedChatId, 70 | orElse: () => null, 71 | ); 72 | } 73 | return null; 74 | } 75 | 76 | @computed 77 | MessageModel get selectedMessageForEditing { 78 | if (this.selectedMessageId != null) { 79 | return this.selectedChatMessagesList.firstWhere( 80 | (message) => message.id == this.selectedMessageId, 81 | orElse: () => null, 82 | ); 83 | } 84 | return null; 85 | } 86 | 87 | @computed 88 | int get totalUnreadMessagesCount { 89 | int count = 0; 90 | this.chatsList.forEach((chat) { 91 | if (this.isAdminView) { 92 | count += chat.user.unreadMessagesCount; 93 | } else { 94 | count += chat.user.unreadMessagesCount; 95 | } 96 | }); 97 | 98 | return count; 99 | } 100 | 101 | clearStore() { 102 | this.setSelectedChatId(null); 103 | this.setChatsList(null); 104 | this.setSelectedChatMessagesList(null); 105 | } 106 | 107 | getChatsList() { 108 | List list = List(); 109 | // TODO: implementar buscar chats do BD 110 | var user = AuthorModel( 111 | type: AuthorType.user, 112 | isConnected: false, 113 | isReading: false, 114 | isTyping: false, 115 | unreadMessagesCount: 0); 116 | var admin = AuthorModel( 117 | type: AuthorType.admin, 118 | isConnected: true, 119 | isReading: true, 120 | isTyping: false, 121 | unreadMessagesCount: 0); 122 | ChatModel chat = ChatModel( 123 | id: "chat1", 124 | subject: "Erro no relatório", 125 | user: user, 126 | admin: admin, 127 | createdAt: 1547001767972, 128 | hasUserMessage: true, 129 | isClosed: false, 130 | lastMessage: "Não estou conseguindo visualizar o relatório", 131 | lastMessageTimestamp: 1539284359878, 132 | lastMessageAuthor: AuthorType.user, 133 | messagesCount: 3, 134 | userId: "user1", 135 | ); 136 | list.add(chat); 137 | 138 | this.setChatsList(list); 139 | } 140 | 141 | getSelectedChatMessages(String _selectedChatId) { 142 | if (_selectedChatId?.isNotEmpty ?? false) { 143 | // TODO: implementar recuperar mensagens do BD 144 | 145 | var list = List(); 146 | var message1 = MessageModel( 147 | id: "message1", 148 | type: MessageType.text, 149 | author: AuthorType.user, 150 | message: "Boa tarde. Preciso de ajuda", 151 | timestamp: 1539284359878, 152 | isRead: true, 153 | ); 154 | var message2 = MessageModel( 155 | id: "message2", 156 | type: MessageType.text, 157 | author: AuthorType.admin, 158 | message: "Olá, boa tarde. Como posso ajudar?", 159 | adminName: "Suporte 1", 160 | timestamp: 1539284359878, 161 | isRead: true, 162 | ); 163 | var message3 = MessageModel( 164 | id: "message3", 165 | type: MessageType.image, 166 | author: AuthorType.user, 167 | message: "Legenda imagem 1", 168 | timestamp: 1581361792407, 169 | isRead: true, 170 | imageUrl: 171 | "https://firebasestorage.googleapis.com/v0/b/modboxdev.appspot.com/o/chats%2F-LzliIWPpWxuHqKPV5AN%2F-Lzljk0Zv6DNEwdVjvzx.jpeg?alt=media&token=b627e3eb-0f01-4cf4-ab1c-6e5ebd76de60", 172 | fileExtension: "jpeg", 173 | ); 174 | var message4 = MessageModel( 175 | id: "message3", 176 | type: MessageType.image, 177 | author: AuthorType.admin, 178 | message: "Legenda da imagem 2", 179 | timestamp: 1581361792407, 180 | isRead: true, 181 | imageUrl: 182 | "https://firebasestorage.googleapis.com/v0/b/modboxdev.appspot.com/o/chats%2F-LzmNcuHYXxLDHMf9fyd%2F-M-1-vYRguTFuDcrpeCG.jpeg?alt=media&token=0f07e734-0dcc-41f7-9881-f8c24448430d", 183 | fileExtension: "jpeg", 184 | ); 185 | var message5 = MessageModel( 186 | id: "message3", 187 | type: MessageType.text, 188 | author: AuthorType.user, 189 | message: "Não estou conseguindo visualizar o relatório", 190 | timestamp: 1581361792407, 191 | isRead: true, 192 | ); 193 | list.add(message1); 194 | list.add(message2); 195 | list.add(message3); 196 | list.add(message4); 197 | list.add(message5); 198 | this.setSelectedChatMessagesList(list); 199 | } else { 200 | this.setSelectedChatMessagesList(null); 201 | } 202 | } 203 | 204 | didSendMessage() { 205 | // TODO: implementar salvar nova mensagem 206 | } 207 | 208 | didDeleteChatMessage(String messageId) { 209 | // TODO: implementar deleção da mensagem 210 | } 211 | 212 | createNewChat() { 213 | // 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /lib/stores/chat_store.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'chat_store.dart'; 4 | 5 | // ************************************************************************** 6 | // StoreGenerator 7 | // ************************************************************************** 8 | 9 | // ignore_for_file: non_constant_identifier_names, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic 10 | 11 | mixin _$ChatStore on _ChatStoreBase, Store { 12 | Computed _$selectedChatComputed; 13 | 14 | @override 15 | ChatModel get selectedChat => 16 | (_$selectedChatComputed ??= Computed(() => super.selectedChat)) 17 | .value; 18 | Computed _$selectedMessageForEditingComputed; 19 | 20 | @override 21 | MessageModel get selectedMessageForEditing => 22 | (_$selectedMessageForEditingComputed ??= 23 | Computed(() => super.selectedMessageForEditing)) 24 | .value; 25 | Computed _$totalUnreadMessagesCountComputed; 26 | 27 | @override 28 | int get totalUnreadMessagesCount => (_$totalUnreadMessagesCountComputed ??= 29 | Computed(() => super.totalUnreadMessagesCount)) 30 | .value; 31 | 32 | final _$isAdminViewAtom = Atom(name: '_ChatStoreBase.isAdminView'); 33 | 34 | @override 35 | bool get isAdminView { 36 | _$isAdminViewAtom.context.enforceReadPolicy(_$isAdminViewAtom); 37 | _$isAdminViewAtom.reportObserved(); 38 | return super.isAdminView; 39 | } 40 | 41 | @override 42 | set isAdminView(bool value) { 43 | _$isAdminViewAtom.context.conditionallyRunInAction(() { 44 | super.isAdminView = value; 45 | _$isAdminViewAtom.reportChanged(); 46 | }, _$isAdminViewAtom, name: '${_$isAdminViewAtom.name}_set'); 47 | } 48 | 49 | final _$isFetchingAtom = Atom(name: '_ChatStoreBase.isFetching'); 50 | 51 | @override 52 | bool get isFetching { 53 | _$isFetchingAtom.context.enforceReadPolicy(_$isFetchingAtom); 54 | _$isFetchingAtom.reportObserved(); 55 | return super.isFetching; 56 | } 57 | 58 | @override 59 | set isFetching(bool value) { 60 | _$isFetchingAtom.context.conditionallyRunInAction(() { 61 | super.isFetching = value; 62 | _$isFetchingAtom.reportChanged(); 63 | }, _$isFetchingAtom, name: '${_$isFetchingAtom.name}_set'); 64 | } 65 | 66 | final _$isWaitingFormAtom = Atom(name: '_ChatStoreBase.isWaitingForm'); 67 | 68 | @override 69 | bool get isWaitingForm { 70 | _$isWaitingFormAtom.context.enforceReadPolicy(_$isWaitingFormAtom); 71 | _$isWaitingFormAtom.reportObserved(); 72 | return super.isWaitingForm; 73 | } 74 | 75 | @override 76 | set isWaitingForm(bool value) { 77 | _$isWaitingFormAtom.context.conditionallyRunInAction(() { 78 | super.isWaitingForm = value; 79 | _$isWaitingFormAtom.reportChanged(); 80 | }, _$isWaitingFormAtom, name: '${_$isWaitingFormAtom.name}_set'); 81 | } 82 | 83 | final _$selectedChatIdAtom = Atom(name: '_ChatStoreBase.selectedChatId'); 84 | 85 | @override 86 | String get selectedChatId { 87 | _$selectedChatIdAtom.context.enforceReadPolicy(_$selectedChatIdAtom); 88 | _$selectedChatIdAtom.reportObserved(); 89 | return super.selectedChatId; 90 | } 91 | 92 | @override 93 | set selectedChatId(String value) { 94 | _$selectedChatIdAtom.context.conditionallyRunInAction(() { 95 | super.selectedChatId = value; 96 | _$selectedChatIdAtom.reportChanged(); 97 | }, _$selectedChatIdAtom, name: '${_$selectedChatIdAtom.name}_set'); 98 | } 99 | 100 | final _$newChatSubjectAtom = Atom(name: '_ChatStoreBase.newChatSubject'); 101 | 102 | @override 103 | String get newChatSubject { 104 | _$newChatSubjectAtom.context.enforceReadPolicy(_$newChatSubjectAtom); 105 | _$newChatSubjectAtom.reportObserved(); 106 | return super.newChatSubject; 107 | } 108 | 109 | @override 110 | set newChatSubject(String value) { 111 | _$newChatSubjectAtom.context.conditionallyRunInAction(() { 112 | super.newChatSubject = value; 113 | _$newChatSubjectAtom.reportChanged(); 114 | }, _$newChatSubjectAtom, name: '${_$newChatSubjectAtom.name}_set'); 115 | } 116 | 117 | final _$messageAtom = Atom(name: '_ChatStoreBase.message'); 118 | 119 | @override 120 | String get message { 121 | _$messageAtom.context.enforceReadPolicy(_$messageAtom); 122 | _$messageAtom.reportObserved(); 123 | return super.message; 124 | } 125 | 126 | @override 127 | set message(String value) { 128 | _$messageAtom.context.conditionallyRunInAction(() { 129 | super.message = value; 130 | _$messageAtom.reportChanged(); 131 | }, _$messageAtom, name: '${_$messageAtom.name}_set'); 132 | } 133 | 134 | final _$selectedMessageIdAtom = 135 | Atom(name: '_ChatStoreBase.selectedMessageId'); 136 | 137 | @override 138 | String get selectedMessageId { 139 | _$selectedMessageIdAtom.context.enforceReadPolicy(_$selectedMessageIdAtom); 140 | _$selectedMessageIdAtom.reportObserved(); 141 | return super.selectedMessageId; 142 | } 143 | 144 | @override 145 | set selectedMessageId(String value) { 146 | _$selectedMessageIdAtom.context.conditionallyRunInAction(() { 147 | super.selectedMessageId = value; 148 | _$selectedMessageIdAtom.reportChanged(); 149 | }, _$selectedMessageIdAtom, name: '${_$selectedMessageIdAtom.name}_set'); 150 | } 151 | 152 | final _$chatsListAtom = Atom(name: '_ChatStoreBase.chatsList'); 153 | 154 | @override 155 | ObservableList get chatsList { 156 | _$chatsListAtom.context.enforceReadPolicy(_$chatsListAtom); 157 | _$chatsListAtom.reportObserved(); 158 | return super.chatsList; 159 | } 160 | 161 | @override 162 | set chatsList(ObservableList value) { 163 | _$chatsListAtom.context.conditionallyRunInAction(() { 164 | super.chatsList = value; 165 | _$chatsListAtom.reportChanged(); 166 | }, _$chatsListAtom, name: '${_$chatsListAtom.name}_set'); 167 | } 168 | 169 | final _$selectedChatMessagesListAtom = 170 | Atom(name: '_ChatStoreBase.selectedChatMessagesList'); 171 | 172 | @override 173 | ObservableList get selectedChatMessagesList { 174 | _$selectedChatMessagesListAtom.context 175 | .enforceReadPolicy(_$selectedChatMessagesListAtom); 176 | _$selectedChatMessagesListAtom.reportObserved(); 177 | return super.selectedChatMessagesList; 178 | } 179 | 180 | @override 181 | set selectedChatMessagesList(ObservableList value) { 182 | _$selectedChatMessagesListAtom.context.conditionallyRunInAction(() { 183 | super.selectedChatMessagesList = value; 184 | _$selectedChatMessagesListAtom.reportChanged(); 185 | }, _$selectedChatMessagesListAtom, 186 | name: '${_$selectedChatMessagesListAtom.name}_set'); 187 | } 188 | 189 | final _$_ChatStoreBaseActionController = 190 | ActionController(name: '_ChatStoreBase'); 191 | 192 | @override 193 | dynamic setIsAdminView(bool newValue) { 194 | final _$actionInfo = _$_ChatStoreBaseActionController.startAction(); 195 | try { 196 | return super.setIsAdminView(newValue); 197 | } finally { 198 | _$_ChatStoreBaseActionController.endAction(_$actionInfo); 199 | } 200 | } 201 | 202 | @override 203 | dynamic setIsFetching(bool newValue) { 204 | final _$actionInfo = _$_ChatStoreBaseActionController.startAction(); 205 | try { 206 | return super.setIsFetching(newValue); 207 | } finally { 208 | _$_ChatStoreBaseActionController.endAction(_$actionInfo); 209 | } 210 | } 211 | 212 | @override 213 | dynamic setIsWaitingForm(bool newValue) { 214 | final _$actionInfo = _$_ChatStoreBaseActionController.startAction(); 215 | try { 216 | return super.setIsWaitingForm(newValue); 217 | } finally { 218 | _$_ChatStoreBaseActionController.endAction(_$actionInfo); 219 | } 220 | } 221 | 222 | @override 223 | dynamic setSelectedChatId(String newValue) { 224 | final _$actionInfo = _$_ChatStoreBaseActionController.startAction(); 225 | try { 226 | return super.setSelectedChatId(newValue); 227 | } finally { 228 | _$_ChatStoreBaseActionController.endAction(_$actionInfo); 229 | } 230 | } 231 | 232 | @override 233 | dynamic onChangeNewChatSubject(String newValue) { 234 | final _$actionInfo = _$_ChatStoreBaseActionController.startAction(); 235 | try { 236 | return super.onChangeNewChatSubject(newValue); 237 | } finally { 238 | _$_ChatStoreBaseActionController.endAction(_$actionInfo); 239 | } 240 | } 241 | 242 | @override 243 | dynamic onChangeMessage(String newValue) { 244 | final _$actionInfo = _$_ChatStoreBaseActionController.startAction(); 245 | try { 246 | return super.onChangeMessage(newValue); 247 | } finally { 248 | _$_ChatStoreBaseActionController.endAction(_$actionInfo); 249 | } 250 | } 251 | 252 | @override 253 | dynamic setSelectedMessageId(String newValue) { 254 | final _$actionInfo = _$_ChatStoreBaseActionController.startAction(); 255 | try { 256 | return super.setSelectedMessageId(newValue); 257 | } finally { 258 | _$_ChatStoreBaseActionController.endAction(_$actionInfo); 259 | } 260 | } 261 | 262 | @override 263 | dynamic cancelEditingMessage() { 264 | final _$actionInfo = _$_ChatStoreBaseActionController.startAction(); 265 | try { 266 | return super.cancelEditingMessage(); 267 | } finally { 268 | _$_ChatStoreBaseActionController.endAction(_$actionInfo); 269 | } 270 | } 271 | 272 | @override 273 | dynamic setChatsList(List newValue) { 274 | final _$actionInfo = _$_ChatStoreBaseActionController.startAction(); 275 | try { 276 | return super.setChatsList(newValue); 277 | } finally { 278 | _$_ChatStoreBaseActionController.endAction(_$actionInfo); 279 | } 280 | } 281 | 282 | @override 283 | dynamic setSelectedChatMessagesList(List newValue) { 284 | final _$actionInfo = _$_ChatStoreBaseActionController.startAction(); 285 | try { 286 | return super.setSelectedChatMessagesList(newValue); 287 | } finally { 288 | _$_ChatStoreBaseActionController.endAction(_$actionInfo); 289 | } 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /lib/stores/main_store.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_chat/routes/app_routes.dart'; 2 | import 'package:flutter_chat/services/firebase_service.dart'; 3 | import 'package:flutter_chat/stores/chat_store.dart'; 4 | import 'package:flutter_chat/stores/settings_store.dart'; 5 | import 'package:flutter_modular/flutter_modular.dart'; 6 | import 'package:mobx/mobx.dart'; 7 | import 'package:flutter_chat/services/notifications_service.dart'; 8 | 9 | import "./auth_store.dart"; 10 | 11 | class MainStore { 12 | AuthStore authStore; 13 | ChatStore chatStore; 14 | SettingsStore settingsStore; 15 | 16 | MainStore(FirebaseService firebaseService) { 17 | NotificationsService notificationsService = NotificationsService(); 18 | 19 | // Set Stores 20 | this.settingsStore = SettingsStore(); 21 | 22 | this.authStore = AuthStore( 23 | firebaseService, 24 | notificationsService, 25 | ); 26 | 27 | this.chatStore = ChatStore(); 28 | 29 | setReactions(); 30 | } 31 | 32 | setReactions() { 33 | reaction( 34 | (_) { 35 | if (this.authStore.didVerifyIsLoggedIn) { 36 | return this.authStore.isAuthenticated; 37 | } 38 | return false; 39 | }, 40 | (bool isAuthenticated) { 41 | print("isAuthenticated $isAuthenticated"); 42 | if (isAuthenticated) { 43 | Modular.to.pushNamedAndRemoveUntil( 44 | pathForRoute(APP_ROUTE.CHATS_LIST), (_) => false); 45 | } else { 46 | Modular.to.pushNamedAndRemoveUntil( 47 | pathForRoute(APP_ROUTE.LOGIN), (_) => false); 48 | } 49 | }, 50 | fireImmediately: true, 51 | ); 52 | 53 | reaction((_) => this.authStore.isAuthenticated, (bool isAuthenticated) { 54 | if (isAuthenticated) { 55 | this.chatStore.setIsAdminView(this.authStore.currentUser.isAdmin); 56 | this.chatStore.getChatsList(); 57 | } else { 58 | this.chatStore.clearStore(); 59 | } 60 | }); 61 | reaction((_) => this.chatStore.selectedChatId, (String selectedChatId) { 62 | if (selectedChatId != null) { 63 | Modular.to.pushNamed("${pathForRoute(APP_ROUTE.CHAT)}$selectedChatId"); 64 | } else { 65 | Modular.to.pop(); 66 | } 67 | }); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/stores/settings_store.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_chat/routes/app_routes.dart'; 2 | import 'package:mobx/mobx.dart'; 3 | part 'settings_store.g.dart'; 4 | 5 | class SettingsStore = _SettingsStoreBase with _$SettingsStore; 6 | 7 | abstract class _SettingsStoreBase with Store { 8 | @observable 9 | String searchString = ""; 10 | @observable 11 | bool isSearching = false; 12 | 13 | @observable 14 | APP_ROUTE _currentRoute = APP_ROUTE.HOME; 15 | 16 | @action 17 | setSearchString(String newValue) => this.searchString = newValue; 18 | @action 19 | setIsSearching(bool newValue) => this.isSearching = newValue; 20 | @action 21 | toggleIsSearching() => this.isSearching = !this.isSearching; 22 | 23 | @action 24 | setCurrentRoute(APP_ROUTE route) => this._currentRoute = route; 25 | 26 | navigateToRoute(APP_ROUTE route) { 27 | this.setCurrentRoute(route); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/stores/settings_store.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'settings_store.dart'; 4 | 5 | // ************************************************************************** 6 | // StoreGenerator 7 | // ************************************************************************** 8 | 9 | // ignore_for_file: non_constant_identifier_names, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic 10 | 11 | mixin _$SettingsStore on _SettingsStoreBase, Store { 12 | final _$searchStringAtom = Atom(name: '_SettingsStoreBase.searchString'); 13 | 14 | @override 15 | String get searchString { 16 | _$searchStringAtom.context.enforceReadPolicy(_$searchStringAtom); 17 | _$searchStringAtom.reportObserved(); 18 | return super.searchString; 19 | } 20 | 21 | @override 22 | set searchString(String value) { 23 | _$searchStringAtom.context.conditionallyRunInAction(() { 24 | super.searchString = value; 25 | _$searchStringAtom.reportChanged(); 26 | }, _$searchStringAtom, name: '${_$searchStringAtom.name}_set'); 27 | } 28 | 29 | final _$isSearchingAtom = Atom(name: '_SettingsStoreBase.isSearching'); 30 | 31 | @override 32 | bool get isSearching { 33 | _$isSearchingAtom.context.enforceReadPolicy(_$isSearchingAtom); 34 | _$isSearchingAtom.reportObserved(); 35 | return super.isSearching; 36 | } 37 | 38 | @override 39 | set isSearching(bool value) { 40 | _$isSearchingAtom.context.conditionallyRunInAction(() { 41 | super.isSearching = value; 42 | _$isSearchingAtom.reportChanged(); 43 | }, _$isSearchingAtom, name: '${_$isSearchingAtom.name}_set'); 44 | } 45 | 46 | final _$_currentRouteAtom = Atom(name: '_SettingsStoreBase._currentRoute'); 47 | 48 | @override 49 | APP_ROUTE get _currentRoute { 50 | _$_currentRouteAtom.context.enforceReadPolicy(_$_currentRouteAtom); 51 | _$_currentRouteAtom.reportObserved(); 52 | return super._currentRoute; 53 | } 54 | 55 | @override 56 | set _currentRoute(APP_ROUTE value) { 57 | _$_currentRouteAtom.context.conditionallyRunInAction(() { 58 | super._currentRoute = value; 59 | _$_currentRouteAtom.reportChanged(); 60 | }, _$_currentRouteAtom, name: '${_$_currentRouteAtom.name}_set'); 61 | } 62 | 63 | final _$_SettingsStoreBaseActionController = 64 | ActionController(name: '_SettingsStoreBase'); 65 | 66 | @override 67 | dynamic setSearchString(String newValue) { 68 | final _$actionInfo = _$_SettingsStoreBaseActionController.startAction(); 69 | try { 70 | return super.setSearchString(newValue); 71 | } finally { 72 | _$_SettingsStoreBaseActionController.endAction(_$actionInfo); 73 | } 74 | } 75 | 76 | @override 77 | dynamic setIsSearching(bool newValue) { 78 | final _$actionInfo = _$_SettingsStoreBaseActionController.startAction(); 79 | try { 80 | return super.setIsSearching(newValue); 81 | } finally { 82 | _$_SettingsStoreBaseActionController.endAction(_$actionInfo); 83 | } 84 | } 85 | 86 | @override 87 | dynamic toggleIsSearching() { 88 | final _$actionInfo = _$_SettingsStoreBaseActionController.startAction(); 89 | try { 90 | return super.toggleIsSearching(); 91 | } finally { 92 | _$_SettingsStoreBaseActionController.endAction(_$actionInfo); 93 | } 94 | } 95 | 96 | @override 97 | dynamic setCurrentRoute(APP_ROUTE route) { 98 | final _$actionInfo = _$_SettingsStoreBaseActionController.startAction(); 99 | try { 100 | return super.setCurrentRoute(route); 101 | } finally { 102 | _$_SettingsStoreBaseActionController.endAction(_$actionInfo); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /lib/utils/date_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:intl/intl.dart'; 2 | 3 | String formattedDayAndHourFromFirebaseTimestamp(int firebaseTimestamp) { 4 | var date = DateTime.fromMicrosecondsSinceEpoch(firebaseTimestamp * 1000); 5 | var formatter = DateFormat("dd/MM/yy HH:mm"); 6 | return formatter.format(date); 7 | } 8 | -------------------------------------------------------------------------------- /lib/utils/image_utils.dart: -------------------------------------------------------------------------------- 1 | import "dart:async"; 2 | import "dart:io"; 3 | import "package:flutter/material.dart"; 4 | import "package:image_cropper/image_cropper.dart"; 5 | import "package:image_picker/image_picker.dart"; 6 | 7 | Future pickImage({ 8 | BuildContext context, 9 | ImageSource source, 10 | int imageQuality, 11 | }) async { 12 | var imageFile = await ImagePicker.pickImage( 13 | source: source ?? ImageSource.gallery, 14 | imageQuality: imageQuality ?? 80, 15 | ); 16 | 17 | return await cropImage(context: context, imageFile: imageFile); 18 | } 19 | 20 | Future cropImage({ 21 | BuildContext context, 22 | File imageFile, 23 | }) async { 24 | var theme = Theme.of(context); 25 | return await ImageCropper.cropImage( 26 | sourcePath: imageFile.path, 27 | aspectRatioPresets: Platform.isAndroid 28 | ? [ 29 | CropAspectRatioPreset.square, 30 | CropAspectRatioPreset.ratio3x2, 31 | CropAspectRatioPreset.original, 32 | CropAspectRatioPreset.ratio4x3, 33 | CropAspectRatioPreset.ratio16x9 34 | ] 35 | : [ 36 | CropAspectRatioPreset.original, 37 | CropAspectRatioPreset.square, 38 | CropAspectRatioPreset.ratio3x2, 39 | CropAspectRatioPreset.ratio4x3, 40 | CropAspectRatioPreset.ratio16x9 41 | ], 42 | androidUiSettings: AndroidUiSettings( 43 | toolbarTitle: "Escolha a imagem", 44 | toolbarColor: theme.primaryColor, 45 | toolbarWidgetColor: theme.accentColor, 46 | initAspectRatio: CropAspectRatioPreset.original, 47 | lockAspectRatio: false, 48 | ), 49 | iosUiSettings: IOSUiSettings( 50 | title: "Escolha a imagem", 51 | doneButtonTitle: "Pronto", 52 | cancelButtonTitle: "Cancelar"), 53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /lib/utils/utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_chat/model/author_model.dart'; 2 | 3 | bool messageIsMine(bool isAdminView, AuthorType author) { 4 | return isAdminView ? author == AuthorType.admin : author == AuthorType.user; 5 | } 6 | -------------------------------------------------------------------------------- /lib/utils/validate_field_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:validate/validate.dart'; 2 | 3 | class ValidateFieldUtil { 4 | static String validateUserName(String value) { 5 | if (value.isEmpty) { 6 | return "Informe seu nome."; 7 | } else if (value.length < 3) { 8 | return "Informe um nome com mais de 2 caracteres."; 9 | } 10 | 11 | return null; 12 | } 13 | 14 | static String validateEmail(String value) { 15 | try { 16 | Validate.isEmail(value.trim()); 17 | } catch (e) { 18 | return 'O e-mail informado é inválido!'; 19 | } 20 | return null; 21 | } 22 | 23 | static String validatePassword(String value) { 24 | if (value.length < 5) { 25 | return 'A senha precisa ter pelo menos 5 caracteres'; 26 | } 27 | return null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /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: "1.0.3" 11 | analyzer: 12 | dependency: transitive 13 | description: 14 | name: analyzer 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "0.39.4" 18 | archive: 19 | dependency: transitive 20 | description: 21 | name: archive 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "2.0.11" 25 | args: 26 | dependency: transitive 27 | description: 28 | name: args 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.5.2" 32 | async: 33 | dependency: transitive 34 | description: 35 | name: async 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "2.4.0" 39 | boolean_selector: 40 | dependency: transitive 41 | description: 42 | name: boolean_selector 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.0.5" 46 | build: 47 | dependency: transitive 48 | description: 49 | name: build 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.2.2" 53 | build_config: 54 | dependency: transitive 55 | description: 56 | name: build_config 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "0.4.1+1" 60 | build_daemon: 61 | dependency: transitive 62 | description: 63 | name: build_daemon 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "2.1.3" 67 | build_resolvers: 68 | dependency: transitive 69 | description: 70 | name: build_resolvers 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "1.3.2" 74 | build_runner: 75 | dependency: "direct dev" 76 | description: 77 | name: build_runner 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "1.7.3" 81 | build_runner_core: 82 | dependency: transitive 83 | description: 84 | name: build_runner_core 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "4.3.0" 88 | built_collection: 89 | dependency: transitive 90 | description: 91 | name: built_collection 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "4.3.2" 95 | built_value: 96 | dependency: transitive 97 | description: 98 | name: built_value 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "7.0.8" 102 | charcode: 103 | dependency: transitive 104 | description: 105 | name: charcode 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "1.1.2" 109 | checked_yaml: 110 | dependency: transitive 111 | description: 112 | name: checked_yaml 113 | url: "https://pub.dartlang.org" 114 | source: hosted 115 | version: "1.0.2" 116 | code_builder: 117 | dependency: transitive 118 | description: 119 | name: code_builder 120 | url: "https://pub.dartlang.org" 121 | source: hosted 122 | version: "3.2.1" 123 | collection: 124 | dependency: transitive 125 | description: 126 | name: collection 127 | url: "https://pub.dartlang.org" 128 | source: hosted 129 | version: "1.14.11" 130 | convert: 131 | dependency: transitive 132 | description: 133 | name: convert 134 | url: "https://pub.dartlang.org" 135 | source: hosted 136 | version: "2.1.1" 137 | crypto: 138 | dependency: transitive 139 | description: 140 | name: crypto 141 | url: "https://pub.dartlang.org" 142 | source: hosted 143 | version: "2.1.3" 144 | csslib: 145 | dependency: transitive 146 | description: 147 | name: csslib 148 | url: "https://pub.dartlang.org" 149 | source: hosted 150 | version: "0.16.1" 151 | cupertino_icons: 152 | dependency: "direct main" 153 | description: 154 | name: cupertino_icons 155 | url: "https://pub.dartlang.org" 156 | source: hosted 157 | version: "0.1.3" 158 | dart_style: 159 | dependency: transitive 160 | description: 161 | name: dart_style 162 | url: "https://pub.dartlang.org" 163 | source: hosted 164 | version: "1.3.3" 165 | firebase: 166 | dependency: transitive 167 | description: 168 | name: firebase 169 | url: "https://pub.dartlang.org" 170 | source: hosted 171 | version: "7.2.1" 172 | firebase_auth: 173 | dependency: "direct main" 174 | description: 175 | name: firebase_auth 176 | url: "https://pub.dartlang.org" 177 | source: hosted 178 | version: "0.15.4" 179 | firebase_auth_platform_interface: 180 | dependency: transitive 181 | description: 182 | name: firebase_auth_platform_interface 183 | url: "https://pub.dartlang.org" 184 | source: hosted 185 | version: "1.1.5" 186 | firebase_auth_web: 187 | dependency: transitive 188 | description: 189 | name: firebase_auth_web 190 | url: "https://pub.dartlang.org" 191 | source: hosted 192 | version: "0.1.2" 193 | firebase_core: 194 | dependency: "direct main" 195 | description: 196 | name: firebase_core 197 | url: "https://pub.dartlang.org" 198 | source: hosted 199 | version: "0.4.3+3" 200 | firebase_core_platform_interface: 201 | dependency: transitive 202 | description: 203 | name: firebase_core_platform_interface 204 | url: "https://pub.dartlang.org" 205 | source: hosted 206 | version: "1.0.2" 207 | firebase_core_web: 208 | dependency: transitive 209 | description: 210 | name: firebase_core_web 211 | url: "https://pub.dartlang.org" 212 | source: hosted 213 | version: "0.1.1+2" 214 | firebase_database: 215 | dependency: "direct main" 216 | description: 217 | name: firebase_database 218 | url: "https://pub.dartlang.org" 219 | source: hosted 220 | version: "3.1.1" 221 | fixnum: 222 | dependency: transitive 223 | description: 224 | name: fixnum 225 | url: "https://pub.dartlang.org" 226 | source: hosted 227 | version: "0.10.11" 228 | flutter: 229 | dependency: "direct main" 230 | description: flutter 231 | source: sdk 232 | version: "0.0.0" 233 | flutter_mobx: 234 | dependency: "direct main" 235 | description: 236 | name: flutter_mobx 237 | url: "https://pub.dartlang.org" 238 | source: hosted 239 | version: "1.0.1" 240 | flutter_modular: 241 | dependency: "direct main" 242 | description: 243 | name: flutter_modular 244 | url: "https://pub.dartlang.org" 245 | source: hosted 246 | version: "0.4.2" 247 | flutter_plugin_android_lifecycle: 248 | dependency: transitive 249 | description: 250 | name: flutter_plugin_android_lifecycle 251 | url: "https://pub.dartlang.org" 252 | source: hosted 253 | version: "1.0.5" 254 | flutter_slidable: 255 | dependency: "direct main" 256 | description: 257 | name: flutter_slidable 258 | url: "https://pub.dartlang.org" 259 | source: hosted 260 | version: "0.5.4" 261 | flutter_test: 262 | dependency: "direct dev" 263 | description: flutter 264 | source: sdk 265 | version: "0.0.0" 266 | flutter_web_plugins: 267 | dependency: transitive 268 | description: flutter 269 | source: sdk 270 | version: "0.0.0" 271 | font_awesome_flutter: 272 | dependency: "direct main" 273 | description: 274 | name: font_awesome_flutter 275 | url: "https://pub.dartlang.org" 276 | source: hosted 277 | version: "8.5.0" 278 | glob: 279 | dependency: transitive 280 | description: 281 | name: glob 282 | url: "https://pub.dartlang.org" 283 | source: hosted 284 | version: "1.2.0" 285 | graphs: 286 | dependency: transitive 287 | description: 288 | name: graphs 289 | url: "https://pub.dartlang.org" 290 | source: hosted 291 | version: "0.2.0" 292 | html: 293 | dependency: transitive 294 | description: 295 | name: html 296 | url: "https://pub.dartlang.org" 297 | source: hosted 298 | version: "0.14.0+3" 299 | http: 300 | dependency: transitive 301 | description: 302 | name: http 303 | url: "https://pub.dartlang.org" 304 | source: hosted 305 | version: "0.12.0+4" 306 | http_multi_server: 307 | dependency: transitive 308 | description: 309 | name: http_multi_server 310 | url: "https://pub.dartlang.org" 311 | source: hosted 312 | version: "2.2.0" 313 | http_parser: 314 | dependency: transitive 315 | description: 316 | name: http_parser 317 | url: "https://pub.dartlang.org" 318 | source: hosted 319 | version: "3.1.3" 320 | image: 321 | dependency: transitive 322 | description: 323 | name: image 324 | url: "https://pub.dartlang.org" 325 | source: hosted 326 | version: "2.1.4" 327 | image_cropper: 328 | dependency: "direct main" 329 | description: 330 | name: image_cropper 331 | url: "https://pub.dartlang.org" 332 | source: hosted 333 | version: "1.2.1" 334 | image_picker: 335 | dependency: "direct main" 336 | description: 337 | name: image_picker 338 | url: "https://pub.dartlang.org" 339 | source: hosted 340 | version: "0.6.3+1" 341 | intl: 342 | dependency: "direct main" 343 | description: 344 | name: intl 345 | url: "https://pub.dartlang.org" 346 | source: hosted 347 | version: "0.16.1" 348 | io: 349 | dependency: transitive 350 | description: 351 | name: io 352 | url: "https://pub.dartlang.org" 353 | source: hosted 354 | version: "0.3.3" 355 | js: 356 | dependency: transitive 357 | description: 358 | name: js 359 | url: "https://pub.dartlang.org" 360 | source: hosted 361 | version: "0.6.1+1" 362 | json_annotation: 363 | dependency: transitive 364 | description: 365 | name: json_annotation 366 | url: "https://pub.dartlang.org" 367 | source: hosted 368 | version: "3.0.1" 369 | logging: 370 | dependency: transitive 371 | description: 372 | name: logging 373 | url: "https://pub.dartlang.org" 374 | source: hosted 375 | version: "0.11.4" 376 | matcher: 377 | dependency: transitive 378 | description: 379 | name: matcher 380 | url: "https://pub.dartlang.org" 381 | source: hosted 382 | version: "0.12.6" 383 | meta: 384 | dependency: transitive 385 | description: 386 | name: meta 387 | url: "https://pub.dartlang.org" 388 | source: hosted 389 | version: "1.1.8" 390 | mime: 391 | dependency: transitive 392 | description: 393 | name: mime 394 | url: "https://pub.dartlang.org" 395 | source: hosted 396 | version: "0.9.6+3" 397 | mobx: 398 | dependency: "direct main" 399 | description: 400 | name: mobx 401 | url: "https://pub.dartlang.org" 402 | source: hosted 403 | version: "1.0.1" 404 | mobx_codegen: 405 | dependency: "direct dev" 406 | description: 407 | name: mobx_codegen 408 | url: "https://pub.dartlang.org" 409 | source: hosted 410 | version: "1.0.1" 411 | modal_progress_hud: 412 | dependency: "direct main" 413 | description: 414 | name: modal_progress_hud 415 | url: "https://pub.dartlang.org" 416 | source: hosted 417 | version: "0.1.3" 418 | node_interop: 419 | dependency: transitive 420 | description: 421 | name: node_interop 422 | url: "https://pub.dartlang.org" 423 | source: hosted 424 | version: "1.0.3" 425 | node_io: 426 | dependency: transitive 427 | description: 428 | name: node_io 429 | url: "https://pub.dartlang.org" 430 | source: hosted 431 | version: "1.0.1+2" 432 | oktoast: 433 | dependency: "direct main" 434 | description: 435 | name: oktoast 436 | url: "https://pub.dartlang.org" 437 | source: hosted 438 | version: "2.3.0" 439 | package_config: 440 | dependency: transitive 441 | description: 442 | name: package_config 443 | url: "https://pub.dartlang.org" 444 | source: hosted 445 | version: "1.1.0" 446 | package_resolver: 447 | dependency: transitive 448 | description: 449 | name: package_resolver 450 | url: "https://pub.dartlang.org" 451 | source: hosted 452 | version: "1.0.10" 453 | path: 454 | dependency: transitive 455 | description: 456 | name: path 457 | url: "https://pub.dartlang.org" 458 | source: hosted 459 | version: "1.6.4" 460 | pedantic: 461 | dependency: transitive 462 | description: 463 | name: pedantic 464 | url: "https://pub.dartlang.org" 465 | source: hosted 466 | version: "1.8.0+1" 467 | petitparser: 468 | dependency: transitive 469 | description: 470 | name: petitparser 471 | url: "https://pub.dartlang.org" 472 | source: hosted 473 | version: "2.4.0" 474 | pool: 475 | dependency: transitive 476 | description: 477 | name: pool 478 | url: "https://pub.dartlang.org" 479 | source: hosted 480 | version: "1.4.0" 481 | pub_semver: 482 | dependency: transitive 483 | description: 484 | name: pub_semver 485 | url: "https://pub.dartlang.org" 486 | source: hosted 487 | version: "1.4.2" 488 | pubspec_parse: 489 | dependency: transitive 490 | description: 491 | name: pubspec_parse 492 | url: "https://pub.dartlang.org" 493 | source: hosted 494 | version: "0.1.5" 495 | quiver: 496 | dependency: transitive 497 | description: 498 | name: quiver 499 | url: "https://pub.dartlang.org" 500 | source: hosted 501 | version: "2.0.5" 502 | shelf: 503 | dependency: transitive 504 | description: 505 | name: shelf 506 | url: "https://pub.dartlang.org" 507 | source: hosted 508 | version: "0.7.5" 509 | shelf_web_socket: 510 | dependency: transitive 511 | description: 512 | name: shelf_web_socket 513 | url: "https://pub.dartlang.org" 514 | source: hosted 515 | version: "0.2.3" 516 | simple_gravatar: 517 | dependency: "direct main" 518 | description: 519 | name: simple_gravatar 520 | url: "https://pub.dartlang.org" 521 | source: hosted 522 | version: "1.0.5" 523 | sky_engine: 524 | dependency: transitive 525 | description: flutter 526 | source: sdk 527 | version: "0.0.99" 528 | source_gen: 529 | dependency: transitive 530 | description: 531 | name: source_gen 532 | url: "https://pub.dartlang.org" 533 | source: hosted 534 | version: "0.9.4+7" 535 | source_span: 536 | dependency: transitive 537 | description: 538 | name: source_span 539 | url: "https://pub.dartlang.org" 540 | source: hosted 541 | version: "1.5.5" 542 | stack_trace: 543 | dependency: transitive 544 | description: 545 | name: stack_trace 546 | url: "https://pub.dartlang.org" 547 | source: hosted 548 | version: "1.9.3" 549 | stream_channel: 550 | dependency: transitive 551 | description: 552 | name: stream_channel 553 | url: "https://pub.dartlang.org" 554 | source: hosted 555 | version: "2.0.0" 556 | stream_transform: 557 | dependency: transitive 558 | description: 559 | name: stream_transform 560 | url: "https://pub.dartlang.org" 561 | source: hosted 562 | version: "1.1.0" 563 | string_scanner: 564 | dependency: transitive 565 | description: 566 | name: string_scanner 567 | url: "https://pub.dartlang.org" 568 | source: hosted 569 | version: "1.0.5" 570 | term_glyph: 571 | dependency: transitive 572 | description: 573 | name: term_glyph 574 | url: "https://pub.dartlang.org" 575 | source: hosted 576 | version: "1.1.0" 577 | test_api: 578 | dependency: transitive 579 | description: 580 | name: test_api 581 | url: "https://pub.dartlang.org" 582 | source: hosted 583 | version: "0.2.11" 584 | timing: 585 | dependency: transitive 586 | description: 587 | name: timing 588 | url: "https://pub.dartlang.org" 589 | source: hosted 590 | version: "0.1.1+2" 591 | typed_data: 592 | dependency: transitive 593 | description: 594 | name: typed_data 595 | url: "https://pub.dartlang.org" 596 | source: hosted 597 | version: "1.1.6" 598 | validate: 599 | dependency: "direct main" 600 | description: 601 | name: validate 602 | url: "https://pub.dartlang.org" 603 | source: hosted 604 | version: "1.7.0" 605 | vector_math: 606 | dependency: transitive 607 | description: 608 | name: vector_math 609 | url: "https://pub.dartlang.org" 610 | source: hosted 611 | version: "2.0.8" 612 | watcher: 613 | dependency: transitive 614 | description: 615 | name: watcher 616 | url: "https://pub.dartlang.org" 617 | source: hosted 618 | version: "0.9.7+13" 619 | web_socket_channel: 620 | dependency: transitive 621 | description: 622 | name: web_socket_channel 623 | url: "https://pub.dartlang.org" 624 | source: hosted 625 | version: "1.1.0" 626 | xml: 627 | dependency: transitive 628 | description: 629 | name: xml 630 | url: "https://pub.dartlang.org" 631 | source: hosted 632 | version: "3.5.0" 633 | yaml: 634 | dependency: transitive 635 | description: 636 | name: yaml 637 | url: "https://pub.dartlang.org" 638 | source: hosted 639 | version: "2.2.0" 640 | sdks: 641 | dart: ">=2.7.0 <3.0.0" 642 | flutter: ">=1.12.13+hotfix.4 <2.0.0" 643 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_chat 2 | description: A new Flutter project. 3 | 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # In Android, build-name is used as versionName while build-number used as versionCode. 10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 12 | # Read more about iOS versioning at 13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 14 | version: 0.1.1+1 15 | 16 | environment: 17 | sdk: ">=2.6.0 <3.0.0" 18 | 19 | dependencies: 20 | flutter: 21 | sdk: flutter 22 | firebase_core: ^0.4.3+3 23 | firebase_auth: ^0.15.4 24 | firebase_database: ^3.1.1 25 | font_awesome_flutter: ^8.5.0 26 | mobx: ^1.0.1 27 | flutter_mobx: ^1.0.1 28 | flutter_modular: ^0.4.2 29 | oktoast: ^2.3.0 30 | simple_gravatar: ^1.0.5 31 | modal_progress_hud: ^0.1.3 32 | validate: ^1.7.0 33 | intl: ^0.16.1 34 | flutter_slidable: ^0.5.4 35 | image_cropper: ^1.2.1 36 | image_picker: ^0.6.3+1 37 | 38 | # The following adds the Cupertino Icons font to your application. 39 | # Use with the CupertinoIcons class for iOS style icons. 40 | cupertino_icons: ^0.1.2 41 | 42 | dev_dependencies: 43 | mobx_codegen: 44 | build_runner: 45 | flutter_test: 46 | sdk: flutter 47 | 48 | # For information on the generic Dart part of this file, see the 49 | # following page: https://dart.dev/tools/pub/pubspec 50 | 51 | # The following section is specific to Flutter. 52 | flutter: 53 | # The following line ensures that the Material Icons font is 54 | # included with your application, so that you can use the icons in 55 | # the material Icons class. 56 | uses-material-design: true 57 | # To add assets to your application, add an assets section, like this: 58 | # assets: 59 | # - images/a_dot_burr.jpeg 60 | # - images/a_dot_ham.jpeg 61 | # An image asset can refer to one or more resolution-specific "variants", see 62 | # https://flutter.dev/assets-and-images/#resolution-aware. 63 | # For details regarding adding assets from package dependencies, see 64 | # https://flutter.dev/assets-and-images/#from-packages 65 | # To add custom fonts to your application, add a fonts section here, 66 | # in this "flutter" section. Each entry in this list should have a 67 | # "family" key with the font family name, and a "fonts" key with a 68 | # list giving the asset and other descriptors for the font. For 69 | # example: 70 | # fonts: 71 | # - family: Schyler 72 | # fonts: 73 | # - asset: fonts/Schyler-Regular.ttf 74 | # - asset: fonts/Schyler-Italic.ttf 75 | # style: italic 76 | # - family: Trajan Pro 77 | # fonts: 78 | # - asset: fonts/TrajanPro.ttf 79 | # - asset: fonts/TrajanPro_Bold.ttf 80 | # weight: 700 81 | # 82 | # For details regarding fonts from package dependencies, 83 | # see https://flutter.dev/custom-fonts/#from-packages 84 | --------------------------------------------------------------------------------