diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..a8e938c0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,47 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Web related
+lib/generated_plugin_registrant.dart
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
diff --git a/.metadata b/.metadata
new file mode 100644
index 00000000..48c6a020
--- /dev/null
+++ b/.metadata
@@ -0,0 +1,45 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled.
+
+version:
+ revision: 676cefaaff197f27424942307668886253e1ec35
+ channel: stable
+
+project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+ platforms:
+ - platform: root
+ create_revision: 676cefaaff197f27424942307668886253e1ec35
+ base_revision: 676cefaaff197f27424942307668886253e1ec35
+ - platform: android
+ create_revision: 676cefaaff197f27424942307668886253e1ec35
+ base_revision: 676cefaaff197f27424942307668886253e1ec35
+ - platform: ios
+ create_revision: 676cefaaff197f27424942307668886253e1ec35
+ base_revision: 676cefaaff197f27424942307668886253e1ec35
+ - platform: linux
+ create_revision: 676cefaaff197f27424942307668886253e1ec35
+ base_revision: 676cefaaff197f27424942307668886253e1ec35
+ - platform: macos
+ create_revision: 676cefaaff197f27424942307668886253e1ec35
+ base_revision: 676cefaaff197f27424942307668886253e1ec35
+ - platform: web
+ create_revision: 676cefaaff197f27424942307668886253e1ec35
+ base_revision: 676cefaaff197f27424942307668886253e1ec35
+ - platform: windows
+ create_revision: 676cefaaff197f27424942307668886253e1ec35
+ base_revision: 676cefaaff197f27424942307668886253e1ec35
+
+ # User provided section
+
+ # List of Local paths (relative to this file) that should be
+ # ignored by the migrate tool.
+ #
+ # Files that are not part of the templates will be ignored by default.
+ unmanaged_files:
+ - 'lib/main.dart'
+ - 'ios/Runner.xcodeproj/project.pbxproj'
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..b5732fac
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 JacksonHenrique
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index be2b402d..8248158d 100644
--- a/README.md
+++ b/README.md
@@ -1,33 +1,90 @@
# Desafio programação - para vaga desenvolvedor Mobile
-Por favor leiam este documento do começo ao fim, com muita atenção.
-O intuito deste teste é avaliar seus conhecimentos técnicos em desenvolvimento mobile.
-Este desafio deve ser feito por você em sua casa. Gaste o tempo que você quiser, porém normalmente você não deve precisar de mais do que algumas horas para finalizar.
-# Instruções de entrega do desafio
+## 📄 Licença
-1. Primeiro, faça um fork deste projeto para sua conta no Github (crie uma se você não possuir).
-2. Em seguida, implemente o projeto,tal qual descrito abaixo, utilizando sua linguagem de programação mobile preferida em seu clone local.
-3. Por fim, envie via email o projeto ou o fork/link do projeto para seu contato bycoders_.
+[](https://github.com/jackson-paulino/desafio_mobile/blob/main/LICENSE)
-# Descrição do projeto
+# Sobre o Projeto
-1. Tela de login usando (email e senha);
-2. Tela home com mapa renderizando um ponto na localização atual do device;
-3. Realizar o login utilizando Firebase Auth;
-4. Armazenar os dados do usuário na store global;
-5. Rastrear login com sucesso e renderização com sucesso com Analytics (enviar um evento com dados considerados primordiais nesses dois casos);
-6. Rastrear os erros e envia-los ao Crashlytics;
-7. Armazenar na base de dados local (preferência por WatermelonDB, mas pode usar outro banco de dados) o usuário logado e sua última posição no mapa;
-8. Testar fluxo de login (unit e e2e);
-9. Testar fluxo da home (unit e e2e).
+Este projeto tem como foco mostrar um pouco do conhecimento técnicos durante o teste seletivo para a vaga de Desenvolvedor Front-end Flutter Pleno na empresa Nextar Tecnologia de Software Ltda.
-# Avaliação
+O Aplicativo possibilita o controle de produtos sendo possível criar novos produtos, edição e exclusão.
-Seu projeto será avaliado de acordo com os seguintes critérios.
+## Layout Mobile
+
+
+
+
+
+
-1. Sua aplicação preenche os requerimentos básicos?
-2. Você documentou a maneira de configurar o ambiente e rodar sua aplicação?
-3. Você seguiu as instruções de envio do desafio?
-4. Qualidade e cobertura dos testes unitários.
+## 📦 Desenvolvimento
-Adicionalmente, tentaremos verificar a sua familiarização com as bibliotecas padrões (standard libs), bem como sua experiência com programação orientada a objetos a partir da estrutura de seu projeto.
+
+### 📋 Tecnologias Utilizadas
+
+- Flutter 3.0.3
+- Dart SDK version: 2.17.5
+
+*Ultizando suporte de segurança nula*
+
+ ### Dependência do Projeto
+
+ - dio: ^4.0.6
+ - dartz: ^0.10.1
+ - firebase_auth: ^3.3.20
+ - cloud_firestore: ^3.1.18
+ - flutter_bloc: ^8.0.1
+ - flutter_modular: ^5.0.3
+ - flutter_form_builder: ^7.3.1
+ - form_builder_validators: ^8.1.1
+ - firebase_analytics: ^9.1.10
+ - firebase_crashlytics: ^2.8.2
+ - moor_flutter: ^4.1.0
+ - geolocator: ^9.0.0
+ - google_maps_flutter: ^2.1.8
+
+ ### Dependência de Desenvolvimento
+
+ - flutter_lints: ^2.0.0
+ - mocktail: ^0.3.0
+ - moor_generator: ^4.6.0+1
+ - build_runner: ^2.1.11
+
+ ### Metodo de implementação
+
+ - Arquitetura (Clean Architecture)
+
+## 🔧 Instalação
+
+
+ *Link de Instalação do Flutter* - https://flutter.dev/docs/get-started/install
+
+ *Link de Intalação do Dart para ultilizar o Slidy* - https://dart.dev/get-dart
+
+```bash
+# Intalação do packege Slidy no Dart
+dart pub global activate slidy
+
+# Clone do Projeto
+git clone https://github.com/JackPaulino/dex_control_product.git
+
+# Baixar as dependências
+Flutter pub get
+```
+
+## ✒️ Dados para efetuar o login no App
+ E-mail: bycoders@bycoders.com.br
+ password: @bycoders
+
+## ✒️ Autores
+
+ * *Desenvolvedor* - Jakson Henrique de Almeida Paulino
+ * *Linkedin* - https://www.linkedin.com/in/jakson-henrique-de-almeida-paulino-28437b121/
+
+## 🎁 Expressões de gratidão
+
+ * Agradeço a oportunidade de participar desse teste seletivo 📢
+
+---
+⌨️ com ❤️ por [Jakson Henrique] (https://github.com/JackPaulino) 😊
diff --git a/analysis_options.yaml b/analysis_options.yaml
new file mode 100644
index 00000000..61b6c4de
--- /dev/null
+++ b/analysis_options.yaml
@@ -0,0 +1,29 @@
+# This file configures the analyzer, which statically analyzes Dart code to
+# check for errors, warnings, and lints.
+#
+# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
+# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
+# invoked from the command line by running `flutter analyze`.
+
+# The following line activates a set of recommended lints for Flutter apps,
+# packages, and plugins designed to encourage good coding practices.
+include: package:flutter_lints/flutter.yaml
+
+linter:
+ # The lint rules applied to this project can be customized in the
+ # section below to disable rules from the `package:flutter_lints/flutter.yaml`
+ # included above or to enable additional rules. A list of all available lints
+ # and their documentation is published at
+ # https://dart-lang.github.io/linter/lints/index.html.
+ #
+ # Instead of disabling a lint rule for the entire project in the
+ # section below, it can also be suppressed for a single line of code
+ # or a specific dart file by using the `// ignore: name_of_lint` and
+ # `// ignore_for_file: name_of_lint` syntax on the line or in the file
+ # producing the lint.
+ rules:
+ # avoid_print: false # Uncomment to disable the `avoid_print` rule
+ # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/android/.gitignore b/android/.gitignore
new file mode 100644
index 00000000..6f568019
--- /dev/null
+++ b/android/.gitignore
@@ -0,0 +1,13 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
+key.properties
+**/*.keystore
+**/*.jks
diff --git a/android/app/build.gradle b/android/app/build.gradle
new file mode 100644
index 00000000..9aa115cf
--- /dev/null
+++ b/android/app/build.gradle
@@ -0,0 +1,77 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+ throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+ flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+ flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'com.google.gms.google-services'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+apply plugin: 'com.google.firebase.crashlytics'
+
+android {
+ compileSdkVersion 33
+ ndkVersion flutter.ndkVersion
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId "com.gmail.jaksonpaulino65.desafio_mobile"
+ // You can update the following values to match your application needs.
+ // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
+ minSdkVersion 21
+ targetSdkVersion flutter.targetSdkVersion
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig signingConfigs.debug
+ }
+ }
+}
+
+flutter {
+ source '../..'
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation platform('com.google.firebase:firebase-bom:30.2.0')
+ implementation 'com.google.firebase:firebase-analytics-ktx'
+ implementation 'com.google.firebase:firebase-auth-ktx'
+ implementation 'com.google.android.gms:play-services-auth:20.2.0'
+}
diff --git a/android/app/google-services.json b/android/app/google-services.json
new file mode 100644
index 00000000..9a2f371b
--- /dev/null
+++ b/android/app/google-services.json
@@ -0,0 +1,47 @@
+{
+ "project_info": {
+ "project_number": "42468331832",
+ "project_id": "challengemobile-a4cb5",
+ "storage_bucket": "challengemobile-a4cb5.appspot.com"
+ },
+ "client": [
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:42468331832:android:6189bca92cd7678f1c71cd",
+ "android_client_info": {
+ "package_name": "com.gmail.jaksonpaulino65.desafio_mobile"
+ }
+ },
+ "oauth_client": [
+ {
+ "client_id": "42468331832-l9pmich1eh0lm3kq4ja2sj03c78pkg3i.apps.googleusercontent.com",
+ "client_type": 1,
+ "android_info": {
+ "package_name": "com.gmail.jaksonpaulino65.desafio_mobile",
+ "certificate_hash": "70bcb8f4f61f1866b528d7630cd730bcae2f4e62"
+ }
+ },
+ {
+ "client_id": "42468331832-nvuca5f2tdjgq28em7gcumnhkdk2ivor.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ],
+ "api_key": [
+ {
+ "current_key": "AIzaSyBamdT9j8SdPVZS7-AtXRzPZWt84kXJepQ"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": [
+ {
+ "client_id": "42468331832-nvuca5f2tdjgq28em7gcumnhkdk2ivor.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "configuration_version": "1"
+}
\ No newline at end of file
diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 00000000..c32a7bc9
--- /dev/null
+++ b/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..0ec54a1d
--- /dev/null
+++ b/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/kotlin/com/example/desafio_mobile/MainActivity.kt b/android/app/src/main/kotlin/com/example/desafio_mobile/MainActivity.kt
new file mode 100644
index 00000000..3d4f1fe5
--- /dev/null
+++ b/android/app/src/main/kotlin/com/example/desafio_mobile/MainActivity.kt
@@ -0,0 +1,6 @@
+package com.gmail.jaksonpaulino65.desafio_mobile
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity: FlutterActivity() {
+}
diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 00000000..f74085f3
--- /dev/null
+++ b/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 00000000..304732f8
--- /dev/null
+++ b/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..db77bb4b
Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..17987b79
Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..09d43914
Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..d5f1c8d3
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..4d6372ee
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 00000000..06952be7
--- /dev/null
+++ b/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml
new file mode 100644
index 00000000..cb1ef880
--- /dev/null
+++ b/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 00000000..c32a7bc9
--- /dev/null
+++ b/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/android/build.gradle b/android/build.gradle
new file mode 100644
index 00000000..06ea72c0
--- /dev/null
+++ b/android/build.gradle
@@ -0,0 +1,33 @@
+buildscript {
+ ext.kotlin_version = '1.6.10'
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:7.1.2'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath 'com.google.gms:google-services:4.3.12'
+ classpath 'com.google.firebase:firebase-crashlytics-gradle:2.7.1'
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+ project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/android/gradle.properties b/android/gradle.properties
new file mode 100644
index 00000000..e60119f3
--- /dev/null
+++ b/android/gradle.properties
@@ -0,0 +1,4 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
+
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..cc5527d7
--- /dev/null
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
diff --git a/android/settings.gradle b/android/settings.gradle
new file mode 100644
index 00000000..44e62bcf
--- /dev/null
+++ b/android/settings.gradle
@@ -0,0 +1,11 @@
+include ':app'
+
+def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
+def properties = new Properties()
+
+assert localPropertiesFile.exists()
+localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+
+def flutterSdkPath = properties.getProperty("flutter.sdk")
+assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
diff --git a/assets/images/signin.png b/assets/images/signin.png
new file mode 100644
index 00000000..e1ab631f
Binary files /dev/null and b/assets/images/signin.png differ
diff --git a/ios/.gitignore b/ios/.gitignore
new file mode 100644
index 00000000..7a7f9873
--- /dev/null
+++ b/ios/.gitignore
@@ -0,0 +1,34 @@
+**/dgph
+*.mode1v3
+*.mode2v3
+*.moved-aside
+*.pbxuser
+*.perspectivev3
+**/*sync/
+.sconsign.dblite
+.tags*
+**/.vagrant/
+**/DerivedData/
+Icon?
+**/Pods/
+**/.symlinks/
+profile
+xcuserdata
+**/.generated/
+Flutter/App.framework
+Flutter/Flutter.framework
+Flutter/Flutter.podspec
+Flutter/Generated.xcconfig
+Flutter/ephemeral/
+Flutter/app.flx
+Flutter/app.zip
+Flutter/flutter_assets/
+Flutter/flutter_export_environment.sh
+ServiceDefinitions.json
+Runner/GeneratedPluginRegistrant.*
+
+# Exceptions to above rules.
+!default.mode1v3
+!default.mode2v3
+!default.pbxuser
+!default.perspectivev3
diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 00000000..8d4492f9
--- /dev/null
+++ b/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ App
+ CFBundleIdentifier
+ io.flutter.flutter.app
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ App
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ MinimumOSVersion
+ 9.0
+
+
diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig
new file mode 100644
index 00000000..592ceee8
--- /dev/null
+++ b/ios/Flutter/Debug.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig
new file mode 100644
index 00000000..592ceee8
--- /dev/null
+++ b/ios/Flutter/Release.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..3cb76ca7
--- /dev/null
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,481 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 50;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ );
+ sourceTree = "";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 97C146ED1CF9000F007C117D /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 9740EEB61CF901F6004384FC /* Run Script */,
+ 97C146EA1CF9000F007C117D /* Sources */,
+ 97C146EB1CF9000F007C117D /* Frameworks */,
+ 97C146EC1CF9000F007C117D /* Resources */,
+ 9705A1C41CF9048500538489 /* Embed Frameworks */,
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 97C146E61CF9000F007C117D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1300;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ LastSwiftMigration = 1100;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 97C146E51CF9000F007C117D;
+ productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 97C146ED1CF9000F007C117D /* Runner */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 97C146EC1CF9000F007C117D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+ };
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 97C146EA1CF9000F007C117D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C146FB1CF9000F007C117D /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 249021D3217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Profile;
+ };
+ 249021D4217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.desafioMobile;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Profile;
+ };
+ 97C147031CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 97C147061CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.desafioMobile;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Debug;
+ };
+ 97C147071CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.desafioMobile;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147031CF9000F007C117D /* Debug */,
+ 97C147041CF9000F007C117D /* Release */,
+ 249021D3217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147061CF9000F007C117D /* Debug */,
+ 97C147071CF9000F007C117D /* Release */,
+ 249021D4217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 97C146E61CF9000F007C117D /* Project object */;
+}
diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..919434a6
--- /dev/null
+++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 00000000..f9b0d7c5
--- /dev/null
+++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 00000000..c87d15a3
--- /dev/null
+++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..1d526a16
--- /dev/null
+++ b/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 00000000..f9b0d7c5
--- /dev/null
+++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift
new file mode 100644
index 00000000..62682332
--- /dev/null
+++ b/ios/Runner/AppDelegate.swift
@@ -0,0 +1,15 @@
+import UIKit
+import Flutter
+import GoogleMaps
+
+@UIApplicationMain
+@objc class AppDelegate: FlutterAppDelegate {
+ override func application(
+ _ application: UIApplication,
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+ ) -> Bool {
+ GeneratedPluginRegistrant.register(with: self)
+ GMSService.provideAPIKey("AIzaSyDXiOvvEt2PO1BDMkATZXEjjU_VtDGlJc0")
+ return super.application(application, didFinishLaunchingWithOptions: launchOptions)
+ }
+}
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000..d36b1fab
--- /dev/null
+++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,122 @@
+{
+ "images" : [
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "83.5x83.5",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-83.5x83.5@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "Icon-App-1024x1024@1x.png",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 00000000..dc9ada47
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 00000000..28c6bf03
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 00000000..2ccbfd96
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 00000000..f091b6b0
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 00000000..4cde1211
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 00000000..d0ef06e7
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 00000000..dcdc2306
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 00000000..2ccbfd96
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 00000000..c8f9ed8f
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 00000000..a6d6b860
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 00000000..a6d6b860
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 00000000..75b2d164
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 00000000..c4df70d3
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 00000000..6a84f41e
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 00000000..d0e1f585
Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 00000000..0bedcf2f
--- /dev/null
+++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 00000000..9da19eac
Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 00000000..9da19eac
Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 00000000..9da19eac
Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 00000000..89c2725b
--- /dev/null
+++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -0,0 +1,5 @@
+# Launch Screen Assets
+
+You can customize the launch screen with your own desired assets by replacing the image files in this directory.
+
+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.
\ No newline at end of file
diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 00000000..f2e259c7
--- /dev/null
+++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 00000000..f3c28516
--- /dev/null
+++ b/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
new file mode 100644
index 00000000..dc61bd26
--- /dev/null
+++ b/ios/Runner/Info.plist
@@ -0,0 +1,55 @@
+
+
+
+
+ io.flutter.embedded_views_preview
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleDisplayName
+ Desafio Mobile
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ desafio_mobile
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+ CADisableMinimumFrameDurationOnPhone
+
+ NSLocationWhenInUseUsageDescription
+ This app needs access to location when open.
+ NSLocationAlwaysUsageDescription
+ This app needs access to location when in the background.
+
+
diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 00000000..308a2a56
--- /dev/null
+++ b/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1 @@
+#import "GeneratedPluginRegistrant.h"
diff --git a/lib/main.dart b/lib/main.dart
new file mode 100644
index 00000000..6e5889e6
--- /dev/null
+++ b/lib/main.dart
@@ -0,0 +1,31 @@
+import 'package:desafio_mobile/route_generator.dart';
+import 'package:desafio_mobile/shell.dart';
+import 'package:firebase_analytics/observer.dart';
+import 'package:firebase_core/firebase_core.dart';
+import 'package:firebase_crashlytics/firebase_crashlytics.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+Future main() async {
+ WidgetsFlutterBinding.ensureInitialized();
+ await Firebase.initializeApp();
+ FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError;
+
+ runApp(const Shell(child: MyApp()));
+}
+
+class MyApp extends StatelessWidget {
+ const MyApp({Key? key}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ debugShowCheckedModeBanner: false,
+ title: 'Flutter Demo',
+ theme: ThemeData.dark(),
+ initialRoute: '/login',
+ onGenerateRoute: RouteGenerator.generatorRoute,
+ navigatorObservers: [context.read()],
+ );
+ }
+}
diff --git a/lib/route_generator.dart b/lib/route_generator.dart
new file mode 100644
index 00000000..5fac53a6
--- /dev/null
+++ b/lib/route_generator.dart
@@ -0,0 +1,42 @@
+import 'package:desafio_mobile/src/domain/entities/user.dart';
+import 'package:flutter/material.dart';
+import 'package:google_maps_flutter/google_maps_flutter.dart';
+
+import 'src/presentation/pages/home_page.dart';
+import 'src/presentation/pages/signin_page.dart';
+
+class RouteGenerator {
+ static Route generatorRoute(RouteSettings settings) {
+ final args = settings.arguments;
+
+ switch (settings.name) {
+ case '/login':
+ return MaterialPageRoute(builder: (context) => const SigninPage());
+ case '/home':
+ if ((args! as List)[0] is User) {
+ if ((args as List)[1] is LatLng) {
+ return MaterialPageRoute(
+ builder: (context) => HomePage(
+ user: (args)[0],
+ position: args[1],
+ ),
+ );
+ }
+ }
+ return _erroRoute();
+ default:
+ _erroRoute();
+ return _erroRoute();
+ }
+ }
+
+ static Route _erroRoute() {
+ return MaterialPageRoute(
+ builder: (context) {
+ return const Scaffold(
+ body: Center(child: Text('Error')),
+ );
+ },
+ );
+ }
+}
diff --git a/lib/shell.dart b/lib/shell.dart
new file mode 100644
index 00000000..5f5c7746
--- /dev/null
+++ b/lib/shell.dart
@@ -0,0 +1,156 @@
+import 'package:desafio_mobile/src/external/database/app_database.dart';
+import 'package:desafio_mobile/src/external/datasources/save_bank_location_datasource.dart';
+import 'package:firebase_analytics/firebase_analytics.dart';
+import 'package:firebase_auth/firebase_auth.dart';
+import 'package:firebase_crashlytics/firebase_crashlytics.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'src/data/repositories/auth_repository.dart';
+import 'src/data/repositories/bank_repository.dart';
+import 'src/data/repositories/position_repository.dart';
+import 'src/domain/usecases/auth_user_usecase.dart';
+import 'src/domain/usecases/featch_bank_location_usecase.dart';
+import 'src/domain/usecases/get_current_position_usecase.dart';
+import 'src/domain/usecases/save_bank_location_usecase.dart';
+import 'src/external/datasources/auth_datasource.dart';
+import 'src/external/datasources/featch_bank_location_datasource.dart';
+import 'src/external/datasources/get_current_position_datasource.dart';
+import 'src/external/location_device/localization.dart';
+import 'src/presentation/bloc/home/database/database_bloc.dart';
+import 'src/presentation/bloc/home/position/position_bloc.dart';
+import 'src/presentation/bloc/signin/signin_bloc.dart';
+
+class Shell extends StatelessWidget {
+ final Widget child;
+ const Shell({Key? key, required this.child}) : super(key: key);
+
+ static final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
+ static final FirebaseAnalytics _analytics = FirebaseAnalytics.instance;
+ static final FirebaseAnalyticsObserver _observer =
+ FirebaseAnalyticsObserver(analytics: _analytics);
+ static final crashlytics = FirebaseCrashlytics.instance;
+ static final db = AppDatabase.instance;
+
+ List get listRepositoryProvider => [
+ //Bank place usecase injection
+ RepositoryProvider(
+ create: (context) => db,
+ ),
+ RepositoryProvider(create: (context) {
+ final db = context.read();
+ return SaveBankLocationDatasource(db);
+ }),
+ RepositoryProvider(create: (context) {
+ final db = context.read();
+ return FeatchBankLocationDatasource(db);
+ }),
+ RepositoryProvider(create: (context) {
+ final saveBankLocation = context.read();
+ final featchBankLocation = context.read();
+ return BankRepository(saveBankLocation, featchBankLocation);
+ }),
+ RepositoryProvider(create: (context) {
+ final repository = context.read();
+ return SaveBankLocationUsecase(repository);
+ }),
+ RepositoryProvider(create: (context) {
+ final repository = context.read();
+ return FeatchBankLocationUsecase(repository);
+ }),
+
+ //position usecase injection
+ RepositoryProvider(
+ create: (context) => Localization(),
+ ),
+ RepositoryProvider(create: (context) {
+ final localization = context.read();
+ return GetCurrentPositionDatasource(localization);
+ }),
+ RepositoryProvider(create: (context) {
+ final datasource = context.read();
+ return PositionRepository(datasource);
+ }),
+ RepositoryProvider(create: (context) {
+ final repository = context.read();
+ return GetCurrentPositionUsecase(repository);
+ }),
+
+ //FirebaseCrashlytics injection
+ RepositoryProvider(
+ create: (context) => crashlytics,
+ ),
+
+ //FirebaseAnalyticsObserver injection
+ RepositoryProvider(
+ create: (context) => _observer,
+ ),
+
+ //FirebaseAnalytics injection
+ RepositoryProvider(
+ create: (context) => _analytics,
+ ),
+
+ //FirebaseAuth injection
+ RepositoryProvider(
+ create: (context) => _firebaseAuth,
+ ),
+
+ //authentication usecase injection
+ RepositoryProvider(
+ create: (context) {
+ final firebaseAuth = context.read();
+ final analytics = context.read();
+ return AuthDatasource(
+ firebaseAuth: firebaseAuth,
+ analytics: analytics,
+ crashlytics: crashlytics,
+ );
+ },
+ ),
+ RepositoryProvider(
+ create: (context) {
+ final authDatasource = context.read();
+ return AuthRepository(authDatasource);
+ },
+ ),
+ RepositoryProvider(
+ create: (context) {
+ final authRepository = context.read();
+ return AuthUserUsecase(authRepository);
+ },
+ )
+ ];
+
+ List get listBlocProvider => [
+ BlocProvider(
+ create: (context) {
+ final authUserUsecase = context.read();
+ return SigninBloc(authUserUsecase);
+ },
+ ),
+ BlocProvider(
+ create: (context) {
+ final usecase = context.read();
+ return PositionBloc(usecase);
+ },
+ ),
+ BlocProvider(
+ create: (context) {
+ final saveBankLocation = context.read();
+ final featchBankLocation = context.read();
+ return DatabaseBloc(saveBankLocation, featchBankLocation);
+ },
+ ),
+ ];
+
+ @override
+ Widget build(BuildContext context) {
+ return MultiRepositoryProvider(
+ providers: listRepositoryProvider,
+ child: MultiBlocProvider(
+ providers: listBlocProvider,
+ child: child,
+ ),
+ );
+ }
+}
diff --git a/lib/src/data/adapters/location_adapter.dart b/lib/src/data/adapters/location_adapter.dart
new file mode 100644
index 00000000..ea4f1bcb
--- /dev/null
+++ b/lib/src/data/adapters/location_adapter.dart
@@ -0,0 +1,17 @@
+import 'package:desafio_mobile/src/domain/entities/user.dart';
+
+import '../../domain/entities/location.dart';
+import '../../external/database/app_database.dart';
+
+class LocationAdapter {
+ LocationAdapter._();
+
+ static Location fromJson(UserLocalizationData data) {
+ return Location(
+ id: data.id,
+ latitude: double.parse(data.latitude),
+ longitude: double.parse(data.longitude),
+ user: User(uid: data.uid, email: data.email),
+ );
+ }
+}
diff --git a/lib/src/data/adapters/position_adapter.dart b/lib/src/data/adapters/position_adapter.dart
new file mode 100644
index 00000000..2ee6627c
--- /dev/null
+++ b/lib/src/data/adapters/position_adapter.dart
@@ -0,0 +1,10 @@
+import 'package:geolocator/geolocator.dart';
+import 'package:google_maps_flutter/google_maps_flutter.dart';
+
+class PositionAdapter {
+ PositionAdapter._();
+
+ static LatLng fromJson(Position data) {
+ return LatLng(data.latitude, data.longitude);
+ }
+}
diff --git a/lib/src/data/adapters/user_adapter.dart b/lib/src/data/adapters/user_adapter.dart
new file mode 100644
index 00000000..b6977248
--- /dev/null
+++ b/lib/src/data/adapters/user_adapter.dart
@@ -0,0 +1,12 @@
+import 'package:desafio_mobile/src/domain/entities/user.dart';
+
+class UserAdapter {
+ UserAdapter._();
+
+ static User fromJson(Map data) {
+ return User(
+ uid: data['uid'],
+ email: data['email'],
+ );
+ }
+}
diff --git a/lib/src/data/datasources/auth_datasource.dart b/lib/src/data/datasources/auth_datasource.dart
new file mode 100644
index 00000000..213affb0
--- /dev/null
+++ b/lib/src/data/datasources/auth_datasource.dart
@@ -0,0 +1,5 @@
+import '../../shared/data_struct/auth.dart';
+
+abstract class IAuthDatasource {
+ Future> authUser(AuthUser authUser);
+}
diff --git a/lib/src/data/datasources/featch_bank_location_datasource.dart b/lib/src/data/datasources/featch_bank_location_datasource.dart
new file mode 100644
index 00000000..a320738b
--- /dev/null
+++ b/lib/src/data/datasources/featch_bank_location_datasource.dart
@@ -0,0 +1,5 @@
+import '../../external/database/app_database.dart';
+
+abstract class IFeatchBankLocationDatasource {
+ Future featchLocation(String uid);
+}
diff --git a/lib/src/data/datasources/get_current_position_datasource.dart b/lib/src/data/datasources/get_current_position_datasource.dart
new file mode 100644
index 00000000..d184004a
--- /dev/null
+++ b/lib/src/data/datasources/get_current_position_datasource.dart
@@ -0,0 +1,5 @@
+import 'package:geolocator/geolocator.dart';
+
+abstract class IGetCurrentPositionDatasource {
+ Future featchLocation();
+}
diff --git a/lib/src/data/datasources/save_bank_location_datasource.dart b/lib/src/data/datasources/save_bank_location_datasource.dart
new file mode 100644
index 00000000..bdd2e1b0
--- /dev/null
+++ b/lib/src/data/datasources/save_bank_location_datasource.dart
@@ -0,0 +1,5 @@
+import 'package:desafio_mobile/src/domain/entities/location.dart';
+
+abstract class ISaveBankLocationDatasource {
+ Future saveLocation(Location location);
+}
diff --git a/lib/src/data/repositories/auth_repository.dart b/lib/src/data/repositories/auth_repository.dart
new file mode 100644
index 00000000..7a17b68d
--- /dev/null
+++ b/lib/src/data/repositories/auth_repository.dart
@@ -0,0 +1,24 @@
+import 'package:desafio_mobile/src/data/adapters/user_adapter.dart';
+import 'package:desafio_mobile/src/domain/errors/errors.dart';
+import 'package:dartz/dartz.dart';
+
+import '../../domain/entities/user.dart';
+import '../../domain/repositories/auth_repository.dart';
+import '../../shared/data_struct/auth.dart';
+import '../datasources/auth_datasource.dart';
+
+class AuthRepository extends IAuthRepository {
+ final IAuthDatasource _authDatasource;
+
+ AuthRepository(this._authDatasource);
+ @override
+ Future> authUser(AuthUser authUser) async {
+ try {
+ final user = await _authDatasource.authUser(authUser);
+
+ return right(UserAdapter.fromJson(user));
+ } on IErrorsException catch (e) {
+ return left(e);
+ }
+ }
+}
diff --git a/lib/src/data/repositories/bank_repository.dart b/lib/src/data/repositories/bank_repository.dart
new file mode 100644
index 00000000..9db977d8
--- /dev/null
+++ b/lib/src/data/repositories/bank_repository.dart
@@ -0,0 +1,45 @@
+import 'package:desafio_mobile/src/data/adapters/location_adapter.dart';
+import 'package:desafio_mobile/src/domain/errors/errors.dart';
+import 'package:dartz/dartz.dart';
+
+import '../../domain/entities/location.dart';
+import '../../domain/repositories/bank_repository.dart';
+import '../datasources/featch_bank_location_datasource.dart';
+import '../datasources/save_bank_location_datasource.dart';
+
+class BankRepository extends IBankRepository {
+ final ISaveBankLocationDatasource _saveLocation;
+ final IFeatchBankLocationDatasource _featchLocation;
+
+ BankRepository(this._saveLocation, this._featchLocation);
+
+ @override
+ Future> saveLocation(Location location) async {
+ try {
+ final result = await _saveLocation.saveLocation(location);
+
+ return right(result);
+ } on IErrorsException catch (e, s) {
+ final error = FalireException(e.message, s);
+ return left(error);
+ }
+ }
+
+ @override
+ Future> featchLocation(String uid) async {
+ try {
+ final result = await _featchLocation.featchLocation(uid);
+
+ Location? location;
+
+ if (result != null) {
+ location = LocationAdapter.fromJson(result);
+ }
+
+ return right(location);
+ } on IErrorsException catch (e, s) {
+ final error = FalireException(e.message, s);
+ return left(error);
+ }
+ }
+}
diff --git a/lib/src/data/repositories/position_repository.dart b/lib/src/data/repositories/position_repository.dart
new file mode 100644
index 00000000..c7f7ea31
--- /dev/null
+++ b/lib/src/data/repositories/position_repository.dart
@@ -0,0 +1,28 @@
+import 'package:desafio_mobile/src/data/adapters/position_adapter.dart';
+import 'package:desafio_mobile/src/domain/errors/errors.dart';
+
+import 'package:dartz/dartz.dart';
+import 'package:google_maps_flutter/google_maps_flutter.dart';
+
+import '../../domain/repositories/position_repository.dart';
+import '../datasources/get_current_position_datasource.dart';
+
+class PositionRepository implements IPositionRepository {
+ final IGetCurrentPositionDatasource _datasource;
+
+ PositionRepository(this._datasource);
+
+ @override
+ Future> featchPosition() async {
+ try {
+ final result = await _datasource.featchLocation();
+
+ final resultPosition = PositionAdapter.fromJson(result);
+
+ return right(resultPosition);
+ } on IErrorsException catch (e, s) {
+ final error = FalireException(e.message, s);
+ return left(error);
+ }
+ }
+}
diff --git a/lib/src/domain/entities/location.dart b/lib/src/domain/entities/location.dart
new file mode 100644
index 00000000..b52306d0
--- /dev/null
+++ b/lib/src/domain/entities/location.dart
@@ -0,0 +1,15 @@
+import 'package:desafio_mobile/src/domain/entities/user.dart';
+
+class Location {
+ final int? id;
+ final double latitude;
+ final double longitude;
+ final User user;
+
+ Location({
+ this.id,
+ required this.latitude,
+ required this.longitude,
+ required this.user,
+ });
+}
diff --git a/lib/src/domain/entities/user.dart b/lib/src/domain/entities/user.dart
new file mode 100644
index 00000000..533d4b56
--- /dev/null
+++ b/lib/src/domain/entities/user.dart
@@ -0,0 +1,6 @@
+class User {
+ final String uid;
+ final String email;
+
+ User({required this.uid, required this.email});
+}
diff --git a/lib/src/domain/errors/errors.dart b/lib/src/domain/errors/errors.dart
new file mode 100644
index 00000000..ac35f0f4
--- /dev/null
+++ b/lib/src/domain/errors/errors.dart
@@ -0,0 +1,10 @@
+abstract class IErrorsException {
+ final String message;
+ final StackTrace? stackTrace;
+
+ const IErrorsException(this.message, [this.stackTrace]);
+}
+
+class FalireException extends IErrorsException {
+ const FalireException(super.message, [super.stackTrace]);
+}
diff --git a/lib/src/domain/repositories/auth_repository.dart b/lib/src/domain/repositories/auth_repository.dart
new file mode 100644
index 00000000..71d24f74
--- /dev/null
+++ b/lib/src/domain/repositories/auth_repository.dart
@@ -0,0 +1,9 @@
+import 'package:dartz/dartz.dart';
+
+import '../../shared/data_struct/auth.dart';
+import '../entities/user.dart';
+import '../errors/errors.dart';
+
+abstract class IAuthRepository {
+ Future> authUser(AuthUser authUser);
+}
diff --git a/lib/src/domain/repositories/bank_repository.dart b/lib/src/domain/repositories/bank_repository.dart
new file mode 100644
index 00000000..73f54201
--- /dev/null
+++ b/lib/src/domain/repositories/bank_repository.dart
@@ -0,0 +1,9 @@
+import 'package:dartz/dartz.dart';
+
+import '../entities/location.dart';
+import '../errors/errors.dart';
+
+abstract class IBankRepository {
+ Future> saveLocation(Location location);
+ Future> featchLocation(String uid);
+}
diff --git a/lib/src/domain/repositories/position_repository.dart b/lib/src/domain/repositories/position_repository.dart
new file mode 100644
index 00000000..4e34eacd
--- /dev/null
+++ b/lib/src/domain/repositories/position_repository.dart
@@ -0,0 +1,7 @@
+import 'package:dartz/dartz.dart';
+import 'package:google_maps_flutter/google_maps_flutter.dart';
+import '../errors/errors.dart';
+
+abstract class IPositionRepository {
+ Future> featchPosition();
+}
diff --git a/lib/src/domain/usecases/auth_user_usecase.dart b/lib/src/domain/usecases/auth_user_usecase.dart
new file mode 100644
index 00000000..d5284b5e
--- /dev/null
+++ b/lib/src/domain/usecases/auth_user_usecase.dart
@@ -0,0 +1,27 @@
+import 'package:dartz/dartz.dart';
+import '../../shared/data_struct/auth.dart';
+import '../entities/user.dart';
+import '../errors/errors.dart';
+import '../repositories/auth_repository.dart';
+
+abstract class IAuthUserUsecase {
+ Future> call(AuthUser authUser);
+}
+
+class AuthUserUsecase implements IAuthUserUsecase {
+ final IAuthRepository _repository;
+
+ AuthUserUsecase(this._repository);
+
+ @override
+ Future> call(AuthUser authUser) async {
+ if (authUser.email.isEmpty || authUser.email == '') {
+ return left(const FalireException('login is empty or null'));
+ }
+ if (authUser.password.isEmpty || authUser.password == '') {
+ return left(const FalireException('password is empty or null'));
+ }
+ final result = await _repository.authUser(authUser);
+ return result;
+ }
+}
diff --git a/lib/src/domain/usecases/featch_bank_location_usecase.dart b/lib/src/domain/usecases/featch_bank_location_usecase.dart
new file mode 100644
index 00000000..4ac6b13d
--- /dev/null
+++ b/lib/src/domain/usecases/featch_bank_location_usecase.dart
@@ -0,0 +1,22 @@
+import 'package:dartz/dartz.dart';
+import 'package:desafio_mobile/src/domain/entities/location.dart';
+
+import '../errors/errors.dart';
+import '../repositories/bank_repository.dart';
+
+abstract class IFeatchBankLocationUsecase {
+ Future> call(String uid);
+}
+
+class FeatchBankLocationUsecase implements IFeatchBankLocationUsecase {
+ final IBankRepository _repository;
+
+ FeatchBankLocationUsecase(this._repository);
+
+ @override
+ Future> call(String uid) async {
+ final result = await _repository.featchLocation(uid);
+
+ return result;
+ }
+}
diff --git a/lib/src/domain/usecases/get_current_position_usecase.dart b/lib/src/domain/usecases/get_current_position_usecase.dart
new file mode 100644
index 00000000..28fa1781
--- /dev/null
+++ b/lib/src/domain/usecases/get_current_position_usecase.dart
@@ -0,0 +1,19 @@
+import 'package:dartz/dartz.dart';
+import 'package:google_maps_flutter/google_maps_flutter.dart';
+import '../errors/errors.dart';
+import '../repositories/position_repository.dart';
+
+abstract class IGetCurrentPositionUsecase {
+ Future> call();
+}
+
+class GetCurrentPositionUsecase implements IGetCurrentPositionUsecase {
+ final IPositionRepository _repository;
+
+ GetCurrentPositionUsecase(this._repository);
+
+ @override
+ Future> call() async {
+ return await _repository.featchPosition();
+ }
+}
diff --git a/lib/src/domain/usecases/save_bank_location_usecase.dart b/lib/src/domain/usecases/save_bank_location_usecase.dart
new file mode 100644
index 00000000..5302888b
--- /dev/null
+++ b/lib/src/domain/usecases/save_bank_location_usecase.dart
@@ -0,0 +1,35 @@
+import 'package:dartz/dartz.dart';
+import 'package:desafio_mobile/src/domain/entities/location.dart';
+
+import '../errors/errors.dart';
+import '../repositories/bank_repository.dart';
+
+abstract class ISaveBankLocationUsecase {
+ Future> call(Location location);
+}
+
+class SaveBankLocationUsecase implements ISaveBankLocationUsecase {
+ final IBankRepository _repository;
+
+ SaveBankLocationUsecase(this._repository);
+
+ @override
+ Future> call(Location location) async {
+ if (location.user.uid.isEmpty || location.user.uid == '') {
+ return left(const FalireException('uid is empty or null'));
+ }
+ if (location.user.email.isEmpty || location.user.email == '') {
+ return left(const FalireException('email is empty or null'));
+ }
+ if (location.latitude == 0) {
+ return left(const FalireException('latitude is empty or null'));
+ }
+ if (location.longitude == 0) {
+ return left(const FalireException('longitude is empty or null'));
+ }
+
+ final result = await _repository.saveLocation(location);
+
+ return result;
+ }
+}
diff --git a/lib/src/external/database/app_database.dart b/lib/src/external/database/app_database.dart
new file mode 100644
index 00000000..216d3765
--- /dev/null
+++ b/lib/src/external/database/app_database.dart
@@ -0,0 +1,30 @@
+import 'package:moor_flutter/moor_flutter.dart';
+
+import 'dao/user_localization_dao.dart';
+
+part 'app_database.g.dart';
+
+class UserLocalization extends Table {
+ IntColumn get id => integer().autoIncrement().nullable()();
+ TextColumn get uid => text().unique()();
+ TextColumn get email => text()();
+ TextColumn get latitude => text()();
+ TextColumn get longitude => text()();
+}
+
+@UseMoor(tables: [UserLocalization])
+class AppDatabase extends _$AppDatabase {
+ static AppDatabase instance = AppDatabase._internal();
+
+ late UserLocalizationDAO localizationDAO;
+
+ AppDatabase._internal()
+ : super(FlutterQueryExecutor.inDatabaseFolder(
+ path: 'app_db.sqlite',
+ )) {
+ localizationDAO = UserLocalizationDAO(this);
+ }
+
+ @override
+ int get schemaVersion => 1;
+}
diff --git a/lib/src/external/database/app_database.g.dart b/lib/src/external/database/app_database.g.dart
new file mode 100644
index 00000000..cbff0847
--- /dev/null
+++ b/lib/src/external/database/app_database.g.dart
@@ -0,0 +1,310 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'app_database.dart';
+
+// **************************************************************************
+// MoorGenerator
+// **************************************************************************
+
+// ignore_for_file: type=lint
+class UserLocalizationData extends DataClass
+ implements Insertable {
+ final int? id;
+ final String uid;
+ final String email;
+ final String latitude;
+ final String longitude;
+ UserLocalizationData(
+ {this.id,
+ required this.uid,
+ required this.email,
+ required this.latitude,
+ required this.longitude});
+ factory UserLocalizationData.fromData(
+ Map data, GeneratedDatabase db,
+ {String? prefix}) {
+ final effectivePrefix = prefix ?? '';
+ return UserLocalizationData(
+ id: const IntType().mapFromDatabaseResponse(data['${effectivePrefix}id']),
+ uid: const StringType()
+ .mapFromDatabaseResponse(data['${effectivePrefix}uid'])!,
+ email: const StringType()
+ .mapFromDatabaseResponse(data['${effectivePrefix}email'])!,
+ latitude: const StringType()
+ .mapFromDatabaseResponse(data['${effectivePrefix}latitude'])!,
+ longitude: const StringType()
+ .mapFromDatabaseResponse(data['${effectivePrefix}longitude'])!,
+ );
+ }
+ @override
+ Map toColumns(bool nullToAbsent) {
+ final map = {};
+ if (!nullToAbsent || id != null) {
+ map['id'] = Variable(id);
+ }
+ map['uid'] = Variable(uid);
+ map['email'] = Variable(email);
+ map['latitude'] = Variable(latitude);
+ map['longitude'] = Variable(longitude);
+ return map;
+ }
+
+ UserLocalizationCompanion toCompanion(bool nullToAbsent) {
+ return UserLocalizationCompanion(
+ id: id == null && nullToAbsent ? const Value.absent() : Value(id),
+ uid: Value(uid),
+ email: Value(email),
+ latitude: Value(latitude),
+ longitude: Value(longitude),
+ );
+ }
+
+ factory UserLocalizationData.fromJson(Map json,
+ {ValueSerializer? serializer}) {
+ serializer ??= moorRuntimeOptions.defaultSerializer;
+ return UserLocalizationData(
+ id: serializer.fromJson(json['id']),
+ uid: serializer.fromJson(json['uid']),
+ email: serializer.fromJson(json['email']),
+ latitude: serializer.fromJson(json['latitude']),
+ longitude: serializer.fromJson(json['longitude']),
+ );
+ }
+ @override
+ Map toJson({ValueSerializer? serializer}) {
+ serializer ??= moorRuntimeOptions.defaultSerializer;
+ return {
+ 'id': serializer.toJson(id),
+ 'uid': serializer.toJson(uid),
+ 'email': serializer.toJson(email),
+ 'latitude': serializer.toJson(latitude),
+ 'longitude': serializer.toJson(longitude),
+ };
+ }
+
+ UserLocalizationData copyWith(
+ {int? id,
+ String? uid,
+ String? email,
+ String? latitude,
+ String? longitude}) =>
+ UserLocalizationData(
+ id: id ?? this.id,
+ uid: uid ?? this.uid,
+ email: email ?? this.email,
+ latitude: latitude ?? this.latitude,
+ longitude: longitude ?? this.longitude,
+ );
+ @override
+ String toString() {
+ return (StringBuffer('UserLocalizationData(')
+ ..write('id: $id, ')
+ ..write('uid: $uid, ')
+ ..write('email: $email, ')
+ ..write('latitude: $latitude, ')
+ ..write('longitude: $longitude')
+ ..write(')'))
+ .toString();
+ }
+
+ @override
+ int get hashCode => Object.hash(id, uid, email, latitude, longitude);
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ (other is UserLocalizationData &&
+ other.id == this.id &&
+ other.uid == this.uid &&
+ other.email == this.email &&
+ other.latitude == this.latitude &&
+ other.longitude == this.longitude);
+}
+
+class UserLocalizationCompanion extends UpdateCompanion {
+ final Value id;
+ final Value uid;
+ final Value email;
+ final Value latitude;
+ final Value longitude;
+ const UserLocalizationCompanion({
+ this.id = const Value.absent(),
+ this.uid = const Value.absent(),
+ this.email = const Value.absent(),
+ this.latitude = const Value.absent(),
+ this.longitude = const Value.absent(),
+ });
+ UserLocalizationCompanion.insert({
+ this.id = const Value.absent(),
+ required String uid,
+ required String email,
+ required String latitude,
+ required String longitude,
+ }) : uid = Value(uid),
+ email = Value(email),
+ latitude = Value(latitude),
+ longitude = Value(longitude);
+ static Insertable custom({
+ Expression? id,
+ Expression? uid,
+ Expression? email,
+ Expression? latitude,
+ Expression? longitude,
+ }) {
+ return RawValuesInsertable({
+ if (id != null) 'id': id,
+ if (uid != null) 'uid': uid,
+ if (email != null) 'email': email,
+ if (latitude != null) 'latitude': latitude,
+ if (longitude != null) 'longitude': longitude,
+ });
+ }
+
+ UserLocalizationCompanion copyWith(
+ {Value? id,
+ Value? uid,
+ Value? email,
+ Value? latitude,
+ Value? longitude}) {
+ return UserLocalizationCompanion(
+ id: id ?? this.id,
+ uid: uid ?? this.uid,
+ email: email ?? this.email,
+ latitude: latitude ?? this.latitude,
+ longitude: longitude ?? this.longitude,
+ );
+ }
+
+ @override
+ Map toColumns(bool nullToAbsent) {
+ final map = {};
+ if (id.present) {
+ map['id'] = Variable(id.value);
+ }
+ if (uid.present) {
+ map['uid'] = Variable(uid.value);
+ }
+ if (email.present) {
+ map['email'] = Variable(email.value);
+ }
+ if (latitude.present) {
+ map['latitude'] = Variable(latitude.value);
+ }
+ if (longitude.present) {
+ map['longitude'] = Variable(longitude.value);
+ }
+ return map;
+ }
+
+ @override
+ String toString() {
+ return (StringBuffer('UserLocalizationCompanion(')
+ ..write('id: $id, ')
+ ..write('uid: $uid, ')
+ ..write('email: $email, ')
+ ..write('latitude: $latitude, ')
+ ..write('longitude: $longitude')
+ ..write(')'))
+ .toString();
+ }
+}
+
+class $UserLocalizationTable extends UserLocalization
+ with TableInfo<$UserLocalizationTable, UserLocalizationData> {
+ @override
+ final GeneratedDatabase attachedDatabase;
+ final String? _alias;
+ $UserLocalizationTable(this.attachedDatabase, [this._alias]);
+ final VerificationMeta _idMeta = const VerificationMeta('id');
+ @override
+ late final GeneratedColumn id = GeneratedColumn(
+ 'id', aliasedName, true,
+ type: const IntType(),
+ requiredDuringInsert: false,
+ defaultConstraints: 'PRIMARY KEY AUTOINCREMENT');
+ final VerificationMeta _uidMeta = const VerificationMeta('uid');
+ @override
+ late final GeneratedColumn uid = GeneratedColumn(
+ 'uid', aliasedName, false,
+ type: const StringType(),
+ requiredDuringInsert: true,
+ defaultConstraints: 'UNIQUE');
+ final VerificationMeta _emailMeta = const VerificationMeta('email');
+ @override
+ late final GeneratedColumn email = GeneratedColumn(
+ 'email', aliasedName, false,
+ type: const StringType(), requiredDuringInsert: true);
+ final VerificationMeta _latitudeMeta = const VerificationMeta('latitude');
+ @override
+ late final GeneratedColumn latitude = GeneratedColumn(
+ 'latitude', aliasedName, false,
+ type: const StringType(), requiredDuringInsert: true);
+ final VerificationMeta _longitudeMeta = const VerificationMeta('longitude');
+ @override
+ late final GeneratedColumn longitude = GeneratedColumn(
+ 'longitude', aliasedName, false,
+ type: const StringType(), requiredDuringInsert: true);
+ @override
+ List get $columns => [id, uid, email, latitude, longitude];
+ @override
+ String get aliasedName => _alias ?? 'user_localization';
+ @override
+ String get actualTableName => 'user_localization';
+ @override
+ VerificationContext validateIntegrity(
+ Insertable instance,
+ {bool isInserting = false}) {
+ final context = VerificationContext();
+ final data = instance.toColumns(true);
+ if (data.containsKey('id')) {
+ context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
+ }
+ if (data.containsKey('uid')) {
+ context.handle(
+ _uidMeta, uid.isAcceptableOrUnknown(data['uid']!, _uidMeta));
+ } else if (isInserting) {
+ context.missing(_uidMeta);
+ }
+ if (data.containsKey('email')) {
+ context.handle(
+ _emailMeta, email.isAcceptableOrUnknown(data['email']!, _emailMeta));
+ } else if (isInserting) {
+ context.missing(_emailMeta);
+ }
+ if (data.containsKey('latitude')) {
+ context.handle(_latitudeMeta,
+ latitude.isAcceptableOrUnknown(data['latitude']!, _latitudeMeta));
+ } else if (isInserting) {
+ context.missing(_latitudeMeta);
+ }
+ if (data.containsKey('longitude')) {
+ context.handle(_longitudeMeta,
+ longitude.isAcceptableOrUnknown(data['longitude']!, _longitudeMeta));
+ } else if (isInserting) {
+ context.missing(_longitudeMeta);
+ }
+ return context;
+ }
+
+ @override
+ Set get $primaryKey => {id};
+ @override
+ UserLocalizationData map(Map data, {String? tablePrefix}) {
+ return UserLocalizationData.fromData(data, attachedDatabase,
+ prefix: tablePrefix != null ? '$tablePrefix.' : null);
+ }
+
+ @override
+ $UserLocalizationTable createAlias(String alias) {
+ return $UserLocalizationTable(attachedDatabase, alias);
+ }
+}
+
+abstract class _$AppDatabase extends GeneratedDatabase {
+ _$AppDatabase(QueryExecutor e) : super(SqlTypeSystem.defaultInstance, e);
+ late final $UserLocalizationTable userLocalization =
+ $UserLocalizationTable(this);
+ @override
+ Iterable get allTables => allSchemaEntities.whereType();
+ @override
+ List get allSchemaEntities => [userLocalization];
+}
diff --git a/lib/src/external/database/dao/user_localization_dao.dart b/lib/src/external/database/dao/user_localization_dao.dart
new file mode 100644
index 00000000..b99705da
--- /dev/null
+++ b/lib/src/external/database/dao/user_localization_dao.dart
@@ -0,0 +1,29 @@
+import 'package:desafio_mobile/src/external/database/app_database.dart';
+import 'package:moor_flutter/moor_flutter.dart';
+
+part 'user_localization_dao.g.dart';
+
+@UseDao(tables: [UserLocalization])
+class UserLocalizationDAO extends DatabaseAccessor with _$UserLocalizationDAOMixin {
+ Future featchLocalization(String uid) async {
+ final list = await (select(userLocalization)).get();
+ UserLocalizationData? result;
+
+ for (var element in list) {
+ if (element.uid == uid) {
+ result = element;
+ }
+ }
+ return result;
+ }
+
+ Future addLocalization(UserLocalizationData entity) {
+ return into(userLocalization).insert(entity);
+ }
+
+ Future updateLocalization(UserLocalizationData entity) {
+ return update(userLocalization).replace(entity);
+ }
+
+ UserLocalizationDAO(AppDatabase db) : super(db);
+}
diff --git a/lib/src/external/database/dao/user_localization_dao.g.dart b/lib/src/external/database/dao/user_localization_dao.g.dart
new file mode 100644
index 00000000..5b123e0c
--- /dev/null
+++ b/lib/src/external/database/dao/user_localization_dao.g.dart
@@ -0,0 +1,12 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'user_localization_dao.dart';
+
+// **************************************************************************
+// DaoGenerator
+// **************************************************************************
+
+mixin _$UserLocalizationDAOMixin on DatabaseAccessor {
+ $UserLocalizationTable get userLocalization =>
+ attachedDatabase.userLocalization;
+}
diff --git a/lib/src/external/datasources/auth_datasource.dart b/lib/src/external/datasources/auth_datasource.dart
new file mode 100644
index 00000000..0d02bbd4
--- /dev/null
+++ b/lib/src/external/datasources/auth_datasource.dart
@@ -0,0 +1,43 @@
+import 'package:desafio_mobile/src/data/datasources/auth_datasource.dart';
+import 'package:desafio_mobile/src/domain/errors/errors.dart';
+import 'package:firebase_analytics/firebase_analytics.dart';
+import 'package:firebase_auth/firebase_auth.dart';
+import 'package:firebase_crashlytics/firebase_crashlytics.dart';
+
+import '../../shared/data_struct/auth.dart';
+
+class AuthDatasource implements IAuthDatasource {
+ final FirebaseAuth firebaseAuth;
+ final FirebaseAnalytics analytics;
+ final FirebaseCrashlytics crashlytics;
+
+ AuthDatasource({
+ required this.firebaseAuth,
+ required this.analytics,
+ required this.crashlytics,
+ });
+
+ Map _getMapUser(User user) {
+ return {'uid': user.uid, 'email': user.email};
+ }
+
+ @override
+ Future> authUser(AuthUser authUser) async {
+ try {
+ final authResult = await firebaseAuth.signInWithEmailAndPassword(
+ email: authUser.email, password: authUser.password);
+
+ analytics.logLogin(loginMethod: authResult.user!.email);
+
+ return _getMapUser(authResult.user!);
+ } on FirebaseAuthException catch (e, s) {
+ crashlytics.setCustomKey(
+ 'outer layer',
+ 'authUser method in external/datasource/auth_datadource',
+ );
+ crashlytics.recordError(e.message!, s);
+
+ throw FalireException(e.message!, s);
+ }
+ }
+}
diff --git a/lib/src/external/datasources/featch_bank_location_datasource.dart b/lib/src/external/datasources/featch_bank_location_datasource.dart
new file mode 100644
index 00000000..f70524a5
--- /dev/null
+++ b/lib/src/external/datasources/featch_bank_location_datasource.dart
@@ -0,0 +1,21 @@
+import 'package:desafio_mobile/src/data/datasources/featch_bank_location_datasource.dart';
+import 'package:desafio_mobile/src/external/database/app_database.dart';
+
+import '../../domain/errors/errors.dart';
+
+class FeatchBankLocationDatasource implements IFeatchBankLocationDatasource {
+ final AppDatabase appDatabase;
+
+ FeatchBankLocationDatasource(this.appDatabase);
+
+ @override
+ Future featchLocation(String uid) async {
+ try {
+ final result = await appDatabase.localizationDAO.featchLocalization(uid);
+ print('result $result');
+ return result;
+ } catch (e, s) {
+ throw FalireException(e.toString(), s);
+ }
+ }
+}
diff --git a/lib/src/external/datasources/get_current_position_datasource.dart b/lib/src/external/datasources/get_current_position_datasource.dart
new file mode 100644
index 00000000..81836eb5
--- /dev/null
+++ b/lib/src/external/datasources/get_current_position_datasource.dart
@@ -0,0 +1,16 @@
+import 'package:desafio_mobile/src/data/datasources/get_current_position_datasource.dart';
+import 'package:geolocator/geolocator.dart';
+
+import '../location_device/localization.dart';
+
+class GetCurrentPositionDatasource implements IGetCurrentPositionDatasource {
+ final Localization localization;
+
+ GetCurrentPositionDatasource(this.localization);
+
+ @override
+ Future featchLocation() async {
+ final result = await localization.featchLocalization();
+ return result;
+ }
+}
diff --git a/lib/src/external/datasources/save_bank_location_datasource.dart b/lib/src/external/datasources/save_bank_location_datasource.dart
new file mode 100644
index 00000000..20b28563
--- /dev/null
+++ b/lib/src/external/datasources/save_bank_location_datasource.dart
@@ -0,0 +1,51 @@
+import 'package:desafio_mobile/src/data/datasources/save_bank_location_datasource.dart';
+import 'package:desafio_mobile/src/domain/errors/errors.dart';
+import 'package:desafio_mobile/src/domain/entities/location.dart';
+
+import '../database/app_database.dart';
+
+class SaveBankLocationDatasource implements ISaveBankLocationDatasource {
+ final AppDatabase appDatabase;
+
+ SaveBankLocationDatasource(this.appDatabase);
+
+ UserLocalizationData convertAddUserLocalization(Location location) {
+ return UserLocalizationData(
+ uid: location.user.uid,
+ email: location.user.email,
+ latitude: location.latitude.toString(),
+ longitude: location.longitude.toString(),
+ );
+ }
+
+ UserLocalizationData convertUpdateUserLocalization(Location location) {
+ return UserLocalizationData(
+ id: location.id,
+ uid: location.user.uid,
+ email: location.user.email,
+ latitude: location.latitude.toString(),
+ longitude: location.longitude.toString(),
+ );
+ }
+
+ @override
+ Future saveLocation(Location location) async {
+ try {
+ if (location.id == null) {
+ UserLocalizationData localization = convertAddUserLocalization(location);
+
+ final result = await appDatabase.localizationDAO.addLocalization(localization);
+
+ return result;
+ } else {
+ UserLocalizationData localization = convertUpdateUserLocalization(location);
+
+ final result = await appDatabase.localizationDAO.updateLocalization(localization);
+
+ return result;
+ }
+ } catch (e, s) {
+ throw FalireException(e.toString(), s);
+ }
+ }
+}
diff --git a/lib/src/external/location_device/localization.dart b/lib/src/external/location_device/localization.dart
new file mode 100644
index 00000000..b36ecbbb
--- /dev/null
+++ b/lib/src/external/location_device/localization.dart
@@ -0,0 +1,37 @@
+import 'package:geolocator/geolocator.dart';
+
+import '../../domain/errors/errors.dart';
+
+class Localization {
+ Future featchLocalization() async {
+ try {
+ return await _position();
+ } catch (e, s) {
+ throw FalireException(e.toString(), s);
+ }
+ }
+
+ Future _position() async {
+ LocationPermission permission;
+
+ bool ativado = await Geolocator.isLocationServiceEnabled();
+ if (!ativado) {
+ throw const FalireException('Por favor, habilite a localização no smartphone');
+ }
+
+ permission = await Geolocator.checkPermission();
+
+ if (permission == LocationPermission.denied) {
+ permission = await Geolocator.requestPermission();
+ if (permission == LocationPermission.denied) {
+ throw const FalireException('Você precisa autorizar o acesso à localização');
+ }
+ }
+
+ if (permission == LocationPermission.deniedForever) {
+ throw const FalireException('Você precisa autorizar o acesso à localização');
+ }
+
+ return await Geolocator.getCurrentPosition();
+ }
+}
diff --git a/lib/src/presentation/bloc/home/database/database_bloc.dart b/lib/src/presentation/bloc/home/database/database_bloc.dart
new file mode 100644
index 00000000..b0f701b8
--- /dev/null
+++ b/lib/src/presentation/bloc/home/database/database_bloc.dart
@@ -0,0 +1,47 @@
+import 'package:flutter_bloc/flutter_bloc.dart';
+import '../../../../domain/usecases/featch_bank_location_usecase.dart';
+import '../../../../domain/usecases/save_bank_location_usecase.dart';
+import 'database_event.dart';
+import 'database_state.dart';
+
+class DatabaseBloc extends Bloc {
+ final ISaveBankLocationUsecase saveBankLocation;
+ final IFeatchBankLocationUsecase featchBankLocation;
+
+ DatabaseBloc(this.saveBankLocation, this.featchBankLocation) : super(DatabaseInitialState()) {
+ on(_addLocation);
+ on(_featchLocation);
+ on(_intialState);
+ }
+
+ void _intialState(
+ DatabaseInitial event,
+ Emitter emitter,
+ ) {
+ emit(DatabaseInitialState());
+ }
+
+ void _addLocation(
+ AddLocation event,
+ Emitter emitter,
+ ) async {
+ emit(DatabaseLoadingState());
+ final result = await saveBankLocation(event.location);
+ result.fold(
+ (l) => emit(DatabaseFailureState(l)),
+ (r) => emit(AddDatabaseFinishedState(r)),
+ );
+ }
+
+ void _featchLocation(
+ FeatchLocation event,
+ Emitter emitter,
+ ) async {
+ emit(DatabaseLoadingState());
+ final result = await featchBankLocation(event.uid);
+ result.fold(
+ (l) => emit(DatabaseFailureState(l)),
+ (r) => emit(FeatchDatabaseFinishedState(r)),
+ );
+ }
+}
diff --git a/lib/src/presentation/bloc/home/database/database_event.dart b/lib/src/presentation/bloc/home/database/database_event.dart
new file mode 100644
index 00000000..199bf509
--- /dev/null
+++ b/lib/src/presentation/bloc/home/database/database_event.dart
@@ -0,0 +1,17 @@
+import '../../../../domain/entities/location.dart';
+
+abstract class DatabaseEvent {}
+
+class DatabaseInitial extends DatabaseEvent {}
+
+class AddLocation implements DatabaseEvent {
+ final Location location;
+
+ AddLocation(this.location);
+}
+
+class FeatchLocation implements DatabaseEvent {
+ final String uid;
+
+ FeatchLocation(this.uid);
+}
diff --git a/lib/src/presentation/bloc/home/database/database_state.dart b/lib/src/presentation/bloc/home/database/database_state.dart
new file mode 100644
index 00000000..3c92434f
--- /dev/null
+++ b/lib/src/presentation/bloc/home/database/database_state.dart
@@ -0,0 +1,26 @@
+import '../../../../domain/entities/location.dart';
+import '../../../../domain/errors/errors.dart';
+
+abstract class DatabaseState {}
+
+class DatabaseInitialState extends DatabaseState {}
+
+class DatabaseLoadingState extends DatabaseState {}
+
+class AddDatabaseFinishedState implements DatabaseState {
+ final int? id;
+
+ AddDatabaseFinishedState(this.id);
+}
+
+class FeatchDatabaseFinishedState implements DatabaseState {
+ final Location? location;
+
+ FeatchDatabaseFinishedState(this.location);
+}
+
+class DatabaseFailureState implements DatabaseState {
+ final IErrorsException errorException;
+
+ DatabaseFailureState(this.errorException);
+}
diff --git a/lib/src/presentation/bloc/home/position/position_bloc.dart b/lib/src/presentation/bloc/home/position/position_bloc.dart
new file mode 100644
index 00000000..7209eea2
--- /dev/null
+++ b/lib/src/presentation/bloc/home/position/position_bloc.dart
@@ -0,0 +1,24 @@
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import '../../../../domain/usecases/get_current_position_usecase.dart';
+import 'position_event.dart';
+import 'position_state.dart';
+
+class PositionBloc extends Bloc {
+ final IGetCurrentPositionUsecase getCurrentPositionUsecase;
+
+ PositionBloc(this.getCurrentPositionUsecase) : super(PositionLoadingState()) {
+ on(_requesPosition);
+ }
+
+ void _requesPosition(
+ RequestPosition event,
+ Emitter emitter,
+ ) async {
+ final result = await getCurrentPositionUsecase();
+ result.fold(
+ (l) => emit(PositionFailureState(l)),
+ (r) => emit(PositionFinishedState(r)),
+ );
+ }
+}
diff --git a/lib/src/presentation/bloc/home/position/position_event.dart b/lib/src/presentation/bloc/home/position/position_event.dart
new file mode 100644
index 00000000..23beca9f
--- /dev/null
+++ b/lib/src/presentation/bloc/home/position/position_event.dart
@@ -0,0 +1,3 @@
+abstract class PositionEvent {}
+
+class RequestPosition implements PositionEvent {}
diff --git a/lib/src/presentation/bloc/home/position/position_state.dart b/lib/src/presentation/bloc/home/position/position_state.dart
new file mode 100644
index 00000000..55e4ebe5
--- /dev/null
+++ b/lib/src/presentation/bloc/home/position/position_state.dart
@@ -0,0 +1,19 @@
+import 'package:google_maps_flutter/google_maps_flutter.dart';
+
+import '../../../../domain/errors/errors.dart';
+
+abstract class PositionState {}
+
+class PositionLoadingState extends PositionState {}
+
+class PositionFinishedState implements PositionState {
+ final LatLng? position;
+
+ PositionFinishedState(this.position);
+}
+
+class PositionFailureState implements PositionState {
+ final IErrorsException errorException;
+
+ PositionFailureState(this.errorException);
+}
diff --git a/lib/src/presentation/bloc/signin/signin_bloc.dart b/lib/src/presentation/bloc/signin/signin_bloc.dart
new file mode 100644
index 00000000..06bde415
--- /dev/null
+++ b/lib/src/presentation/bloc/signin/signin_bloc.dart
@@ -0,0 +1,29 @@
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import '../../../domain/usecases/auth_user_usecase.dart';
+import '../../../shared/data_struct/auth.dart';
+import 'signin_event.dart';
+import 'signin_state.dart';
+
+class SigninBloc extends Bloc {
+ final IAuthUserUsecase authUserUsecase;
+
+ SigninBloc(this.authUserUsecase) : super(SigninInitialState()) {
+ on(_onSubmitAuthForm);
+ }
+
+ void _onSubmitAuthForm(
+ SubmitAuthForm event,
+ Emitter emitter,
+ ) async {
+ emit(SigninLoadingState());
+ AuthUser data = AuthUser(email: event.username, password: event.password);
+
+ final result = await authUserUsecase(data);
+
+ result.fold(
+ (l) => emit(SigninStateAuthFailure(l)),
+ (r) => emit(SigninStateAuthFinished(r)),
+ );
+ }
+}
diff --git a/lib/src/presentation/bloc/signin/signin_event.dart b/lib/src/presentation/bloc/signin/signin_event.dart
new file mode 100644
index 00000000..474e4105
--- /dev/null
+++ b/lib/src/presentation/bloc/signin/signin_event.dart
@@ -0,0 +1,8 @@
+abstract class SigninEvent {}
+
+class SubmitAuthForm implements SigninEvent {
+ final String username;
+ final String password;
+
+ SubmitAuthForm({required this.username, required this.password});
+}
diff --git a/lib/src/presentation/bloc/signin/signin_state.dart b/lib/src/presentation/bloc/signin/signin_state.dart
new file mode 100644
index 00000000..937452b9
--- /dev/null
+++ b/lib/src/presentation/bloc/signin/signin_state.dart
@@ -0,0 +1,20 @@
+import '../../../domain/entities/user.dart';
+import '../../../domain/errors/errors.dart';
+
+abstract class SigninState {}
+
+class SigninInitialState extends SigninState {}
+
+class SigninLoadingState extends SigninState {}
+
+class SigninStateAuthFinished implements SigninState {
+ final User user;
+
+ SigninStateAuthFinished(this.user);
+}
+
+class SigninStateAuthFailure implements SigninState {
+ final IErrorsException errorException;
+
+ SigninStateAuthFailure(this.errorException);
+}
diff --git a/lib/src/presentation/pages/home_page.dart b/lib/src/presentation/pages/home_page.dart
new file mode 100644
index 00000000..92fa985f
--- /dev/null
+++ b/lib/src/presentation/pages/home_page.dart
@@ -0,0 +1,119 @@
+import 'dart:async';
+import 'package:desafio_mobile/src/domain/entities/location.dart';
+import 'package:desafio_mobile/src/presentation/bloc/home/database/database_event.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:google_maps_flutter/google_maps_flutter.dart';
+import '../../domain/entities/user.dart';
+import '../bloc/home/database/database_bloc.dart';
+import '../bloc/home/database/database_state.dart';
+import '../bloc/home/position/position_bloc.dart';
+import '../bloc/home/position/position_event.dart';
+import '../bloc/home/position/position_state.dart';
+
+class HomePage extends StatefulWidget {
+ final User user;
+ final LatLng? position;
+ const HomePage({Key? key, required this.user, this.position}) : super(key: key);
+
+ @override
+ State createState() => _HomePageState();
+}
+
+class _HomePageState extends State {
+ bool isErrorDisplay = false;
+ final Completer _controller = Completer();
+ Set markers = {};
+ LatLng intPosition = const LatLng(-2.9068567, -41.7904943);
+
+ void _onMapController(GoogleMapController controller) {
+ _controller.complete(controller);
+ }
+
+ Future _goToTheLake(LatLng? position) async {
+ if (position != null) {
+ intPosition = LatLng(position.latitude, position.latitude);
+ }
+
+ Marker marker = Marker(
+ markerId: const MarkerId('123'),
+ position: intPosition,
+ infoWindow: const InfoWindow(
+ title: 'Sua Localicação',
+ snippet: 'Desafio ByCoders',
+ ),
+ );
+
+ final GoogleMapController controller = await _controller.future;
+ controller.moveCamera(CameraUpdate.newLatLng(intPosition));
+
+ setState(() {
+ markers.add(marker);
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ var positionbloc = context.read();
+ var databasebloc = context.read();
+ return BlocListener(
+ listenWhen: (_, __) => !isErrorDisplay,
+ listener: (context, state) {
+ if (state is PositionFinishedState) {
+ _goToTheLake(state.position!);
+
+ var location = Location(
+ latitude: intPosition.latitude,
+ longitude: intPosition.longitude,
+ user: widget.user,
+ );
+ databasebloc.add(AddLocation(location));
+ }
+ },
+ bloc: positionbloc,
+ child: BlocBuilder(
+ bloc: databasebloc,
+ builder: (context, state) {
+ if (state is FeatchDatabaseFinishedState) {
+ var positionBank = LatLng(
+ state.location!.latitude,
+ state.location!.longitude,
+ );
+ _goToTheLake(positionBank);
+ databasebloc.add(DatabaseInitial());
+ }
+ return Scaffold(
+ appBar: AppBar(
+ centerTitle: true,
+ title: const Text('Bem vindo a Map'),
+ leading: Container(),
+ actions: [
+ IconButton(
+ onPressed: () {
+ databasebloc.add(FeatchLocation(widget.user.uid));
+ },
+ tooltip: 'Localização no Ultimo Login',
+ icon: const Icon(Icons.mode_of_travel_outlined),
+ ),
+ IconButton(
+ onPressed: () {
+ positionbloc.add(RequestPosition());
+ },
+ tooltip: 'Localização Atual',
+ icon: const Icon(Icons.my_location_outlined),
+ ),
+ ],
+ ),
+ body: GoogleMap(
+ onMapCreated: _onMapController,
+ markers: markers,
+ initialCameraPosition: CameraPosition(
+ target: intPosition,
+ zoom: 11.0,
+ ),
+ ),
+ );
+ }),
+ );
+ }
+}
diff --git a/lib/src/presentation/pages/signin_page.dart b/lib/src/presentation/pages/signin_page.dart
new file mode 100644
index 00000000..4ecdaa79
--- /dev/null
+++ b/lib/src/presentation/pages/signin_page.dart
@@ -0,0 +1,47 @@
+import 'package:flutter/material.dart';
+import '../widgets/signin_form.dart';
+
+class SigninPage extends StatefulWidget {
+ const SigninPage({
+ Key? key,
+ }) : super(key: key);
+
+ @override
+ State createState() => _SigninPageState();
+}
+
+class _SigninPageState extends State {
+ @override
+ Widget build(BuildContext context) {
+ Size size = MediaQuery.of(context).size;
+ return Scaffold(
+ body: Padding(
+ padding: const EdgeInsets.all(20),
+ child: SingleChildScrollView(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const SizedBox(height: 100),
+ ClipRRect(
+ borderRadius: BorderRadius.circular(10),
+ child: Image.asset(
+ 'assets/images/signin.png',
+ width: 200,
+ ),
+ ),
+ const SizedBox(height: 35),
+ const SigninForm(),
+ const SizedBox(height: 20),
+ SizedBox(
+ width: size.width * 0.03,
+ ),
+ SizedBox(
+ height: size.height * 0.04,
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/src/presentation/widgets/custom_button.dart b/lib/src/presentation/widgets/custom_button.dart
new file mode 100644
index 00000000..ffacc9dc
--- /dev/null
+++ b/lib/src/presentation/widgets/custom_button.dart
@@ -0,0 +1,107 @@
+import 'package:flutter/material.dart';
+
+class CustomButton extends StatelessWidget {
+ final Text? text;
+ final Text? subtext;
+ final Widget? leadingIcon;
+ final Widget? trailingIcon;
+ final bool alignIconEdge;
+ final EdgeInsets? padding;
+ final EdgeInsets? margin;
+ final int colorCode;
+ final double? width;
+ final double? height;
+ final double? borderRadius;
+ final void Function()? onPressed;
+ final double? elevation;
+
+ const CustomButton({
+ Key? key,
+ this.text,
+ this.subtext,
+ this.leadingIcon,
+ this.trailingIcon,
+ this.padding,
+ this.margin,
+ this.colorCode = 0XFF0067FF,
+ this.width,
+ this.height,
+ this.borderRadius,
+ this.onPressed,
+ this.alignIconEdge = false,
+ this.elevation,
+ }) : assert(
+ text != null || leadingIcon != null,
+ 'Must have text or leadingIcon',
+ ),
+ assert(
+ text != null || subtext == null,
+ 'Must have text to have subtext',
+ ),
+ super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ final iconOnly = text == null;
+
+ return Container(
+ width: width,
+ height: height,
+ margin: margin,
+ child: ElevatedButton(
+ style: ElevatedButton.styleFrom(
+ elevation: elevation,
+ primary: Color(colorCode),
+ shape: iconOnly
+ ? const CircleBorder()
+ : RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(borderRadius ?? 16),
+ ),
+ ),
+ onPressed: onPressed,
+ child: Padding(
+ padding: padding ?? const EdgeInsets.all(24.0),
+ child: _buildButtonContent(),
+ ),
+ ),
+ );
+ }
+
+ Widget _buildButtonContent() {
+ final textContent = Column(
+ mainAxisSize: MainAxisSize.min,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ if (text != null) text!,
+ if (subtext != null) subtext!,
+ ],
+ );
+
+ return Row(
+ mainAxisAlignment: _getIconsAlignment(),
+ children: [
+ if (leadingIcon != null) leadingIcon!,
+ if (leadingIcon != null && text != null) SizedBox(width: alignIconEdge ? 4 : 8),
+ if (text != null)
+ alignIconEdge
+ ? Expanded(
+ child: textContent,
+ )
+ : textContent,
+ if (trailingIcon != null && text != null) SizedBox(width: alignIconEdge ? 4 : 8),
+ if (trailingIcon != null) trailingIcon!,
+ ],
+ );
+ }
+
+ MainAxisAlignment _getIconsAlignment() {
+ final leadingIconOnly = leadingIcon != null && trailingIcon == null;
+ final trailingIconOnly = leadingIcon == null && trailingIcon != null;
+
+ return alignIconEdge && leadingIconOnly
+ ? MainAxisAlignment.start
+ : alignIconEdge && trailingIconOnly
+ ? MainAxisAlignment.end
+ : MainAxisAlignment.center;
+ }
+}
diff --git a/lib/src/presentation/widgets/custom_form.dart b/lib/src/presentation/widgets/custom_form.dart
new file mode 100644
index 00000000..4228bb53
--- /dev/null
+++ b/lib/src/presentation/widgets/custom_form.dart
@@ -0,0 +1,43 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_form_builder/flutter_form_builder.dart';
+
+class CustomForm extends StatefulWidget {
+ final Function(BuildContext, Map Function()) builder;
+ final AutovalidateMode autovalidateMode;
+
+ const CustomForm({
+ Key? key,
+ required this.builder,
+ required this.autovalidateMode,
+ }) : super(key: key);
+
+ @override
+ State createState() => _CustomFormState();
+}
+
+class _CustomFormState extends State {
+ final _formKey = GlobalKey();
+
+ Map getValidatedFormState() {
+ bool isValid = _formKey.currentState?.saveAndValidate() ?? false;
+ var formFields = _formKey.currentState?.fields ?? {};
+
+ Map mappedFormFields = {'isValid': isValid};
+
+ formFields.forEach((key, value) {
+ mappedFormFields[key] = value.value;
+ });
+
+ return mappedFormFields;
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return FormBuilder(
+ key: _formKey,
+ autovalidateMode: widget.autovalidateMode,
+ child: widget.builder(context, getValidatedFormState),
+ );
+ }
+}
diff --git a/lib/src/presentation/widgets/custom_form_text_field.dart b/lib/src/presentation/widgets/custom_form_text_field.dart
new file mode 100644
index 00000000..eb9f1fa3
--- /dev/null
+++ b/lib/src/presentation/widgets/custom_form_text_field.dart
@@ -0,0 +1,211 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_form_builder/flutter_form_builder.dart';
+import 'package:form_builder_validators/form_builder_validators.dart';
+
+part 'subwidgets/error_text_widget.dart';
+part 'subwidgets/helper_text_widget.dart';
+
+class CustomFormTextField extends StatefulWidget {
+ final String name;
+ final String label;
+ final String hint;
+ final bool enabled;
+ final Widget? prefixIcon;
+ final Widget? suffixIcon;
+ final String? helperText;
+ final bool obscureText;
+ final TextInputType? keyboardType;
+ final List? inputFormatters;
+ final String? initialValue;
+ final TextEditingController? controller;
+ final void Function(String?)? onChanged;
+ final List> validators;
+ final AutovalidateMode autovalidateMode;
+ final TextCapitalization textCapitalization;
+
+ const CustomFormTextField({
+ Key? key,
+ required this.name,
+ required this.label,
+ required this.hint,
+ this.enabled = true,
+ this.prefixIcon,
+ this.suffixIcon,
+ this.helperText,
+ this.obscureText = false,
+ this.keyboardType,
+ this.inputFormatters,
+ this.initialValue,
+ this.controller,
+ this.onChanged,
+ this.validators = const [],
+ this.autovalidateMode = AutovalidateMode.disabled,
+ this.textCapitalization = TextCapitalization.none,
+ }) : assert(initialValue == null || controller == null),
+ super(key: key);
+
+ @override
+ State createState() => _CustomFormTextFieldState();
+}
+
+class _CustomFormTextFieldState extends State {
+ String? _errorText;
+ late final TextEditingController _controller;
+ final FocusNode _focusNode = FocusNode();
+ bool _hasInteractedByUser = false;
+ double _borderWidth = CustomFormTextFieldStyle.unfocusedBorderWidth;
+
+ @override
+ void initState() {
+ super.initState();
+ _initController();
+ _focusNode.addListener(_onFocusChaged);
+ }
+
+ @override
+ void dispose() {
+ if (widget.controller == null) _controller.dispose();
+ _focusNode.removeListener(_onFocusChaged);
+ _focusNode.dispose();
+ super.dispose();
+ }
+
+ void _initController() {
+ _controller = widget.controller ?? TextEditingController(text: widget.initialValue);
+ }
+
+ void _onFocusChaged() {
+ setState(() {
+ _updateBorderWidth();
+ });
+ }
+
+ void _updateBorderWidth() {
+ _borderWidth = _focusNode.hasFocus
+ ? CustomFormTextFieldStyle.focusedBorderWidth
+ : CustomFormTextFieldStyle.unfocusedBorderWidth;
+ }
+
+ void _validate() {
+ final validators = widget.validators;
+ for (final validator in validators) {
+ _errorText = validator(_controller.text);
+ if (_errorText != null) break;
+ }
+ }
+
+ void _onChanged(String? text) {
+ setState(() {
+ _hasInteractedByUser = true;
+ });
+ widget.onChanged?.call(text);
+ }
+
+ void _onSaved(String? text) {
+ setState(() {
+ _validate();
+ });
+ }
+
+ Color get _borderColor {
+ final colorCode = _errorText != null
+ ? CustomFormTextFieldStyle.errorColor
+ : _focusNode.hasFocus
+ ? CustomFormTextFieldStyle.focusedColor
+ : CustomFormTextFieldStyle.defaultBorderColor;
+ return Color(colorCode);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ if (widget.enabled) {
+ switch (widget.autovalidateMode) {
+ case AutovalidateMode.always:
+ _validate();
+ break;
+ case AutovalidateMode.onUserInteraction:
+ if (_hasInteractedByUser) _validate();
+ break;
+ case AutovalidateMode.disabled:
+ break;
+ }
+ }
+ final borderInternalPadding =
+ CustomFormTextFieldStyle.maxBorderInternalSpaceUsed - _borderWidth;
+ final themeData = Theme.of(context);
+ return Theme(
+ data: themeData.copyWith(
+ colorScheme: themeData.colorScheme.copyWith(
+ primary: const Color(CustomFormTextFieldStyle.focusedColor),
+ ),
+ ),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Container(
+ height: 56,
+ padding: EdgeInsets.all(borderInternalPadding),
+ decoration: BoxDecoration(
+ borderRadius: const BorderRadius.all(Radius.circular(12)),
+ border: Border.all(
+ color: _borderColor,
+ width: _borderWidth,
+ ),
+ ),
+ child: FormBuilderTextField(
+ name: widget.name,
+ enabled: widget.enabled,
+ obscureText: widget.obscureText,
+ keyboardType: widget.keyboardType,
+ inputFormatters: widget.inputFormatters,
+ controller: _controller,
+ onChanged: _onChanged,
+ focusNode: _focusNode,
+ decoration: InputDecoration(
+ labelText: widget.label,
+ hintText: widget.hint,
+ prefixIcon: widget.prefixIcon,
+ suffixIcon: widget.suffixIcon,
+ labelStyle: const TextStyle(
+ color: Color(CustomFormTextFieldStyle.labelColor),
+ ),
+ contentPadding: const EdgeInsets.symmetric(
+ vertical: 8,
+ horizontal: 16,
+ ),
+ enabledBorder: InputBorder.none,
+ errorBorder: InputBorder.none,
+ border: InputBorder.none,
+ focusedErrorBorder: InputBorder.none,
+ disabledBorder: InputBorder.none,
+ focusedBorder: InputBorder.none,
+ errorStyle: const TextStyle(height: 0, fontSize: 0),
+ ),
+ validator: FormBuilderValidators.compose(widget.validators),
+ onSaved: _onSaved,
+ textCapitalization: widget.textCapitalization,
+ ),
+ ),
+ if (_errorText != null)
+ _ErrorTextWidget(errorText: _errorText!)
+ else
+ _HelperTextWidget(helperText: widget.helperText)
+ ],
+ ),
+ );
+ }
+}
+
+class CustomFormTextFieldStyle {
+ CustomFormTextFieldStyle._();
+
+ static const defaultBorderColor = 0xFFC6C7CD;
+ static const focusedColor = 0xFF2047E0;
+ static const errorColor = 0xFFEB0433;
+ static const labelColor = 0xFF57595F;
+ static const helperTextColor = 0xFF1D1E22;
+ static const double focusedBorderWidth = 2;
+ static const double unfocusedBorderWidth = 1;
+ static const double maxBorderInternalSpaceUsed = 2;
+}
diff --git a/lib/src/presentation/widgets/custom_form_text_field_email.dart b/lib/src/presentation/widgets/custom_form_text_field_email.dart
new file mode 100644
index 00000000..db1fd62b
--- /dev/null
+++ b/lib/src/presentation/widgets/custom_form_text_field_email.dart
@@ -0,0 +1,60 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:form_builder_validators/form_builder_validators.dart';
+import 'custom_form_text_field.dart';
+
+class CustomFormTextFieldEmail extends StatelessWidget {
+ final String name;
+ final String label;
+ final String hint;
+ final bool enabled;
+ final String? helperText;
+ final String? initialValue;
+ final TextEditingController? controller;
+ final void Function(String?)? onChanged;
+ final List> validators;
+ final AutovalidateMode autovalidateMode;
+ final bool hasPrefixIcon;
+
+ const CustomFormTextFieldEmail({
+ Key? key,
+ required this.name,
+ required this.label,
+ required this.hint,
+ this.enabled = true,
+ this.helperText,
+ this.initialValue,
+ this.controller,
+ this.onChanged,
+ this.validators = const [],
+ this.autovalidateMode = AutovalidateMode.disabled,
+ this.hasPrefixIcon = false,
+ }) : assert(initialValue == null || controller == null),
+ super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return CustomFormTextField(
+ name: name,
+ label: label,
+ hint: hint,
+ enabled: enabled,
+ prefixIcon: hasPrefixIcon ? const Icon(Icons.alternate_email) : null,
+ helperText: helperText,
+ keyboardType: TextInputType.emailAddress,
+ inputFormatters: [
+ FilteringTextInputFormatter.deny(RegExp(r'\s')),
+ ],
+ initialValue: initialValue,
+ controller: controller,
+ onChanged: onChanged,
+ validators: [
+ ...validators,
+ FormBuilderValidators.email(
+ errorText: 'Email inválido',
+ ),
+ ],
+ autovalidateMode: autovalidateMode,
+ );
+ }
+}
diff --git a/lib/src/presentation/widgets/custom_form_text_field_password.dart b/lib/src/presentation/widgets/custom_form_text_field_password.dart
new file mode 100644
index 00000000..a280e895
--- /dev/null
+++ b/lib/src/presentation/widgets/custom_form_text_field_password.dart
@@ -0,0 +1,80 @@
+import 'package:flutter/material.dart';
+import 'package:form_builder_validators/form_builder_validators.dart';
+
+import 'custom_form_text_field.dart';
+
+class CustomFormTextFieldPassword extends StatefulWidget {
+ final String name;
+ final String label;
+ final String hint;
+ final bool enabled;
+ final String? helperText;
+ final String? initialValue;
+ final TextEditingController? controller;
+ final void Function(String?)? onChanged;
+ final List> validators;
+ final AutovalidateMode autovalidateMode;
+ final bool hasPrefixIcon;
+ final bool hasShowHidePasswordIcon;
+
+ const CustomFormTextFieldPassword({
+ Key? key,
+ required this.name,
+ required this.label,
+ required this.hint,
+ this.enabled = true,
+ this.helperText,
+ this.initialValue,
+ this.controller,
+ this.onChanged,
+ this.validators = const [],
+ this.autovalidateMode = AutovalidateMode.disabled,
+ this.hasPrefixIcon = false,
+ this.hasShowHidePasswordIcon = true,
+ }) : assert(initialValue == null || controller == null),
+ super(key: key);
+
+ @override
+ State createState() => _CustomFormTextFieldPasswordState();
+}
+
+class _CustomFormTextFieldPasswordState extends State {
+ bool _obscureText = true;
+ @override
+ Widget build(BuildContext context) {
+ return CustomFormTextField(
+ name: widget.name,
+ label: widget.label,
+ hint: widget.hint,
+ enabled: widget.enabled,
+ prefixIcon: widget.hasPrefixIcon
+ ? const Icon(
+ IconData(
+ 61572,
+ fontFamily: 'FontAwesomeSolid',
+ fontPackage: 'font_awesome_flutter',
+ ),
+ )
+ : null,
+ suffixIcon: widget.hasShowHidePasswordIcon
+ ? IconButton(
+ icon: Icon(
+ _obscureText ? Icons.visibility : Icons.visibility_off,
+ ),
+ onPressed: () => setState(() => _obscureText = !_obscureText),
+ )
+ : null,
+ helperText: widget.helperText,
+ obscureText: _obscureText,
+ onChanged: widget.onChanged,
+ validators: [
+ ...widget.validators,
+ FormBuilderValidators.minLength(
+ 6,
+ errorText: 'Deve conter no mínimo 6 caracteres',
+ ),
+ ],
+ autovalidateMode: widget.autovalidateMode,
+ );
+ }
+}
diff --git a/lib/src/presentation/widgets/signin_form.dart b/lib/src/presentation/widgets/signin_form.dart
new file mode 100644
index 00000000..ccd2d653
--- /dev/null
+++ b/lib/src/presentation/widgets/signin_form.dart
@@ -0,0 +1,129 @@
+import 'package:desafio_mobile/src/presentation/bloc/home/position/position_state.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:form_builder_validators/form_builder_validators.dart';
+import '../../shared/data_struct/auth_user_state.dart';
+import '../bloc/home/position/position_bloc.dart';
+import '../bloc/home/position/position_event.dart';
+import '../bloc/signin/signin_bloc.dart';
+import '../bloc/signin/signin_event.dart';
+import '../bloc/signin/signin_state.dart';
+import 'custom_form.dart';
+import 'custom_button.dart';
+import 'custom_form_text_field_email.dart';
+import 'custom_form_text_field_password.dart';
+
+class SigninForm extends StatefulWidget {
+ final String? email;
+ final String? password;
+
+ const SigninForm({
+ Key? key,
+ this.email,
+ this.password,
+ }) : super(key: key);
+
+ @override
+ State createState() => _SigninFormState();
+}
+
+class _SigninFormState extends State {
+ bool isvalid = false;
+ @override
+ Widget build(BuildContext context) {
+ var bloc = context.read();
+ var positionbloc = context.read();
+ return BlocBuilder(
+ bloc: positionbloc,
+ builder: (context, positionState) {
+ return BlocBuilder(
+ bloc: bloc,
+ builder: (context, state) {
+ if (state is SigninStateAuthFinished) {
+ positionbloc.add(RequestPosition());
+ if (positionState is PositionFinishedState) {
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ Navigator.pushReplacementNamed(context, '/home',
+ arguments: [state.user, positionState.position]);
+ });
+ }
+ return const SizedBox(
+ height: 300, child: Center(child: CircularProgressIndicator()));
+ }
+ return CustomForm(
+ autovalidateMode: AutovalidateMode.disabled,
+ builder: (
+ BuildContext context,
+ Map Function() getValidatedFormState,
+ ) =>
+ Column(
+ children: [
+ CustomFormTextFieldEmail(
+ name: 'email',
+ label: 'E-mail',
+ hint: 'E-mail',
+ initialValue: widget.email,
+ onChanged: (_) {
+ setState(() => isvalid = getValidatedFormState()['isValid']);
+ },
+ validators: [
+ FormBuilderValidators.required(
+ errorText: 'Digite seu e-mail',
+ )
+ ],
+ ),
+ const SizedBox(height: 15),
+ CustomFormTextFieldPassword(
+ name: 'password',
+ label: 'Senha',
+ hint: 'Senha',
+ onChanged: (_) {
+ setState(() => isvalid = getValidatedFormState()['isValid']);
+ },
+ ),
+ const SizedBox(height: 24),
+ if (state is SigninLoadingState)
+ const CircularProgressIndicator()
+ else
+ CustomButton(
+ text: const Text(
+ 'Entrar',
+ style: TextStyle(
+ color: Color(0XFFEBF7FF),
+ fontSize: 16,
+ fontWeight: FontWeight.w800,
+ ),
+ ),
+ colorCode: 0xFF2047E0,
+ padding: const EdgeInsets.all(20),
+ width: MediaQuery.of(context).size.width,
+ borderRadius: 16,
+ onPressed: () async {
+ final signinForm = AuthUserForm.fromFormFields(
+ getValidatedFormState(),
+ );
+ if (isvalid) {
+ bloc.add(
+ SubmitAuthForm(
+ username: signinForm.username!,
+ password: signinForm.password!,
+ ),
+ );
+ }
+ },
+ ),
+ const SizedBox(height: 15),
+ if (state is SigninStateAuthFailure)
+ Text(
+ state.errorException.message,
+ textAlign: TextAlign.center,
+ style: const TextStyle(color: Colors.red, fontSize: 16),
+ )
+ ],
+ ),
+ );
+ },
+ );
+ });
+ }
+}
diff --git a/lib/src/presentation/widgets/subwidgets/error_text_widget.dart b/lib/src/presentation/widgets/subwidgets/error_text_widget.dart
new file mode 100644
index 00000000..98bb5258
--- /dev/null
+++ b/lib/src/presentation/widgets/subwidgets/error_text_widget.dart
@@ -0,0 +1,23 @@
+part of '../custom_form_text_field.dart';
+
+class _ErrorTextWidget extends StatelessWidget {
+ final String errorText;
+ const _ErrorTextWidget({
+ Key? key,
+ required this.errorText,
+ }) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ margin: const EdgeInsets.only(left: 16.0, top: 8.0, bottom: 8.0),
+ child: Text(
+ errorText,
+ style: const TextStyle(
+ fontSize: 12,
+ color: Colors.red,
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/src/presentation/widgets/subwidgets/helper_text_widget.dart b/lib/src/presentation/widgets/subwidgets/helper_text_widget.dart
new file mode 100644
index 00000000..b2293615
--- /dev/null
+++ b/lib/src/presentation/widgets/subwidgets/helper_text_widget.dart
@@ -0,0 +1,23 @@
+part of '../custom_form_text_field.dart';
+
+class _HelperTextWidget extends StatelessWidget {
+ final String? helperText;
+ const _HelperTextWidget({
+ Key? key,
+ required this.helperText,
+ }) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ margin: const EdgeInsets.only(left: 16.0, top: 8.0),
+ child: Text(
+ helperText ?? '',
+ style: const TextStyle(
+ fontSize: 12,
+ color: Color(CustomFormTextFieldStyle.helperTextColor),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/src/shared/data_struct/auth.dart b/lib/src/shared/data_struct/auth.dart
new file mode 100644
index 00000000..b3ecf2a7
--- /dev/null
+++ b/lib/src/shared/data_struct/auth.dart
@@ -0,0 +1,6 @@
+class AuthUser {
+ final String email;
+ final String password;
+
+ AuthUser({required this.email, required this.password});
+}
diff --git a/lib/src/shared/data_struct/auth_user_state.dart b/lib/src/shared/data_struct/auth_user_state.dart
new file mode 100644
index 00000000..ab1c869a
--- /dev/null
+++ b/lib/src/shared/data_struct/auth_user_state.dart
@@ -0,0 +1,16 @@
+class AuthUserForm {
+ final bool isValid;
+ final String? username;
+ final String? password;
+
+ AuthUserForm({
+ required this.isValid,
+ this.username,
+ this.password,
+ });
+
+ AuthUserForm.fromFormFields(Map fields)
+ : isValid = fields['isValid'],
+ username = fields['email'],
+ password = fields['password'];
+}
diff --git a/linux/.gitignore b/linux/.gitignore
new file mode 100644
index 00000000..d3896c98
--- /dev/null
+++ b/linux/.gitignore
@@ -0,0 +1 @@
+flutter/ephemeral
diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt
new file mode 100644
index 00000000..eed1ec91
--- /dev/null
+++ b/linux/CMakeLists.txt
@@ -0,0 +1,138 @@
+# Project-level configuration.
+cmake_minimum_required(VERSION 3.10)
+project(runner LANGUAGES CXX)
+
+# The name of the executable created for the application. Change this to change
+# the on-disk name of your application.
+set(BINARY_NAME "desafio_mobile")
+# The unique GTK application identifier for this application. See:
+# https://wiki.gnome.org/HowDoI/ChooseApplicationID
+set(APPLICATION_ID "com.gmail.jaksonpaulino65.desafio_mobile")
+
+# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
+# versions of CMake.
+cmake_policy(SET CMP0063 NEW)
+
+# Load bundled libraries from the lib/ directory relative to the binary.
+set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
+
+# Root filesystem for cross-building.
+if(FLUTTER_TARGET_PLATFORM_SYSROOT)
+ set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
+ set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
+ set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
+ set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+endif()
+
+# Define build configuration options.
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE
+ STRING "Flutter build mode" FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+ "Debug" "Profile" "Release")
+endif()
+
+# Compilation settings that should be applied to most targets.
+#
+# Be cautious about adding new options here, as plugins use this function by
+# default. In most cases, you should add new options to specific targets instead
+# of modifying this function.
+function(APPLY_STANDARD_SETTINGS TARGET)
+ target_compile_features(${TARGET} PUBLIC cxx_std_14)
+ target_compile_options(${TARGET} PRIVATE -Wall -Werror)
+ target_compile_options(${TARGET} PRIVATE "$<$>:-O3>")
+ target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>")
+endfunction()
+
+# Flutter library and tool build rules.
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+
+add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
+
+# Define the application target. To change its name, change BINARY_NAME above,
+# not the value here, or `flutter run` will no longer work.
+#
+# Any new source files that you add to the application should be added here.
+add_executable(${BINARY_NAME}
+ "main.cc"
+ "my_application.cc"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+)
+
+# Apply the standard set of build settings. This can be removed for applications
+# that need different build settings.
+apply_standard_settings(${BINARY_NAME})
+
+# Add dependency libraries. Add any application-specific dependencies here.
+target_link_libraries(${BINARY_NAME} PRIVATE flutter)
+target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
+
+# Run the Flutter tool portions of the build. This must not be removed.
+add_dependencies(${BINARY_NAME} flutter_assemble)
+
+# Only the install-generated bundle's copy of the executable will launch
+# correctly, since the resources must in the right relative locations. To avoid
+# people trying to run the unbundled copy, put it in a subdirectory instead of
+# the default top-level location.
+set_target_properties(${BINARY_NAME}
+ PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
+)
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# By default, "installing" just makes a relocatable bundle in the build
+# directory.
+set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+# Start with a clean build bundle directory every time.
+install(CODE "
+ file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
+ " COMPONENT Runtime)
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
+ install(FILES "${bundled_library}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endforeach(bundled_library)
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+ file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+ " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+ DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
+ install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
diff --git a/linux/flutter/CMakeLists.txt b/linux/flutter/CMakeLists.txt
new file mode 100644
index 00000000..d5bd0164
--- /dev/null
+++ b/linux/flutter/CMakeLists.txt
@@ -0,0 +1,88 @@
+# This file controls Flutter-level build steps. It should not be edited.
+cmake_minimum_required(VERSION 3.10)
+
+set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
+
+# Configuration provided via flutter tool.
+include(${EPHEMERAL_DIR}/generated_config.cmake)
+
+# TODO: Move the rest of this into files in ephemeral. See
+# https://github.com/flutter/flutter/issues/57146.
+
+# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
+# which isn't available in 3.10.
+function(list_prepend LIST_NAME PREFIX)
+ set(NEW_LIST "")
+ foreach(element ${${LIST_NAME}})
+ list(APPEND NEW_LIST "${PREFIX}${element}")
+ endforeach(element)
+ set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
+endfunction()
+
+# === Flutter Library ===
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
+pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
+
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
+
+# Published to parent scope for install step.
+set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
+set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
+set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
+set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+ "fl_basic_message_channel.h"
+ "fl_binary_codec.h"
+ "fl_binary_messenger.h"
+ "fl_dart_project.h"
+ "fl_engine.h"
+ "fl_json_message_codec.h"
+ "fl_json_method_codec.h"
+ "fl_message_codec.h"
+ "fl_method_call.h"
+ "fl_method_channel.h"
+ "fl_method_codec.h"
+ "fl_method_response.h"
+ "fl_plugin_registrar.h"
+ "fl_plugin_registry.h"
+ "fl_standard_message_codec.h"
+ "fl_standard_method_codec.h"
+ "fl_string_codec.h"
+ "fl_value.h"
+ "fl_view.h"
+ "flutter_linux.h"
+)
+list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+ "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
+target_link_libraries(flutter INTERFACE
+ PkgConfig::GTK
+ PkgConfig::GLIB
+ PkgConfig::GIO
+)
+add_dependencies(flutter flutter_assemble)
+
+# === Flutter tool backend ===
+# _phony_ is a non-existent file to force this command to run every time,
+# since currently there's no way to get a full input/output list from the
+# flutter tool.
+add_custom_command(
+ OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+ ${CMAKE_CURRENT_BINARY_DIR}/_phony_
+ COMMAND ${CMAKE_COMMAND} -E env
+ ${FLUTTER_TOOL_ENVIRONMENT}
+ "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
+ ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
+ VERBATIM
+)
+add_custom_target(flutter_assemble DEPENDS
+ "${FLUTTER_LIBRARY}"
+ ${FLUTTER_LIBRARY_HEADERS}
+)
diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc
new file mode 100644
index 00000000..e71a16d2
--- /dev/null
+++ b/linux/flutter/generated_plugin_registrant.cc
@@ -0,0 +1,11 @@
+//
+// Generated file. Do not edit.
+//
+
+// clang-format off
+
+#include "generated_plugin_registrant.h"
+
+
+void fl_register_plugins(FlPluginRegistry* registry) {
+}
diff --git a/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h
new file mode 100644
index 00000000..e0f0a47b
--- /dev/null
+++ b/linux/flutter/generated_plugin_registrant.h
@@ -0,0 +1,15 @@
+//
+// Generated file. Do not edit.
+//
+
+// clang-format off
+
+#ifndef GENERATED_PLUGIN_REGISTRANT_
+#define GENERATED_PLUGIN_REGISTRANT_
+
+#include
+
+// Registers Flutter plugins.
+void fl_register_plugins(FlPluginRegistry* registry);
+
+#endif // GENERATED_PLUGIN_REGISTRANT_
diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake
new file mode 100644
index 00000000..2e1de87a
--- /dev/null
+++ b/linux/flutter/generated_plugins.cmake
@@ -0,0 +1,23 @@
+#
+# Generated file, do not edit.
+#
+
+list(APPEND FLUTTER_PLUGIN_LIST
+)
+
+list(APPEND FLUTTER_FFI_PLUGIN_LIST
+)
+
+set(PLUGIN_BUNDLED_LIBRARIES)
+
+foreach(plugin ${FLUTTER_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
+ target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
+endforeach(plugin)
+
+foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
+endforeach(ffi_plugin)
diff --git a/linux/main.cc b/linux/main.cc
new file mode 100644
index 00000000..e7c5c543
--- /dev/null
+++ b/linux/main.cc
@@ -0,0 +1,6 @@
+#include "my_application.h"
+
+int main(int argc, char** argv) {
+ g_autoptr(MyApplication) app = my_application_new();
+ return g_application_run(G_APPLICATION(app), argc, argv);
+}
diff --git a/linux/my_application.cc b/linux/my_application.cc
new file mode 100644
index 00000000..885b93a8
--- /dev/null
+++ b/linux/my_application.cc
@@ -0,0 +1,104 @@
+#include "my_application.h"
+
+#include
+#ifdef GDK_WINDOWING_X11
+#include
+#endif
+
+#include "flutter/generated_plugin_registrant.h"
+
+struct _MyApplication {
+ GtkApplication parent_instance;
+ char** dart_entrypoint_arguments;
+};
+
+G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
+
+// Implements GApplication::activate.
+static void my_application_activate(GApplication* application) {
+ MyApplication* self = MY_APPLICATION(application);
+ GtkWindow* window =
+ GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
+
+ // Use a header bar when running in GNOME as this is the common style used
+ // by applications and is the setup most users will be using (e.g. Ubuntu
+ // desktop).
+ // If running on X and not using GNOME then just use a traditional title bar
+ // in case the window manager does more exotic layout, e.g. tiling.
+ // If running on Wayland assume the header bar will work (may need changing
+ // if future cases occur).
+ gboolean use_header_bar = TRUE;
+#ifdef GDK_WINDOWING_X11
+ GdkScreen* screen = gtk_window_get_screen(window);
+ if (GDK_IS_X11_SCREEN(screen)) {
+ const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
+ if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
+ use_header_bar = FALSE;
+ }
+ }
+#endif
+ if (use_header_bar) {
+ GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
+ gtk_widget_show(GTK_WIDGET(header_bar));
+ gtk_header_bar_set_title(header_bar, "desafio_mobile");
+ gtk_header_bar_set_show_close_button(header_bar, TRUE);
+ gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
+ } else {
+ gtk_window_set_title(window, "desafio_mobile");
+ }
+
+ gtk_window_set_default_size(window, 1280, 720);
+ gtk_widget_show(GTK_WIDGET(window));
+
+ g_autoptr(FlDartProject) project = fl_dart_project_new();
+ fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
+
+ FlView* view = fl_view_new(project);
+ gtk_widget_show(GTK_WIDGET(view));
+ gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
+
+ fl_register_plugins(FL_PLUGIN_REGISTRY(view));
+
+ gtk_widget_grab_focus(GTK_WIDGET(view));
+}
+
+// Implements GApplication::local_command_line.
+static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {
+ MyApplication* self = MY_APPLICATION(application);
+ // Strip out the first argument as it is the binary name.
+ self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
+
+ g_autoptr(GError) error = nullptr;
+ if (!g_application_register(application, nullptr, &error)) {
+ g_warning("Failed to register: %s", error->message);
+ *exit_status = 1;
+ return TRUE;
+ }
+
+ g_application_activate(application);
+ *exit_status = 0;
+
+ return TRUE;
+}
+
+// Implements GObject::dispose.
+static void my_application_dispose(GObject* object) {
+ MyApplication* self = MY_APPLICATION(object);
+ g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
+ G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
+}
+
+static void my_application_class_init(MyApplicationClass* klass) {
+ G_APPLICATION_CLASS(klass)->activate = my_application_activate;
+ G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
+ G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
+}
+
+static void my_application_init(MyApplication* self) {}
+
+MyApplication* my_application_new() {
+ return MY_APPLICATION(g_object_new(my_application_get_type(),
+ "application-id", APPLICATION_ID,
+ "flags", G_APPLICATION_NON_UNIQUE,
+ nullptr));
+}
diff --git a/linux/my_application.h b/linux/my_application.h
new file mode 100644
index 00000000..72271d5e
--- /dev/null
+++ b/linux/my_application.h
@@ -0,0 +1,18 @@
+#ifndef FLUTTER_MY_APPLICATION_H_
+#define FLUTTER_MY_APPLICATION_H_
+
+#include
+
+G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
+ GtkApplication)
+
+/**
+ * my_application_new:
+ *
+ * Creates a new Flutter-based application.
+ *
+ * Returns: a new #MyApplication.
+ */
+MyApplication* my_application_new();
+
+#endif // FLUTTER_MY_APPLICATION_H_
diff --git a/macos/.gitignore b/macos/.gitignore
new file mode 100644
index 00000000..746adbb6
--- /dev/null
+++ b/macos/.gitignore
@@ -0,0 +1,7 @@
+# Flutter-related
+**/Flutter/ephemeral/
+**/Pods/
+
+# Xcode-related
+**/dgph
+**/xcuserdata/
diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig
new file mode 100644
index 00000000..c2efd0b6
--- /dev/null
+++ b/macos/Flutter/Flutter-Debug.xcconfig
@@ -0,0 +1 @@
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig
new file mode 100644
index 00000000..c2efd0b6
--- /dev/null
+++ b/macos/Flutter/Flutter-Release.xcconfig
@@ -0,0 +1 @@
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift
new file mode 100644
index 00000000..1d6f170d
--- /dev/null
+++ b/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -0,0 +1,24 @@
+//
+// Generated file. Do not edit.
+//
+
+import FlutterMacOS
+import Foundation
+
+import cloud_firestore
+import firebase_analytics
+import firebase_auth
+import firebase_core
+import firebase_crashlytics
+import geolocator_apple
+import sqflite
+
+func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
+ FLTFirebaseFirestorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseFirestorePlugin"))
+ FLTFirebaseAnalyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAnalyticsPlugin"))
+ FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
+ FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
+ FLTFirebaseCrashlyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCrashlyticsPlugin"))
+ GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
+ SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
+}
diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..48c57053
--- /dev/null
+++ b/macos/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,572 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 51;
+ objects = {
+
+/* Begin PBXAggregateTarget section */
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
+ buildPhases = (
+ 33CC111E2044C6BF0003C045 /* ShellScript */,
+ );
+ dependencies = (
+ );
+ name = "Flutter Assemble";
+ productName = FLX;
+ };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 33CC111A2044C6BA0003C045;
+ remoteInfo = FLX;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 33CC110E2044A8840003C045 /* Bundle Framework */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Bundle Framework";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; };
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; };
+ 33CC10ED2044A3C60003C045 /* desafio_mobile.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "desafio_mobile.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; };
+ 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; };
+ 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; };
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; };
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; };
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; };
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; };
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; };
+ 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; };
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 33CC10EA2044A3C60003C045 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 33BA886A226E78AF003329D5 /* Configs */ = {
+ isa = PBXGroup;
+ children = (
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
+ );
+ path = Configs;
+ sourceTree = "";
+ };
+ 33CC10E42044A3C60003C045 = {
+ isa = PBXGroup;
+ children = (
+ 33FAB671232836740065AC1E /* Runner */,
+ 33CEB47122A05771004F2AC0 /* Flutter */,
+ 33CC10EE2044A3C60003C045 /* Products */,
+ D73912EC22F37F3D000D13A0 /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 33CC10EE2044A3C60003C045 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10ED2044A3C60003C045 /* desafio_mobile.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 33CC11242044D66E0003C045 /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */,
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */,
+ 33CC10F72044A3C60003C045 /* Info.plist */,
+ );
+ name = Resources;
+ path = ..;
+ sourceTree = "";
+ };
+ 33CEB47122A05771004F2AC0 /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
+ );
+ path = Flutter;
+ sourceTree = "";
+ };
+ 33FAB671232836740065AC1E /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */,
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */,
+ 33E51914231749380026EE4D /* Release.entitlements */,
+ 33CC11242044D66E0003C045 /* Resources */,
+ 33BA886A226E78AF003329D5 /* Configs */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+ D73912EC22F37F3D000D13A0 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 33CC10EC2044A3C60003C045 /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 33CC10E92044A3C60003C045 /* Sources */,
+ 33CC10EA2044A3C60003C045 /* Frameworks */,
+ 33CC10EB2044A3C60003C045 /* Resources */,
+ 33CC110E2044A8840003C045 /* Bundle Framework */,
+ 3399D490228B24CF009A79C7 /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */,
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 33CC10ED2044A3C60003C045 /* desafio_mobile.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 33CC10E52044A3C60003C045 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 0920;
+ LastUpgradeCheck = 1300;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 33CC10EC2044A3C60003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ LastSwiftMigration = 1100;
+ ProvisioningStyle = Automatic;
+ SystemCapabilities = {
+ com.apple.Sandbox = {
+ enabled = 1;
+ };
+ };
+ };
+ 33CC111A2044C6BA0003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ ProvisioningStyle = Manual;
+ };
+ };
+ };
+ buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 33CC10E42044A3C60003C045;
+ productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 33CC10EC2044A3C60003C045 /* Runner */,
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 33CC10EB2044A3C60003C045 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3399D490228B24CF009A79C7 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
+ };
+ 33CC111E2044C6BF0003C045 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ Flutter/ephemeral/FlutterInputs.xcfilelist,
+ );
+ inputPaths = (
+ Flutter/ephemeral/tripwire,
+ );
+ outputFileListPaths = (
+ Flutter/ephemeral/FlutterOutputs.xcfilelist,
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 33CC10E92044A3C60003C045 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
+ targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 33CC10F52044A3C60003C045 /* Base */,
+ );
+ name = MainMenu.xib;
+ path = Runner;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 338D0CE9231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Profile;
+ };
+ 338D0CEA231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Profile;
+ };
+ 338D0CEB231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Profile;
+ };
+ 33CC10F92044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 33CC10FA2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Release;
+ };
+ 33CC10FC2044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Debug;
+ };
+ 33CC10FD2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Release;
+ };
+ 33CC111C2044C6BA0003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ 33CC111D2044C6BA0003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Automatic;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10F92044A3C60003C045 /* Debug */,
+ 33CC10FA2044A3C60003C045 /* Release */,
+ 338D0CE9231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10FC2044A3C60003C045 /* Debug */,
+ 33CC10FD2044A3C60003C045 /* Release */,
+ 338D0CEA231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC111C2044C6BA0003C045 /* Debug */,
+ 33CC111D2044C6BA0003C045 /* Release */,
+ 338D0CEB231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 33CC10E52044A3C60003C045 /* Project object */;
+}
diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 00000000..2497533b
--- /dev/null
+++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..1d526a16
--- /dev/null
+++ b/macos/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift
new file mode 100644
index 00000000..d53ef643
--- /dev/null
+++ b/macos/Runner/AppDelegate.swift
@@ -0,0 +1,9 @@
+import Cocoa
+import FlutterMacOS
+
+@NSApplicationMain
+class AppDelegate: FlutterAppDelegate {
+ override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
+ return true
+ }
+}
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000..a2ec33f1
--- /dev/null
+++ b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,68 @@
+{
+ "images" : [
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_16.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_64.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_128.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_1024.png",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
new file mode 100644
index 00000000..3c4935a7
Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
new file mode 100644
index 00000000..ed4cc164
Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
new file mode 100644
index 00000000..483be613
Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
new file mode 100644
index 00000000..bcbf36df
Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
new file mode 100644
index 00000000..9c0a6528
Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
new file mode 100644
index 00000000..e71a7261
Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
new file mode 100644
index 00000000..8a31fe2d
Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ
diff --git a/macos/Runner/Base.lproj/MainMenu.xib b/macos/Runner/Base.lproj/MainMenu.xib
new file mode 100644
index 00000000..80e867a4
--- /dev/null
+++ b/macos/Runner/Base.lproj/MainMenu.xib
@@ -0,0 +1,343 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig
new file mode 100644
index 00000000..e6ecc48d
--- /dev/null
+++ b/macos/Runner/Configs/AppInfo.xcconfig
@@ -0,0 +1,14 @@
+// Application-level settings for the Runner target.
+//
+// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
+// future. If not, the values below would default to using the project name when this becomes a
+// 'flutter create' template.
+
+// The application's name. By default this is also the title of the Flutter window.
+PRODUCT_NAME = desafio_mobile
+
+// The application's bundle identifier
+PRODUCT_BUNDLE_IDENTIFIER = com.example.desafioMobile
+
+// The copyright displayed in application information
+PRODUCT_COPYRIGHT = Copyright © 2022 com.example. All rights reserved.
diff --git a/macos/Runner/Configs/Debug.xcconfig b/macos/Runner/Configs/Debug.xcconfig
new file mode 100644
index 00000000..36b0fd94
--- /dev/null
+++ b/macos/Runner/Configs/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Debug.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/macos/Runner/Configs/Release.xcconfig b/macos/Runner/Configs/Release.xcconfig
new file mode 100644
index 00000000..dff4f495
--- /dev/null
+++ b/macos/Runner/Configs/Release.xcconfig
@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Release.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/macos/Runner/Configs/Warnings.xcconfig b/macos/Runner/Configs/Warnings.xcconfig
new file mode 100644
index 00000000..42bcbf47
--- /dev/null
+++ b/macos/Runner/Configs/Warnings.xcconfig
@@ -0,0 +1,13 @@
+WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
+GCC_WARN_UNDECLARED_SELECTOR = YES
+CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
+CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
+CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
+CLANG_WARN_PRAGMA_PACK = YES
+CLANG_WARN_STRICT_PROTOTYPES = YES
+CLANG_WARN_COMMA = YES
+GCC_WARN_STRICT_SELECTOR_MATCH = YES
+CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
+CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
+GCC_WARN_SHADOW = YES
+CLANG_WARN_UNREACHABLE_CODE = YES
diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements
new file mode 100644
index 00000000..dddb8a30
--- /dev/null
+++ b/macos/Runner/DebugProfile.entitlements
@@ -0,0 +1,12 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.cs.allow-jit
+
+ com.apple.security.network.server
+
+
+
diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist
new file mode 100644
index 00000000..4789daa6
--- /dev/null
+++ b/macos/Runner/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSMinimumSystemVersion
+ $(MACOSX_DEPLOYMENT_TARGET)
+ NSHumanReadableCopyright
+ $(PRODUCT_COPYRIGHT)
+ NSMainNibFile
+ MainMenu
+ NSPrincipalClass
+ NSApplication
+
+
diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift
new file mode 100644
index 00000000..2722837e
--- /dev/null
+++ b/macos/Runner/MainFlutterWindow.swift
@@ -0,0 +1,15 @@
+import Cocoa
+import FlutterMacOS
+
+class MainFlutterWindow: NSWindow {
+ override func awakeFromNib() {
+ let flutterViewController = FlutterViewController.init()
+ let windowFrame = self.frame
+ self.contentViewController = flutterViewController
+ self.setFrame(windowFrame, display: true)
+
+ RegisterGeneratedPlugins(registry: flutterViewController)
+
+ super.awakeFromNib()
+ }
+}
diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements
new file mode 100644
index 00000000..852fa1a4
--- /dev/null
+++ b/macos/Runner/Release.entitlements
@@ -0,0 +1,8 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+
+
diff --git a/pubspec.lock b/pubspec.lock
new file mode 100644
index 00000000..f8e8eed4
--- /dev/null
+++ b/pubspec.lock
@@ -0,0 +1,899 @@
+# Generated by pub
+# See https://dart.dev/tools/pub/glossary#lockfile
+packages:
+ _fe_analyzer_shared:
+ dependency: transitive
+ description:
+ name: _fe_analyzer_shared
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "40.0.0"
+ analyzer:
+ dependency: transitive
+ description:
+ name: analyzer
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.1.0"
+ analyzer_plugin:
+ dependency: transitive
+ description:
+ name: analyzer_plugin
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.10.0"
+ args:
+ dependency: transitive
+ description:
+ name: args
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.3.1"
+ async:
+ dependency: transitive
+ description:
+ name: async
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.8.2"
+ bloc:
+ dependency: transitive
+ description:
+ name: bloc
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "8.0.3"
+ boolean_selector:
+ dependency: transitive
+ description:
+ name: boolean_selector
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.0"
+ build:
+ dependency: transitive
+ description:
+ name: build
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.3.0"
+ build_config:
+ dependency: transitive
+ description:
+ name: build_config
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.0"
+ build_daemon:
+ dependency: transitive
+ description:
+ name: build_daemon
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.1.0"
+ build_resolvers:
+ dependency: transitive
+ description:
+ name: build_resolvers
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.9"
+ build_runner:
+ dependency: "direct dev"
+ description:
+ name: build_runner
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.11"
+ build_runner_core:
+ dependency: transitive
+ description:
+ name: build_runner_core
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "7.2.3"
+ built_collection:
+ dependency: transitive
+ description:
+ name: built_collection
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "5.1.1"
+ built_value:
+ dependency: transitive
+ description:
+ name: built_value
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "8.3.3"
+ characters:
+ dependency: transitive
+ description:
+ name: characters
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.2.0"
+ charcode:
+ dependency: transitive
+ description:
+ name: charcode
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.3.1"
+ checked_yaml:
+ dependency: transitive
+ description:
+ name: checked_yaml
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.1"
+ cli_util:
+ dependency: transitive
+ description:
+ name: cli_util
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.3.5"
+ clock:
+ dependency: transitive
+ description:
+ name: clock
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.0"
+ cloud_firestore:
+ dependency: "direct main"
+ description:
+ name: cloud_firestore
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.1.18"
+ cloud_firestore_platform_interface:
+ dependency: transitive
+ description:
+ name: cloud_firestore_platform_interface
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "5.5.8"
+ cloud_firestore_web:
+ dependency: transitive
+ description:
+ name: cloud_firestore_web
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.6.17"
+ code_builder:
+ dependency: transitive
+ description:
+ name: code_builder
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.1.0"
+ collection:
+ dependency: transitive
+ description:
+ name: collection
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.16.0"
+ convert:
+ dependency: transitive
+ description:
+ name: convert
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.0.2"
+ coverage:
+ dependency: transitive
+ description:
+ name: coverage
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.3.2"
+ crypto:
+ dependency: transitive
+ description:
+ name: crypto
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.0.2"
+ cupertino_icons:
+ dependency: "direct main"
+ description:
+ name: cupertino_icons
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.5"
+ dart_style:
+ dependency: transitive
+ description:
+ name: dart_style
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.2.3"
+ dartz:
+ dependency: "direct main"
+ description:
+ name: dartz
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.10.1"
+ dio:
+ dependency: "direct main"
+ description:
+ name: dio
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.0.6"
+ drift:
+ dependency: transitive
+ description:
+ name: drift
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.7.0"
+ drift_dev:
+ dependency: transitive
+ description:
+ name: drift_dev
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.7.0"
+ fake_async:
+ dependency: transitive
+ description:
+ name: fake_async
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.3.0"
+ ffi:
+ dependency: transitive
+ description:
+ name: ffi
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.1"
+ file:
+ dependency: transitive
+ description:
+ name: file
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "6.1.2"
+ firebase_analytics:
+ dependency: "direct main"
+ description:
+ name: firebase_analytics
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "9.1.10"
+ firebase_analytics_platform_interface:
+ dependency: transitive
+ description:
+ name: firebase_analytics_platform_interface
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.1.8"
+ firebase_analytics_web:
+ dependency: transitive
+ description:
+ name: firebase_analytics_web
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.4.0+15"
+ firebase_auth:
+ dependency: "direct main"
+ description:
+ name: firebase_auth
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.3.20"
+ firebase_auth_platform_interface:
+ dependency: transitive
+ description:
+ name: firebase_auth_platform_interface
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "6.2.8"
+ firebase_auth_web:
+ dependency: transitive
+ description:
+ name: firebase_auth_web
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.3.17"
+ firebase_core:
+ dependency: transitive
+ description:
+ name: firebase_core
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.18.0"
+ firebase_core_platform_interface:
+ dependency: transitive
+ description:
+ name: firebase_core_platform_interface
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.4.1"
+ firebase_core_web:
+ dependency: transitive
+ description:
+ name: firebase_core_web
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.6.5"
+ firebase_crashlytics:
+ dependency: "direct main"
+ description:
+ name: firebase_crashlytics
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.8.2"
+ firebase_crashlytics_platform_interface:
+ dependency: transitive
+ description:
+ name: firebase_crashlytics_platform_interface
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.2.8"
+ fixnum:
+ dependency: transitive
+ description:
+ name: fixnum
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.1"
+ flutter:
+ dependency: "direct main"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_bloc:
+ dependency: "direct main"
+ description:
+ name: flutter_bloc
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "8.0.1"
+ flutter_form_builder:
+ dependency: "direct main"
+ description:
+ name: flutter_form_builder
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "7.3.1"
+ flutter_lints:
+ dependency: "direct dev"
+ description:
+ name: flutter_lints
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.1"
+ flutter_localizations:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_modular:
+ dependency: "direct main"
+ description:
+ name: flutter_modular
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "5.0.3"
+ flutter_modular_annotations:
+ dependency: transitive
+ description:
+ name: flutter_modular_annotations
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.0.2"
+ flutter_plugin_android_lifecycle:
+ dependency: transitive
+ description:
+ name: flutter_plugin_android_lifecycle
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.6"
+ flutter_test:
+ dependency: "direct dev"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_web_plugins:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ form_builder_validators:
+ dependency: "direct main"
+ description:
+ name: form_builder_validators
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "8.1.1"
+ frontend_server_client:
+ dependency: transitive
+ description:
+ name: frontend_server_client
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.3"
+ geolocator:
+ dependency: "direct main"
+ description:
+ name: geolocator
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "9.0.0"
+ geolocator_android:
+ dependency: transitive
+ description:
+ name: geolocator_android
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.0.0"
+ geolocator_apple:
+ dependency: transitive
+ description:
+ name: geolocator_apple
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.2.0"
+ geolocator_platform_interface:
+ dependency: transitive
+ description:
+ name: geolocator_platform_interface
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.0.5"
+ geolocator_web:
+ dependency: transitive
+ description:
+ name: geolocator_web
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.5"
+ geolocator_windows:
+ dependency: transitive
+ description:
+ name: geolocator_windows
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.1.1"
+ glob:
+ dependency: transitive
+ description:
+ name: glob
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.0"
+ google_maps_flutter:
+ dependency: "direct main"
+ description:
+ name: google_maps_flutter
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.8"
+ google_maps_flutter_platform_interface:
+ dependency: transitive
+ description:
+ name: google_maps_flutter_platform_interface
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.2.0"
+ graphs:
+ dependency: transitive
+ description:
+ name: graphs
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.0"
+ http_multi_server:
+ dependency: transitive
+ description:
+ name: http_multi_server
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.2.1"
+ http_parser:
+ dependency: transitive
+ description:
+ name: http_parser
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.0.1"
+ intl:
+ dependency: transitive
+ description:
+ name: intl
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.17.0"
+ io:
+ dependency: transitive
+ description:
+ name: io
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.3"
+ js:
+ dependency: transitive
+ description:
+ name: js
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.6.4"
+ json_annotation:
+ dependency: transitive
+ description:
+ name: json_annotation
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.5.0"
+ lints:
+ dependency: transitive
+ description:
+ name: lints
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0"
+ logging:
+ dependency: transitive
+ description:
+ name: logging
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.2"
+ matcher:
+ dependency: transitive
+ description:
+ name: matcher
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.12.11"
+ material_color_utilities:
+ dependency: transitive
+ description:
+ name: material_color_utilities
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.1.4"
+ meta:
+ dependency: transitive
+ description:
+ name: meta
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.7.0"
+ mime:
+ dependency: transitive
+ description:
+ name: mime
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.2"
+ mocktail:
+ dependency: "direct dev"
+ description:
+ name: mocktail
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.3.0"
+ modular_core:
+ dependency: transitive
+ description:
+ name: modular_core
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.3+1"
+ modular_interfaces:
+ dependency: transitive
+ description:
+ name: modular_interfaces
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.2"
+ moor:
+ dependency: transitive
+ description:
+ name: moor
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.6.1+1"
+ moor_flutter:
+ dependency: "direct main"
+ description:
+ name: moor_flutter
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.1.0"
+ moor_generator:
+ dependency: "direct dev"
+ description:
+ name: moor_generator
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.6.0+1"
+ nested:
+ dependency: transitive
+ description:
+ name: nested
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.0"
+ node_preamble:
+ dependency: transitive
+ description:
+ name: node_preamble
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.1"
+ package_config:
+ dependency: transitive
+ description:
+ name: package_config
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.0"
+ path:
+ dependency: transitive
+ description:
+ name: path
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.8.1"
+ plugin_platform_interface:
+ dependency: transitive
+ description:
+ name: plugin_platform_interface
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.2"
+ pool:
+ dependency: transitive
+ description:
+ name: pool
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.5.1"
+ provider:
+ dependency: transitive
+ description:
+ name: provider
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "6.0.3"
+ pub_semver:
+ dependency: transitive
+ description:
+ name: pub_semver
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.1"
+ pubspec_parse:
+ dependency: transitive
+ description:
+ name: pubspec_parse
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.2.0"
+ recase:
+ dependency: transitive
+ description:
+ name: recase
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.0.0"
+ shelf:
+ dependency: transitive
+ description:
+ name: shelf
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.3.1"
+ shelf_packages_handler:
+ dependency: transitive
+ description:
+ name: shelf_packages_handler
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.0.1"
+ shelf_static:
+ dependency: transitive
+ description:
+ name: shelf_static
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.1"
+ shelf_web_socket:
+ dependency: transitive
+ description:
+ name: shelf_web_socket
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.2"
+ sky_engine:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.99"
+ source_gen:
+ dependency: transitive
+ description:
+ name: source_gen
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.2.2"
+ source_map_stack_trace:
+ dependency: transitive
+ description:
+ name: source_map_stack_trace
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.0"
+ source_maps:
+ dependency: transitive
+ description:
+ name: source_maps
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.10.10"
+ source_span:
+ dependency: transitive
+ description:
+ name: source_span
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.8.2"
+ sqflite:
+ dependency: transitive
+ description:
+ name: sqflite
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.2+1"
+ sqflite_common:
+ dependency: transitive
+ description:
+ name: sqflite_common
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.2.1+1"
+ sqlite3:
+ dependency: transitive
+ description:
+ name: sqlite3
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.7.2"
+ sqlparser:
+ dependency: transitive
+ description:
+ name: sqlparser
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.22.0"
+ stack_trace:
+ dependency: transitive
+ description:
+ name: stack_trace
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.10.0"
+ stream_channel:
+ dependency: transitive
+ description:
+ name: stream_channel
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.0"
+ stream_transform:
+ dependency: transitive
+ description:
+ name: stream_transform
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0"
+ string_scanner:
+ dependency: transitive
+ description:
+ name: string_scanner
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.0"
+ synchronized:
+ dependency: transitive
+ description:
+ name: synchronized
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.0.0+2"
+ term_glyph:
+ dependency: transitive
+ description:
+ name: term_glyph
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.2.0"
+ test:
+ dependency: transitive
+ description:
+ name: test
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.21.1"
+ test_api:
+ dependency: transitive
+ description:
+ name: test_api
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.4.9"
+ test_core:
+ dependency: transitive
+ description:
+ name: test_core
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.4.13"
+ timing:
+ dependency: transitive
+ description:
+ name: timing
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.0"
+ typed_data:
+ dependency: transitive
+ description:
+ name: typed_data
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.3.1"
+ vector_math:
+ dependency: transitive
+ description:
+ name: vector_math
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.1.2"
+ vm_service:
+ dependency: transitive
+ description:
+ name: vm_service
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "8.3.0"
+ watcher:
+ dependency: transitive
+ description:
+ name: watcher
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.1"
+ web_socket_channel:
+ dependency: transitive
+ description:
+ name: web_socket_channel
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.2.0"
+ webkit_inspection_protocol:
+ dependency: transitive
+ description:
+ name: webkit_inspection_protocol
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.0"
+ yaml:
+ dependency: transitive
+ description:
+ name: yaml
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.1.1"
+sdks:
+ dart: ">=2.17.5 <3.0.0"
+ flutter: ">=3.0.0"
diff --git a/pubspec.yaml b/pubspec.yaml
new file mode 100644
index 00000000..56f2919d
--- /dev/null
+++ b/pubspec.yaml
@@ -0,0 +1,105 @@
+name: desafio_mobile
+description: A new Flutter project.
+
+# The following line prevents the package from being accidentally published to
+# pub.dev using `flutter pub publish`. This is preferred for private packages.
+publish_to: 'none' # Remove this line if you wish to publish to pub.dev
+
+# The following defines the version and build number for your application.
+# A version number is three numbers separated by dots, like 1.2.43
+# followed by an optional build number separated by a +.
+# Both the version and the builder number may be overridden in flutter
+# build by specifying --build-name and --build-number, respectively.
+# In Android, build-name is used as versionName while build-number used as versionCode.
+# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
+# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
+# Read more about iOS versioning at
+# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
+version: 1.0.0+1
+
+environment:
+ sdk: ">=2.17.5 <3.0.0"
+
+# Dependencies specify other packages that your package needs in order to work.
+# To automatically upgrade your package dependencies to the latest versions
+# consider running `flutter pub upgrade --major-versions`. Alternatively,
+# dependencies can be manually updated by changing the version numbers below to
+# the latest version available on pub.dev. To see which dependencies have newer
+# versions available, run `flutter pub outdated`.
+dependencies:
+ flutter:
+ sdk: flutter
+
+
+ # The following adds the Cupertino Icons font to your application.
+ # Use with the CupertinoIcons class for iOS style icons.
+ cupertino_icons: ^1.0.2
+ dio: ^4.0.6
+ dartz: ^0.10.1
+ firebase_auth: ^3.3.20
+ cloud_firestore: ^3.1.18
+ flutter_bloc: ^8.0.1
+ flutter_modular: ^5.0.3
+ flutter_form_builder: ^7.3.1
+ form_builder_validators: ^8.1.1
+ firebase_analytics: ^9.1.10
+ firebase_crashlytics: ^2.8.2
+ moor_flutter: ^4.1.0
+ geolocator: ^9.0.0
+ google_maps_flutter: ^2.1.8
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+
+ # The "flutter_lints" package below contains a set of recommended lints to
+ # encourage good coding practices. The lint set provided by the package is
+ # activated in the `analysis_options.yaml` file located at the root of your
+ # package. See that file for information about deactivating specific lint
+ # rules and activating additional ones.
+ flutter_lints: ^2.0.0
+ mocktail: ^0.3.0
+ moor_generator: ^4.6.0+1
+ build_runner: ^2.1.11
+
+# For information on the generic Dart part of this file, see the
+# following page: https://dart.dev/tools/pub/pubspec
+
+# The following section is specific to Flutter packages.
+flutter:
+
+ # The following line ensures that the Material Icons font is
+ # included with your application, so that you can use the icons in
+ # the material Icons class.
+ uses-material-design: true
+
+ # To add assets to your application, add an assets section, like this:
+ assets:
+ - assets/images/
+ # - images/a_dot_ham.jpeg
+
+ # An image asset can refer to one or more resolution-specific "variants", see
+ # https://flutter.dev/assets-and-images/#resolution-aware
+
+ # For details regarding adding assets from package dependencies, see
+ # https://flutter.dev/assets-and-images/#from-packages
+
+ # To add custom fonts to your application, add a fonts section here,
+ # in this "flutter" section. Each entry in this list should have a
+ # "family" key with the font family name, and a "fonts" key with a
+ # list giving the asset and other descriptors for the font. For
+ # example:
+ # fonts:
+ # - family: Schyler
+ # fonts:
+ # - asset: fonts/Schyler-Regular.ttf
+ # - asset: fonts/Schyler-Italic.ttf
+ # style: italic
+ # - family: Trajan Pro
+ # fonts:
+ # - asset: fonts/TrajanPro.ttf
+ # - asset: fonts/TrajanPro_Bold.ttf
+ # weight: 700
+ #
+ # For details regarding fonts from package dependencies,
+ # see https://flutter.dev/custom-fonts/#from-packages
diff --git a/test/src/data/repositories/auth_repository_test.dart b/test/src/data/repositories/auth_repository_test.dart
new file mode 100644
index 00000000..166592b9
--- /dev/null
+++ b/test/src/data/repositories/auth_repository_test.dart
@@ -0,0 +1,34 @@
+import 'package:dartz/dartz.dart';
+import 'package:desafio_mobile/src/shared/data_struct/auth.dart';
+import 'package:desafio_mobile/src/data/datasources/auth_datasource.dart';
+import 'package:desafio_mobile/src/data/repositories/auth_repository.dart';
+import 'package:desafio_mobile/src/domain/entities/user.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mocktail/mocktail.dart';
+
+class AuthDatasourceMock extends Mock implements IAuthDatasource {}
+
+void main() {
+ final IAuthDatasource authDatasource = AuthDatasourceMock();
+
+ final repository = AuthRepository(authDatasource);
+
+ test('verificar de retorna o usuario', () async {
+ final auth = AuthUser(email: '', password: '');
+ when(() => authDatasource.authUser(auth)).thenAnswer((_) async => json);
+
+ final result = await repository.authUser(auth);
+
+ final user = result.fold(id, id);
+ expect(user, isA());
+
+ expect((user as User).email, 'jacksonpaulino@gmail.com');
+
+ expect((user).uid, 'DDr80n424LMt5MM8eweeklcnsqQ2');
+ });
+}
+
+final json = {
+ 'uid': 'DDr80n424LMt5MM8eweeklcnsqQ2',
+ 'email': 'jacksonpaulino@gmail.com'
+};
diff --git a/test/src/data/repositories/bank_repository_test.dart b/test/src/data/repositories/bank_repository_test.dart
new file mode 100644
index 00000000..4fb6d07d
--- /dev/null
+++ b/test/src/data/repositories/bank_repository_test.dart
@@ -0,0 +1,67 @@
+import 'package:desafio_mobile/src/data/datasources/featch_bank_location_datasource.dart';
+import 'package:desafio_mobile/src/data/datasources/save_bank_location_datasource.dart';
+import 'package:desafio_mobile/src/data/repositories/bank_repository.dart';
+import 'package:desafio_mobile/src/domain/entities/location.dart';
+import 'package:desafio_mobile/src/domain/entities/user.dart';
+import 'package:desafio_mobile/src/external/database/app_database.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mocktail/mocktail.dart';
+
+class SaveLocationDatasourceMock extends Mock implements ISaveBankLocationDatasource {}
+
+class FeatchLocationDatasourceMock extends Mock implements IFeatchBankLocationDatasource {}
+
+void main() {
+ final ISaveBankLocationDatasource saveLocation = SaveLocationDatasourceMock();
+ final IFeatchBankLocationDatasource featchLocation = FeatchLocationDatasourceMock();
+
+ final repository = BankRepository(saveLocation, featchLocation);
+
+ test('check if the location has been saved in the bank', () async {
+ final user = User(uid: 'uid', email: 'email');
+ final location = Location(latitude: 123, longitude: 123, user: user);
+
+ when(() => saveLocation.saveLocation(location)).thenAnswer((_) async => 1);
+
+ final result = await repository.saveLocation(location);
+
+ final resultLocation = result.fold((l) => l, (r) => r);
+
+ expect(resultLocation, isA());
+
+ expect((resultLocation as int), 1);
+ });
+
+ test('should return the location saved in the database', () async {
+ final user = User(uid: 'uid', email: 'email');
+ final userLocalization = UserLocalizationData(
+ id: 1,
+ uid: 'uid',
+ email: 'email',
+ latitude: '111',
+ longitude: '2222',
+ );
+
+ when(() => featchLocation.featchLocation(user.uid)).thenAnswer((_) async => userLocalization);
+
+ final result = await repository.featchLocation(user.uid);
+
+ final resultLocation = result.fold((l) => l, (r) => r);
+
+ expect(resultLocation, isA());
+
+ expect((resultLocation as Location).user.uid, 'uid');
+ });
+
+ test('should return null when not finding the location in the database', () async {
+ final user = User(uid: 'uid', email: 'email');
+
+ when(() => featchLocation.featchLocation(user.uid)).thenAnswer((_) async => null);
+
+ final result = await repository.featchLocation(user.uid);
+
+ final resultLocation = result.fold((l) => l, (r) => r);
+
+ expect(resultLocation, null);
+ });
+}
diff --git a/test/src/data/repositories/position_repository_test.dart b/test/src/data/repositories/position_repository_test.dart
new file mode 100644
index 00000000..64d07e37
--- /dev/null
+++ b/test/src/data/repositories/position_repository_test.dart
@@ -0,0 +1,53 @@
+import 'package:desafio_mobile/src/data/datasources/get_current_position_datasource.dart';
+import 'package:desafio_mobile/src/data/repositories/position_repository.dart';
+import 'package:desafio_mobile/src/domain/errors/errors.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:geolocator/geolocator.dart';
+import 'package:google_maps_flutter/google_maps_flutter.dart';
+import 'package:mocktail/mocktail.dart';
+
+class GetCurrentPositionDatasourceMock extends Mock implements IGetCurrentPositionDatasource {}
+
+void main() {
+ final IGetCurrentPositionDatasource getCurrentPosition = GetCurrentPositionDatasourceMock();
+
+ final repository = PositionRepository(getCurrentPosition);
+
+ test('should return a Position with the device\'s location', () async {
+ final position = Position(
+ longitude: 123.0,
+ latitude: 90.0,
+ timestamp: DateTime.now(),
+ accuracy: 1,
+ altitude: 1,
+ heading: 1,
+ speed: 1,
+ speedAccuracy: 1,
+ );
+
+ when(() => getCurrentPosition.featchLocation()).thenAnswer((_) async => position);
+
+ final result = await repository.featchPosition();
+
+ final resultLocation = result.fold((l) => l, (r) => r);
+
+ expect(resultLocation, isA());
+
+ expect((resultLocation as LatLng).latitude, 90.0);
+ });
+
+ test('should return a FalireException if not authorized', () async {
+ const falire = FalireException('Você precisa autorizar o acesso à localização');
+
+ when(() => getCurrentPosition.featchLocation()).thenThrow(falire);
+
+ final result = await repository.featchPosition();
+
+ final resultLocation = result.fold((l) => l, (r) => r);
+
+ expect(resultLocation, isA());
+
+ expect((resultLocation as FalireException).message,
+ 'Você precisa autorizar o acesso à localização');
+ });
+}
diff --git a/test/src/domain/usecases/auth_user_usecase_test.dart b/test/src/domain/usecases/auth_user_usecase_test.dart
new file mode 100644
index 00000000..3e755b2f
--- /dev/null
+++ b/test/src/domain/usecases/auth_user_usecase_test.dart
@@ -0,0 +1,46 @@
+import 'package:dartz/dartz.dart';
+import 'package:desafio_mobile/src/shared/data_struct/auth.dart';
+import 'package:desafio_mobile/src/domain/entities/user.dart';
+import 'package:desafio_mobile/src/domain/errors/errors.dart';
+import 'package:desafio_mobile/src/domain/repositories/auth_repository.dart';
+import 'package:desafio_mobile/src/domain/usecases/auth_user_usecase.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mocktail/mocktail.dart';
+
+class AuthRepositoryMock extends Mock implements IAuthRepository {}
+
+void main() {
+ final repository = AuthRepositoryMock();
+ final usecase = AuthUserUsecase(repository);
+
+ test('should return the data of the logged in user', () async {
+ final auth = AuthUser(email: 'login', password: 'password');
+
+ when(() => repository.authUser(auth)).thenAnswer((_) async => right(User(uid: '', email: '')));
+
+ final result = await usecase(auth);
+
+ expect(result.fold(id, id), isA());
+ });
+
+ test('should return an AuthException if it sends the login empty or null', () async {
+ final auth = AuthUser(email: '', password: '00');
+ const falire = FalireException('error');
+
+ when(() => repository.authUser(auth)).thenAnswer((_) async => left(falire));
+
+ final result = await usecase(auth);
+
+ expect(result.fold(id, id), isA());
+ });
+ test('should return an AuthException if it sends the password empty or null', () async {
+ final auth = AuthUser(email: 'a', password: '');
+ const falire = FalireException('error');
+
+ when(() => repository.authUser(auth)).thenAnswer((_) async => left(falire));
+
+ final result = await usecase(auth);
+
+ expect(result.fold(id, id), isA());
+ });
+}
diff --git a/test/src/domain/usecases/featch_location_usecase_test.dart b/test/src/domain/usecases/featch_location_usecase_test.dart
new file mode 100644
index 00000000..a57eba7d
--- /dev/null
+++ b/test/src/domain/usecases/featch_location_usecase_test.dart
@@ -0,0 +1,47 @@
+import 'package:dartz/dartz.dart';
+import 'package:desafio_mobile/src/domain/entities/location.dart';
+import 'package:desafio_mobile/src/domain/entities/user.dart';
+import 'package:desafio_mobile/src/domain/errors/errors.dart';
+import 'package:desafio_mobile/src/domain/repositories/bank_repository.dart';
+import 'package:desafio_mobile/src/domain/usecases/featch_bank_location_usecase.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mocktail/mocktail.dart';
+
+class LocationRepositoryMock extends Mock implements IBankRepository {}
+
+void main() {
+ final repository = LocationRepositoryMock();
+ final usecase = FeatchBankLocationUsecase(repository);
+
+ test('should return the last location saved in the database', () async {
+ final user = User(uid: 'uid', email: 'email');
+ final location = Location(latitude: 123, longitude: 123, user: user);
+
+ when(() => repository.featchLocation(user.uid)).thenAnswer((_) async => right(location));
+
+ final result = await usecase(user.uid);
+
+ expect(result.fold(id, id), isA());
+ });
+
+ test('should return null when it does not have a location saved in the database', () async {
+ final user = User(uid: 'uid', email: 'email');
+
+ when(() => repository.featchLocation(user.uid)).thenAnswer((_) async => right(null));
+
+ final result = await usecase(user.uid);
+
+ expect(result.fold(id, id), null);
+ });
+
+ test('should return a FalireException for some error in the database query', () async {
+ final user = User(uid: 'a', email: '');
+ const falire = FalireException('error');
+
+ when(() => repository.featchLocation(user.uid)).thenAnswer((_) async => left(falire));
+
+ final result = await usecase(user.uid);
+
+ expect(result.fold(id, id), isA());
+ });
+}
diff --git a/test/src/domain/usecases/get_current_position_usecase_test.dart b/test/src/domain/usecases/get_current_position_usecase_test.dart
new file mode 100644
index 00000000..4ae08a60
--- /dev/null
+++ b/test/src/domain/usecases/get_current_position_usecase_test.dart
@@ -0,0 +1,56 @@
+import 'package:dartz/dartz.dart';
+import 'package:desafio_mobile/src/data/adapters/position_adapter.dart';
+import 'package:desafio_mobile/src/domain/errors/errors.dart';
+import 'package:desafio_mobile/src/domain/repositories/position_repository.dart';
+import 'package:desafio_mobile/src/domain/usecases/get_current_position_usecase.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:geolocator/geolocator.dart';
+import 'package:google_maps_flutter/google_maps_flutter.dart';
+import 'package:mocktail/mocktail.dart';
+
+class PositionRepositoryMock extends Mock implements IPositionRepository {}
+
+void main() {
+ final repository = PositionRepositoryMock();
+ final usecase = GetCurrentPositionUsecase(repository);
+
+ test('should return the current position of the device', () async {
+ final position = Position(
+ longitude: 123.0,
+ latitude: 123.0,
+ timestamp: DateTime.now(),
+ accuracy: 1,
+ altitude: 1,
+ heading: 1,
+ speed: 1,
+ speedAccuracy: 1,
+ );
+ final positionAdapter = PositionAdapter.fromJson(position);
+
+ when(() => repository.featchPosition()).thenAnswer((_) async => right(positionAdapter));
+
+ final result = await usecase();
+
+ final resultPosition = result.fold(id, id);
+
+ expect(result.fold(id, id), isA());
+
+ expect((resultPosition as LatLng).longitude, 123.0);
+ });
+
+ test('should return Failure Exception if you don\'t have permission to access divice location',
+ () async {
+ const falire = FalireException('Você precisa autorizar o acesso à localização');
+
+ when(() => repository.featchPosition()).thenAnswer((_) async => left(falire));
+
+ final result = await usecase();
+
+ final resultPosition = result.fold(id, id);
+
+ expect(result.fold(id, id), isA());
+
+ expect((resultPosition as FalireException).message,
+ 'Você precisa autorizar o acesso à localização');
+ });
+}
diff --git a/test/src/domain/usecases/save_location_usecase_test.dart b/test/src/domain/usecases/save_location_usecase_test.dart
new file mode 100644
index 00000000..52d21beb
--- /dev/null
+++ b/test/src/domain/usecases/save_location_usecase_test.dart
@@ -0,0 +1,71 @@
+import 'package:dartz/dartz.dart';
+import 'package:desafio_mobile/src/domain/entities/location.dart';
+import 'package:desafio_mobile/src/domain/entities/user.dart';
+import 'package:desafio_mobile/src/domain/errors/errors.dart';
+import 'package:desafio_mobile/src/domain/repositories/bank_repository.dart';
+import 'package:desafio_mobile/src/domain/usecases/save_bank_location_usecase.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mocktail/mocktail.dart';
+
+class LocationRepositoryMock extends Mock implements IBankRepository {}
+
+void main() {
+ final repository = LocationRepositoryMock();
+ final usecase = SaveBankLocationUsecase(repository);
+
+ test('should return the code of the location saved in the database', () async {
+ final user = User(uid: 'uid', email: 'email');
+ final location = Location(latitude: 123, longitude: 123, user: user);
+
+ when(() => repository.saveLocation(location)).thenAnswer((_) async => right(1));
+
+ final result = await usecase(location);
+
+ expect(result.fold(id, id), isA());
+ });
+
+ test('should return a FalireException if the uid is empty or null', () async {
+ final user = User(uid: '', email: 'email');
+ final location = Location(latitude: 123, longitude: 123, user: user);
+ const falire = FalireException('error');
+
+ when(() => repository.saveLocation(location)).thenAnswer((_) async => left(falire));
+
+ final result = await usecase(location);
+
+ expect(result.fold(id, id), isA());
+ });
+ test('should return a FalireException if the email is empty or null', () async {
+ final user = User(uid: 'a', email: '');
+ final location = Location(latitude: 123, longitude: 123, user: user);
+ const falire = FalireException('error');
+
+ when(() => repository.saveLocation(location)).thenAnswer((_) async => left(falire));
+
+ final result = await usecase(location);
+
+ expect(result.fold(id, id), isA());
+ });
+ test('should return a FalireException if latitude is empty or null', () async {
+ final user = User(uid: 'a', email: 'a');
+ final location = Location(latitude: 0, longitude: 123, user: user);
+ const falire = FalireException('error');
+
+ when(() => repository.saveLocation(location)).thenAnswer((_) async => left(falire));
+
+ final result = await usecase(location);
+
+ expect(result.fold(id, id), isA());
+ });
+ test('should return a FalireException if the longitude is empty or null', () async {
+ final user = User(uid: 'a', email: 'a');
+ final location = Location(latitude: 123, longitude: 0, user: user);
+ const falire = FalireException('error');
+
+ when(() => repository.saveLocation(location)).thenAnswer((_) async => left(falire));
+
+ final result = await usecase(location);
+
+ expect(result.fold(id, id), isA());
+ });
+}
diff --git a/test/widget_test.dart b/test/widget_test.dart
new file mode 100644
index 00000000..2385651b
--- /dev/null
+++ b/test/widget_test.dart
@@ -0,0 +1,30 @@
+// This is a basic Flutter widget test.
+//
+// To perform an interaction with a widget in your test, use the WidgetTester
+// utility in the flutter_test package. For example, you can send tap and scroll
+// gestures. You can also use WidgetTester to find child widgets in the widget
+// tree, read text, and verify that the values of widget properties are correct.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'package:desafio_mobile/main.dart';
+
+void main() {
+ testWidgets('Counter increments smoke test', (WidgetTester tester) async {
+ // Build our app and trigger a frame.
+ await tester.pumpWidget(const MyApp());
+
+ // Verify that our counter starts at 0.
+ expect(find.text('0'), findsOneWidget);
+ expect(find.text('1'), findsNothing);
+
+ // Tap the '+' icon and trigger a frame.
+ await tester.tap(find.byIcon(Icons.add));
+ await tester.pump();
+
+ // Verify that our counter has incremented.
+ expect(find.text('0'), findsNothing);
+ expect(find.text('1'), findsOneWidget);
+ });
+}
diff --git a/web/favicon.png b/web/favicon.png
new file mode 100644
index 00000000..8aaa46ac
Binary files /dev/null and b/web/favicon.png differ
diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png
new file mode 100644
index 00000000..b749bfef
Binary files /dev/null and b/web/icons/Icon-192.png differ
diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png
new file mode 100644
index 00000000..88cfd48d
Binary files /dev/null and b/web/icons/Icon-512.png differ
diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png
new file mode 100644
index 00000000..eb9b4d76
Binary files /dev/null and b/web/icons/Icon-maskable-192.png differ
diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png
new file mode 100644
index 00000000..d69c5669
Binary files /dev/null and b/web/icons/Icon-maskable-512.png differ
diff --git a/web/index.html b/web/index.html
new file mode 100644
index 00000000..9da00513
--- /dev/null
+++ b/web/index.html
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ desafio_mobile
+
+
+
+
+
+
+
+
+
+
diff --git a/web/manifest.json b/web/manifest.json
new file mode 100644
index 00000000..31e36971
--- /dev/null
+++ b/web/manifest.json
@@ -0,0 +1,35 @@
+{
+ "name": "desafio_mobile",
+ "short_name": "desafio_mobile",
+ "start_url": ".",
+ "display": "standalone",
+ "background_color": "#0175C2",
+ "theme_color": "#0175C2",
+ "description": "A new Flutter project.",
+ "orientation": "portrait-primary",
+ "prefer_related_applications": false,
+ "icons": [
+ {
+ "src": "icons/Icon-192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-maskable-192.png",
+ "sizes": "192x192",
+ "type": "image/png",
+ "purpose": "maskable"
+ },
+ {
+ "src": "icons/Icon-maskable-512.png",
+ "sizes": "512x512",
+ "type": "image/png",
+ "purpose": "maskable"
+ }
+ ]
+}
diff --git a/windows/.gitignore b/windows/.gitignore
new file mode 100644
index 00000000..d492d0d9
--- /dev/null
+++ b/windows/.gitignore
@@ -0,0 +1,17 @@
+flutter/ephemeral/
+
+# Visual Studio user-specific files.
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# Visual Studio build-related files.
+x64/
+x86/
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt
new file mode 100644
index 00000000..81c7318c
--- /dev/null
+++ b/windows/CMakeLists.txt
@@ -0,0 +1,101 @@
+# Project-level configuration.
+cmake_minimum_required(VERSION 3.14)
+project(desafio_mobile LANGUAGES CXX)
+
+# The name of the executable created for the application. Change this to change
+# the on-disk name of your application.
+set(BINARY_NAME "desafio_mobile")
+
+# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
+# versions of CMake.
+cmake_policy(SET CMP0063 NEW)
+
+# Define build configuration option.
+get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if(IS_MULTICONFIG)
+ set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
+ CACHE STRING "" FORCE)
+else()
+ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE
+ STRING "Flutter build mode" FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+ "Debug" "Profile" "Release")
+ endif()
+endif()
+# Define settings for the Profile build mode.
+set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
+set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
+set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
+set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
+
+# Use Unicode for all projects.
+add_definitions(-DUNICODE -D_UNICODE)
+
+# Compilation settings that should be applied to most targets.
+#
+# Be cautious about adding new options here, as plugins use this function by
+# default. In most cases, you should add new options to specific targets instead
+# of modifying this function.
+function(APPLY_STANDARD_SETTINGS TARGET)
+ target_compile_features(${TARGET} PUBLIC cxx_std_17)
+ target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
+ target_compile_options(${TARGET} PRIVATE /EHsc)
+ target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
+ target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>")
+endfunction()
+
+# Flutter library and tool build rules.
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# Application build; see runner/CMakeLists.txt.
+add_subdirectory("runner")
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# Support files are copied into place next to the executable, so that it can
+# run in place. This is done instead of making a separate bundle (as on Linux)
+# so that building and running from within Visual Studio will work.
+set(BUILD_BUNDLE_DIR "$")
+# Make the "install" step default, as it's required to run.
+set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+if(PLUGIN_BUNDLED_LIBRARIES)
+ install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+ file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+ " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+ DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ CONFIGURATIONS Profile;Release
+ COMPONENT Runtime)
diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt
new file mode 100644
index 00000000..930d2071
--- /dev/null
+++ b/windows/flutter/CMakeLists.txt
@@ -0,0 +1,104 @@
+# This file controls Flutter-level build steps. It should not be edited.
+cmake_minimum_required(VERSION 3.14)
+
+set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
+
+# Configuration provided via flutter tool.
+include(${EPHEMERAL_DIR}/generated_config.cmake)
+
+# TODO: Move the rest of this into files in ephemeral. See
+# https://github.com/flutter/flutter/issues/57146.
+set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
+
+# === Flutter Library ===
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
+
+# Published to parent scope for install step.
+set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
+set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
+set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
+set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+ "flutter_export.h"
+ "flutter_windows.h"
+ "flutter_messenger.h"
+ "flutter_plugin_registrar.h"
+ "flutter_texture_registrar.h"
+)
+list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+ "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
+add_dependencies(flutter flutter_assemble)
+
+# === Wrapper ===
+list(APPEND CPP_WRAPPER_SOURCES_CORE
+ "core_implementations.cc"
+ "standard_codec.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
+list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
+ "plugin_registrar.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
+list(APPEND CPP_WRAPPER_SOURCES_APP
+ "flutter_engine.cc"
+ "flutter_view_controller.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
+
+# Wrapper sources needed for a plugin.
+add_library(flutter_wrapper_plugin STATIC
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_PLUGIN}
+)
+apply_standard_settings(flutter_wrapper_plugin)
+set_target_properties(flutter_wrapper_plugin PROPERTIES
+ POSITION_INDEPENDENT_CODE ON)
+set_target_properties(flutter_wrapper_plugin PROPERTIES
+ CXX_VISIBILITY_PRESET hidden)
+target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
+target_include_directories(flutter_wrapper_plugin PUBLIC
+ "${WRAPPER_ROOT}/include"
+)
+add_dependencies(flutter_wrapper_plugin flutter_assemble)
+
+# Wrapper sources needed for the runner.
+add_library(flutter_wrapper_app STATIC
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_APP}
+)
+apply_standard_settings(flutter_wrapper_app)
+target_link_libraries(flutter_wrapper_app PUBLIC flutter)
+target_include_directories(flutter_wrapper_app PUBLIC
+ "${WRAPPER_ROOT}/include"
+)
+add_dependencies(flutter_wrapper_app flutter_assemble)
+
+# === Flutter tool backend ===
+# _phony_ is a non-existent file to force this command to run every time,
+# since currently there's no way to get a full input/output list from the
+# flutter tool.
+set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
+set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
+add_custom_command(
+ OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+ ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
+ ${CPP_WRAPPER_SOURCES_APP}
+ ${PHONY_OUTPUT}
+ COMMAND ${CMAKE_COMMAND} -E env
+ ${FLUTTER_TOOL_ENVIRONMENT}
+ "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
+ windows-x64 $