From 265171040b8d7d2381d68f63ce928ce9f8380dcb Mon Sep 17 00:00:00 2001 From: Italo Matos Date: Wed, 21 Aug 2024 19:44:21 -0300 Subject: [PATCH 01/12] atualizando projeto --- .metadata | 39 +- .vscode/settings.json | 3 + android/.project | 11 + .../org.eclipse.buildship.core.prefs | 6 +- android/app/build.gradle | 95 +-- android/app/google-services.json | 48 -- android/build.gradle | 32 +- .../gradle/wrapper/gradle-wrapper.properties | 6 +- android/settings.gradle | 31 +- assets/images/devicon_google.png | Bin 0 -> 841 bytes firebase.json | 1 + ios/RunnerTests/RunnerTests.swift | 12 + lib/firebase_options.dart | 40 +- lib/main.dart | 5 +- .../mission_variables_bloc.dart | 3 +- .../screen/configuration_screen.dart | 4 +- .../forget_password/bloc/forgot_pwd_bloc.dart | 52 +- .../screen/forgot_my_password_screen.dart | 90 +- lib/modules/login/bloc/login_bloc.dart | 84 +- lib/modules/login/bloc/login_event.dart | 45 +- lib/modules/login/bloc/login_state.dart | 10 +- lib/modules/login/screen/login_screen.dart | 21 + lib/modules/signup/bloc/sign_up_bloc.dart | 7 +- lib/utils/helpers/calc_text_size.dart | 6 +- .../authentication/authentication.dart | 2 + .../authentication/email_password_auth.dart | 190 ++--- .../authentication/facebook_auth.dart | 150 ++-- .../services/authentication/google_auth.dart | 33 +- .../services/user_storage/user_storage.dart | 18 +- lib/widgets/elevated_button_container.dart | 4 +- lib/widgets/forgot_my_password.dart | 152 ++-- lib/widgets/login.dart | 190 +---- lib/widgets/mission_creation.dart | 8 +- lib/widgets/not_found_screen.dart | 6 +- lib/widgets/terminal.dart | 4 +- pubspec.lock | 774 ++++++++++++------ pubspec.yaml | 44 +- 37 files changed, 1176 insertions(+), 1050 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 android/app/google-services.json create mode 100644 assets/images/devicon_google.png create mode 100644 firebase.json create mode 100644 ios/RunnerTests/RunnerTests.swift diff --git a/.metadata b/.metadata index ec98142..6eb54a1 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,42 @@ # This file should be version controlled and should not be manually edited. version: - revision: d79295af24c3ed621c33713ecda14ad196fd9c31 - channel: stable + revision: "761747bfc538b5af34aa0d3fac380f1bc331ec49" + channel: "stable" project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: android + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: ios + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: linux + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: macos + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: web + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: windows + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + + # 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/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e0f15db --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic" +} \ No newline at end of file diff --git a/android/.project b/android/.project index 3964dd3..79bb12b 100644 --- a/android/.project +++ b/android/.project @@ -14,4 +14,15 @@ org.eclipse.buildship.core.gradleprojectnature + + + 1721135133511 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + diff --git a/android/.settings/org.eclipse.buildship.core.prefs b/android/.settings/org.eclipse.buildship.core.prefs index 2b6d83b..7c7a5b5 100644 --- a/android/.settings/org.eclipse.buildship.core.prefs +++ b/android/.settings/org.eclipse.buildship.core.prefs @@ -1,11 +1,11 @@ -arguments= -auto.sync=false +arguments=--init-script /var/folders/20/lrk1001d3v34wc_th8z_vgph0000gn/T/db3b08fc4a9ef609cb16b96b200fa13e563f396e9bb1ed0905fdab7bc3bc513b.gradle --init-script /var/folders/20/lrk1001d3v34wc_th8z_vgph0000gn/T/52cde0cfcf3e28b8b7510e992210d9614505e0911af0c190bd590d7158574963.gradle +auto.sync=true build.scans.enabled=false connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) connection.project.dir= eclipse.preferences.version=1 gradle.user.home= -java.home=/usr/lib/jvm/java-11-openjdk-amd64 +java.home=/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home jvm.arguments= offline.mode=false override.workspace.settings=true diff --git a/android/app/build.gradle b/android/app/build.gradle index bcaadfc..33afcee 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,3 +1,10 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" + id "com.google.gms.google-services" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +13,6 @@ def localProperties = new Properties() } } -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' @@ -21,74 +23,49 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -def mapsProperties = new Properties() - def localMapsPropertiesFile = rootProject.file('local_maps.properties') - if (localMapsPropertiesFile.exists()) { - project.logger.info('Load maps properties from local file') - localMapsPropertiesFile.withReader('UTF-8') { reader -> - mapsProperties.load(reader) - } - } else { - project.logger.info('Load maps properties from environment') - try { - mapsProperties['MAPS_API_KEY'] = System.getenv('MAPS_API_KEY') - } catch(NullPointerException e) { - project.logger.warn('Failed to load MAPS_API_KEY from environment.', e) - } - } -def mapsApiKey = mapsProperties.getProperty('MAPS_API_KEY') -if(mapsApiKey == null){ - mapsApiKey = "" - project.logger.error('Google Maps Api Key not configured. Set it in `local_maps.properties` or in the environment variable `MAPS_API_KEY`') -} - - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 31 + namespace 'com.zenithaerospace.monitor' + + compileSdkVersion 34 + ndkVersion "25.2.9519653" sourceSets { main.java.srcDirs += 'src/main/kotlin' } - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + defaultConfig { + applicationId "com.zenithaerospace.monitor" + minSdkVersion 23 + targetSdkVersion 31 + multiDexEnabled true + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName +// manifestPlaceholders = [MAPS_API_KEY: mapsApiKey] + } + buildTypes { + release { + // Configuração de assinatura para o build de release + // (Substitua com sua própria configuração de assinatura) + signingConfig signingConfigs.debug + } + } - applicationId "com.zenithaerospace.monitor" - minSdkVersion 21 - targetSdkVersion 31 - multiDexEnabled true - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - manifestPlaceholders = [MAPS_API_KEY: mapsApiKey] - } - - 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 - } - } - compileOptions { - sourceCompatibility 1.8 - targetCompatibility 1.8 - } + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } } flutter { - source '../..' + source '../..' } dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation platform('com.google.firebase:firebase-bom:28.4.1') - implementation 'com.google.firebase:firebase-auth' - implementation 'com.google.android.gms:play-services-auth:19.2.0' - implementation 'com.android.support:multidex:1.0.3' + implementation platform('com.google.firebase:firebase-bom:33.1.2') + implementation 'com.google.firebase:firebase-auth' + implementation 'com.google.android.gms:play-services-auth:19.2.0' + implementation 'com.android.support:multidex:1.0.3' } diff --git a/android/app/google-services.json b/android/app/google-services.json deleted file mode 100644 index 540495c..0000000 --- a/android/app/google-services.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "project_info": { - "project_number": "647188572266", - "firebase_url": "https://zenith-monitor-a0d89.firebaseio.com", - "project_id": "zenith-monitor-a0d89", - "storage_bucket": "zenith-monitor-a0d89.appspot.com" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:647188572266:android:32f5b010a7468bbf5c0213", - "android_client_info": { - "package_name": "com.zenithaerospace.monitor" - } - }, - "oauth_client": [ - { - "client_id": "647188572266-lgq11v9n583h40cp5kikmst94lpd0bbr.apps.googleusercontent.com", - "client_type": 1, - "android_info": { - "package_name": "com.zenithaerospace.monitor", - "certificate_hash": "91962c976e6dc576832709338bf58626c6de3fd9" - } - }, - { - "client_id": "647188572266-l4inuhtshf3p5cvo5srtao1r7tnvcdfd.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyAdFLNjRj6EtKgvuyeyZZb6lwmSneDCltw" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "647188572266-l4inuhtshf3p5cvo5srtao1r7tnvcdfd.apps.googleusercontent.com", - "client_type": 3 - } - ] - } - } - } - ], - "configuration_version": "1" -} \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 3d57536..4c22646 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,17 +1,3 @@ -buildscript { - ext.kotlin_version = '1.6.10' - repositories { - google() - jcenter() - } - - dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.google.gms:google-services:4.3.8' // Google Services' plugin - } -} - allprojects { repositories { google() @@ -21,10 +7,26 @@ allprojects { rootProject.buildDir = '../build' subprojects { + afterEvaluate { project -> + if (project.hasProperty('android')) { + project.android { + if (namespace == null) { + namespace project.group + } + // Avoid adding android.defaults.buildfeatures.buildconfig=true + // to your gradle.properties file because that property is deprecated in AGP 8.0 and is scheduled to be removed in AGP 9.0. + buildFeatures { + if (buildConfig == null) { + buildConfig true + } + } + } + } + } project.buildDir = "${rootProject.buildDir}/${project.name}" project.evaluationDependsOn(':app') } task clean(type: Delete) { delete rootProject.buildDir -} +} \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index bc6a58a..acb8e75 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Jun 23 08:50:38 CEST 2017 +#Tue Jul 16 09:50:51 BRT 2024 distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-all.zip distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip +zipStoreBase=GRADLE_USER_HOME \ No newline at end of file diff --git a/android/settings.gradle b/android/settings.gradle index 44e62bc..6c3a7a2 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,11 +1,26 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} -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" +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.0.2" apply false + id "org.jetbrains.kotlin.android" version "1.8.10" apply false + id "com.google.gms.google-services" version "4.4.2" apply false +} + +include ":app" \ No newline at end of file diff --git a/assets/images/devicon_google.png b/assets/images/devicon_google.png new file mode 100644 index 0000000000000000000000000000000000000000..ef466fda7f285d2e7b7ffb8f79dd09a035808bea GIT binary patch literal 841 zcmV-P1GfB$P)Jg1U6A8_O5Y3^C**f`p+45OG{Wp7C;2D^-aB(CfT%?c|u@qLQQd~lj2*3KA zbMtKbA4M?Fd^?6mIy(9a4Zow|Hy`8S`GrK={6e(AM3l}?xW#>IuyVakEqpqvS8h52 zcEjed`^uGA?mC@Rn8^BdQqe(v4)pxnMs;JR1$ch@QaD8s_rU>yn>zJ3hb8BSy{3Tm zs2w=1GNoX@{tgP6*$@-uUXq%&;MlDt*qA*r-aM8wy$lA@dW=^TcpsQDM~Jz573*xM z@6Aoan70MD#Y3u&3PFI7Ve}_b$tzNE6dW7~?aYg}=~coZfl)7-l_a-IL+zjtHvEuj zHQnSduE5NCt4bX{^kP)>#6Scs4a_ zLO}H2eW}qhw1<>7*!b^QtjJwKYn9${?|)G<%XB8YY6syi3iEyUCcA3^Dk5TP`#W zhrbSll)~=Yqv2->#^RIdgo(QbDy2nw5Bq)#FiW9X=XV}I z=o#H}gR+wdInm)iKRC^6m*BW{F*A9uC%qF=<)M-jSqnT4To9DXY-%p;=6pZ;G41jY z0**kC+aMZ30Exdas LoginBloc()), + BlocProvider(create: (context) => LoginBloc(auth: GoogleAuth())), BlocProvider( create: (context) => DataBloc(usbManager: usbManager, fireServices: fireServices)), @@ -77,7 +78,7 @@ class Application extends StatelessWidget { routes: { '/login': (context) => const LoginScreen(), '/signup': (context) => const SignUpScreen(), - '/forgotPwd': (context) => const ForgotMyPassword(), + // '/forgotPwd': (context) => const ForgotMyPassword(), '/map': (context) => const MapScreen(), '/configuration': (context) => ConfigurationScreen(), '/terminal': (context) => const TerminalScreen(), diff --git a/lib/modules/configuration/bloc/mission_controller/mission_variables_bloc.dart b/lib/modules/configuration/bloc/mission_controller/mission_variables_bloc.dart index a6720df..5a6da25 100644 --- a/lib/modules/configuration/bloc/mission_controller/mission_variables_bloc.dart +++ b/lib/modules/configuration/bloc/mission_controller/mission_variables_bloc.dart @@ -35,7 +35,7 @@ class MissionVariablesBloc (result == ConnectivityResult.none) ? false : true; add(ConnectionChanged()); - }); + } as void Function(List event)?); dataBloc.stream.listen((event) { if (event is UsbDisconnectedState) { @@ -48,7 +48,6 @@ class MissionVariablesBloc }); } - @override Stream mapEventToState( MissionVariablesEvent event) async* { if (event is AddStandardVariableEvent) { diff --git a/lib/modules/configuration/screen/configuration_screen.dart b/lib/modules/configuration/screen/configuration_screen.dart index 4225048..2266c63 100644 --- a/lib/modules/configuration/screen/configuration_screen.dart +++ b/lib/modules/configuration/screen/configuration_screen.dart @@ -66,7 +66,9 @@ class ConfigurationScreen extends StatelessWidget { (MissionVariablesBloc bloc) => bloc.connections); return FutureBuilder( - future: Connectivity().checkConnectivity(), + future: Connectivity() + .checkConnectivity() + .then((results) => results.first), builder: (BuildContext context, AsyncSnapshot snapshot) { diff --git a/lib/modules/forget_password/bloc/forgot_pwd_bloc.dart b/lib/modules/forget_password/bloc/forgot_pwd_bloc.dart index 0896e04..21ca8e7 100644 --- a/lib/modules/forget_password/bloc/forgot_pwd_bloc.dart +++ b/lib/modules/forget_password/bloc/forgot_pwd_bloc.dart @@ -1,29 +1,29 @@ -import 'package:bloc/bloc.dart'; -import 'package:zenith_monitor/utils/services/authentication/authentication_exceptions.dart'; -import 'package:zenith_monitor/utils/services/authentication/email_password_auth.dart'; +// import 'package:bloc/bloc.dart'; +// import 'package:zenith_monitor/utils/services/authentication/authentication_exceptions.dart'; +// // import 'package:zenith_monitor/utils/services/authentication/email_password_auth.dart'; -part 'forgot_pwd_state.dart'; -part 'forgot_pwd_event.dart'; +// part 'forgot_pwd_state.dart'; +// part 'forgot_pwd_event.dart'; -class ForgotPwdBloc extends Bloc { - ForgotPwdBloc() : super(ForgotPwdInitialState()); +// class ForgotPwdBloc extends Bloc { +// ForgotPwdBloc() : super(ForgotPwdInitialState()); - @override - Stream mapEventToState(ForgotPwdEvent event) async* { - if (event is PwdResetEmail) { - yield LoadingState(); - EmailAndPasswordAuth _auth = EmailAndPasswordAuth(); - try { - await _auth.resetPassword(event.email); - yield ForgotPwdSuccess(); - } on UserNotFound { - yield ForgotPwdError(errorMessage: "Usuário não encontrado"); - } on EmailBadlyFormatted { - yield ForgotPwdError( - errorMessage: "O email não está na formatação correta"); - } catch (e) { - print(e.toString()); - } - } - } -} +// @override +// Stream mapEventToState(ForgotPwdEvent event) async* { +// if (event is PwdResetEmail) { +// yield LoadingState(); +// EmailAndPasswordAuth _auth = EmailAndPasswordAuth(); +// try { +// await _auth.resetPassword(event.email); +// yield ForgotPwdSuccess(); +// } on UserNotFound { +// yield ForgotPwdError(errorMessage: "Usuário não encontrado"); +// } on EmailBadlyFormatted { +// yield ForgotPwdError( +// errorMessage: "O email não está na formatação correta"); +// } catch (e) { +// print(e.toString()); +// } +// } +// } +// } diff --git a/lib/modules/forget_password/screen/forgot_my_password_screen.dart b/lib/modules/forget_password/screen/forgot_my_password_screen.dart index 24ecdde..4bdb3a0 100644 --- a/lib/modules/forget_password/screen/forgot_my_password_screen.dart +++ b/lib/modules/forget_password/screen/forgot_my_password_screen.dart @@ -1,48 +1,48 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:zenith_monitor/constants/colors_constants.dart'; -import 'package:zenith_monitor/modules/forget_password/bloc/forgot_pwd_bloc.dart'; -import 'package:zenith_monitor/utils/ui/animations/zenith_progress_indicator.dart'; -import 'package:zenith_monitor/widgets/forgot_my_password.dart'; +// import 'package:flutter/material.dart'; +// import 'package:flutter_bloc/flutter_bloc.dart'; +// import 'package:zenith_monitor/constants/colors_constants.dart'; +// import 'package:zenith_monitor/modules/forget_password/bloc/forgot_pwd_bloc.dart'; +// import 'package:zenith_monitor/utils/ui/animations/zenith_progress_indicator.dart'; +// import 'package:zenith_monitor/widgets/forgot_my_password.dart'; -class ForgotMyPassword extends StatelessWidget { - const ForgotMyPassword(); +// class ForgotMyPassword extends StatelessWidget { +// const ForgotMyPassword(); - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (context) => ForgotPwdBloc(), - child: SafeArea( - child: Scaffold( - body: BlocBuilder( - builder: (context, state) { - String? statusMessage; - Color? messageColor; - if (state is LoadingState) { - return const ZenithProgressIndicator( - size: 100, fileName: "z_icon_white.png"); - } - if (state is ForgotPwdSuccess) { - statusMessage = - "Um email para recuperação de senha foi enviado com sucesso"; - messageColor = mantisGreen; - } +// @override +// Widget build(BuildContext context) { +// return BlocProvider( +// create: (context) => ForgotPwdBloc(), +// child: SafeArea( +// child: Scaffold( +// body: BlocBuilder( +// builder: (context, state) { +// String? statusMessage; +// Color? messageColor; +// if (state is LoadingState) { +// return const ZenithProgressIndicator( +// size: 100, fileName: "z_icon_white.png"); +// } +// if (state is ForgotPwdSuccess) { +// statusMessage = +// "Um email para recuperação de senha foi enviado com sucesso"; +// messageColor = mantisGreen; +// } - if (state is ForgotPwdError) { - statusMessage = state.errorMessage; - messageColor = lightCoral; - } - return ForgotMyPasswordBody( - screenWidth: MediaQuery.of(context).size.width, - screenHeight: MediaQuery.of(context).size.height, - deviceOrientation: MediaQuery.of(context).orientation, - statusMessage: statusMessage, - messageColor: messageColor, - ); - }, - ), - backgroundColor: eerieBlack, - )), - ); - } -} +// if (state is ForgotPwdError) { +// statusMessage = state.errorMessage; +// messageColor = lightCoral; +// } +// return ForgotMyPasswordBody( +// screenWidth: MediaQuery.of(context).size.width, +// screenHeight: MediaQuery.of(context).size.height, +// deviceOrientation: MediaQuery.of(context).orientation, +// statusMessage: statusMessage, +// messageColor: messageColor, +// ); +// }, +// ), +// backgroundColor: eerieBlack, +// )), +// ); +// } +// } diff --git a/lib/modules/login/bloc/login_bloc.dart b/lib/modules/login/bloc/login_bloc.dart index f14e08a..9b54350 100644 --- a/lib/modules/login/bloc/login_bloc.dart +++ b/lib/modules/login/bloc/login_bloc.dart @@ -2,73 +2,43 @@ import 'package:bloc/bloc.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/material.dart'; import 'package:zenith_monitor/modules/login/screen/login_screen.dart'; -import 'package:zenith_monitor/utils/mixins/class_local_user.dart'; import 'package:zenith_monitor/utils/services/authentication/authentication.dart'; -import 'package:zenith_monitor/utils/services/authentication/email_password_auth.dart'; -import 'package:zenith_monitor/utils/services/authentication/authentication_exceptions.dart'; -import 'package:zenith_monitor/utils/services/authentication/facebook_auth.dart'; import 'package:zenith_monitor/utils/services/authentication/google_auth.dart'; import 'package:zenith_monitor/utils/services/user_firestore/user_document.dart'; -import 'package:zenith_monitor/utils/services/user_firestore/user_document_exceptions.dart'; -import 'package:zenith_monitor/utils/services/user_storage/user_storage.dart'; +import 'package:zenith_monitor/utils/mixins/class_local_user.dart'; part 'login_state.dart'; part 'login_event.dart'; class LoginBloc extends Bloc { - LoginBloc() : super(LoginInitialState()); + late final Authentication _auth; - late LocalUser _user; - late Authentication _auth; + LoginBloc({required Authentication auth}) + : _auth = auth, + super(LoginInitialState()) { + on(_onGoogleLogin); + on(_onSignOut); + } - @override - Stream mapEventToState(LoginEvent event) async* { - if (event is AuthenticationEvent) { - yield LoadingState(); - try { - await event.loginCall(); - _user = await event.getUser(); - _auth = event.auth; - yield LoginSuccess(_user); - } on WrongPassword { - yield LoginError(errorMessage: "Senha errada"); - } on UserNotFound { - yield LoginError(errorMessage: "Usuário não encontrado"); - } on EmailBadlyFormatted { - yield LoginError( - errorMessage: "O email não está na formatação correta"); - } on NullUser { - yield LoginError( - errorMessage: - "Algum problema ocorreu durante a autenticação do usuário"); - } on EmailNotVerified { - yield LoginError( - errorMessage: "O email do usuário ainda não foi autenticado"); - } on UserFileNotFound { - yield LoginError( - errorMessage: - "Os dados do usuário não foram encontrados. Por favor, forneça-os novamente"); - //apresentar janela para fornecimento dos dados - } on AnotherCredentialUsed { - yield LoginError( - errorMessage: - "O email associado a este método de autenticação já foi utilizado em outro método. Por favor, utilize outro método."); - } on FirebaseProblem catch (e) { - print(e.errorType()); - yield LoginError( - errorMessage: - "Um problema ocorreu durante a utilização do banco de dados. Verifique se o email fornecido foi verificado."); - } catch (e) { - print(e.toString()); - yield LoginError(errorMessage: "Erro desconhecido"); - } - } else if (event is SignOutEvent) { - await _auth.signOut(); - Navigator.pushAndRemoveUntil( - event.context, - MaterialPageRoute( - builder: (BuildContext context) => const LoginScreen()), - ModalRoute.withName('/login')); + Future _onGoogleLogin( + GoogleLoginEvent event, Emitter emit) async { + emit(LoadingState()); + try { + await event.loginCall(); + LocalUser user = await event.getUser(); + emit(LoginSuccess(user)); + } catch (e) { + emit(LoginError(errorMessage: "Erro ao tentar fazer login: $e")); } } + + Future _onSignOut(SignOutEvent event, Emitter emit) async { + await _auth.signOut(); + Navigator.pushAndRemoveUntil( + // ignore: use_build_context_synchronously + event.context, + MaterialPageRoute(builder: (BuildContext context) => const LoginScreen()), + ModalRoute.withName('/login'), + ); + } } diff --git a/lib/modules/login/bloc/login_event.dart b/lib/modules/login/bloc/login_event.dart index ad2f0b3..919ed78 100644 --- a/lib/modules/login/bloc/login_event.dart +++ b/lib/modules/login/bloc/login_event.dart @@ -3,7 +3,7 @@ part of 'login_bloc.dart'; abstract class LoginEvent {} abstract class AuthenticationEvent extends LoginEvent { - Authentication auth; + final Authentication auth; AuthenticationEvent({required this.auth}); @@ -11,37 +11,12 @@ abstract class AuthenticationEvent extends LoginEvent { Future getUser(); } -class EmailLoginEvent extends AuthenticationEvent { - String email; - String password; - - EmailLoginEvent({required this.email, required this.password}) - : super(auth: EmailAndPasswordAuth()); - - @override - Future loginCall() async { - await EmailAndPasswordAuth().signIn(email, password); - } - - @override - Future getUser() async { - UserDocument userDocument = UserDocument(authMethod: auth); - DocumentSnapshot? userDocSnap = - await userDocument.getUserFirebaseDocument(); - if (userDocSnap == null) { - await UserStorage().uploadImage(); - } - - return await userDocument.getUserFirestore(userDocSnap); - } -} - class GoogleLoginEvent extends AuthenticationEvent { GoogleLoginEvent() : super(auth: GoogleAuth()); @override Future loginCall() async { - await GoogleAuth().signInwithGoogle(); + await auth.signInWithGoogle(); } @override @@ -53,20 +28,8 @@ class GoogleLoginEvent extends AuthenticationEvent { } } -class FacebookLoginEvent extends AuthenticationEvent { - FacebookLoginEvent() : super(auth: FacebookAuth()); - @override - Future loginCall() async { - await FacebookAuth().signInWithFacebook(); - } - - @override - Future getUser() async { - return await auth.getUserAuthentication(); - } -} - class SignOutEvent extends LoginEvent { - BuildContext context; + final BuildContext context; + SignOutEvent({required this.context}); } diff --git a/lib/modules/login/bloc/login_state.dart b/lib/modules/login/bloc/login_state.dart index c9d8df8..fe9d271 100644 --- a/lib/modules/login/bloc/login_state.dart +++ b/lib/modules/login/bloc/login_state.dart @@ -1,13 +1,15 @@ part of 'login_bloc.dart'; abstract class LoginState { - LocalUser? user; + late final LocalUser? user; + + LoginState({this.user}); } class LoginInitialState extends LoginState {} class LoginError extends LoginState { - String errorMessage; + final String errorMessage; LoginError({required this.errorMessage}); } @@ -15,7 +17,5 @@ class LoginError extends LoginState { class LoadingState extends LoginState {} class LoginSuccess extends LoginState { - LoginSuccess(LocalUser newUser) { - user = newUser; - } + LoginSuccess(LocalUser newUser) : super(user: newUser); } diff --git a/lib/modules/login/screen/login_screen.dart b/lib/modules/login/screen/login_screen.dart index 878ebaa..3322976 100644 --- a/lib/modules/login/screen/login_screen.dart +++ b/lib/modules/login/screen/login_screen.dart @@ -7,5 +7,26 @@ class LoginScreen extends StatelessWidget { @override Widget build(BuildContext context) { return const LoginWidget(); + // final ButtonStyle style = + // ElevatedButton.styleFrom(textStyle: const TextStyle(fontSize: 20)); + // return Center( + // child: Column( + // mainAxisSize: MainAxisSize.min, + // children: [ + // ElevatedButton( + // style: style, + // onPressed: () {}, + // child: Row( + // mainAxisSize: MainAxisSize.min, + // children: [ + // Image.asset('assets/images/devicon_google.png', height: 20), + // const SizedBox(width: 10), + // const Text('Entrar com o Google'), + // ], + // ), + // ), + // ], + // ), + // ); } } diff --git a/lib/modules/signup/bloc/sign_up_bloc.dart b/lib/modules/signup/bloc/sign_up_bloc.dart index 0a582f2..6bb9d5f 100644 --- a/lib/modules/signup/bloc/sign_up_bloc.dart +++ b/lib/modules/signup/bloc/sign_up_bloc.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:bloc/bloc.dart'; import 'package:image_picker/image_picker.dart'; import 'package:zenith_monitor/utils/mixins/class_local_user.dart'; -import 'package:zenith_monitor/utils/services/authentication/email_password_auth.dart'; +// import 'package:zenith_monitor/utils/services/authentication/email_password_auth.dart'; import 'package:zenith_monitor/utils/services/authentication/authentication_exceptions.dart'; import 'package:zenith_monitor/utils/services/user_storage/user_storage.dart'; @@ -13,11 +13,10 @@ part 'sign_up_event.dart'; class SignUpBloc extends Bloc { SignUpBloc() : super(SignUpInitial()); - EmailAndPasswordAuth auth = EmailAndPasswordAuth(); + // EmailAndPasswordAuth auth = EmailAndPasswordAuth(); UserStorage storage = UserStorage(); File? profileImage; - @override Stream mapEventToState(SignUpEvent event) async* { if (event is UserRegisterEvent) { yield LoadingState(); @@ -29,7 +28,7 @@ class SignUpBloc extends Bloc { if (event.password != event.pwdConfirmation) { yield SignUpError(errorMessage: "As senhas não batem"); } else { - await auth.register(event.newUser, event.password); + // await auth.register(event.newUser, event.password); yield SuccessfulSignup(); } } on WeakPassword { diff --git a/lib/utils/helpers/calc_text_size.dart b/lib/utils/helpers/calc_text_size.dart index 3599a55..634b7b3 100644 --- a/lib/utils/helpers/calc_text_size.dart +++ b/lib/utils/helpers/calc_text_size.dart @@ -1,11 +1,15 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; /// The function calcTextSize is used to get the size of a string based on its TextStyle Size calcTextSize(String text, TextStyle style) { + // TextScaler textScaler = TextScaler(WidgetsBinding.instance!.window.scale); + final TextPainter textPainter = TextPainter( text: TextSpan(text: text, style: style), textDirection: TextDirection.ltr, - textScaleFactor: WidgetsBinding.instance!.window.textScaleFactor, + textScaler: TextScaler.noScaling, )..layout(); return textPainter.size; } diff --git a/lib/utils/services/authentication/authentication.dart b/lib/utils/services/authentication/authentication.dart index 5905bb4..65ad341 100644 --- a/lib/utils/services/authentication/authentication.dart +++ b/lib/utils/services/authentication/authentication.dart @@ -1,9 +1,11 @@ import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_auth/firebase_auth.dart'; import 'package:zenith_monitor/utils/mixins/class_local_user.dart'; abstract class Authentication { String type = ""; + Future signInWithGoogle(); Future getUserAuthentication(); Future userCreationConditions(DocumentSnapshot? userDoc); diff --git a/lib/utils/services/authentication/email_password_auth.dart b/lib/utils/services/authentication/email_password_auth.dart index 51f96e5..8c51965 100644 --- a/lib/utils/services/authentication/email_password_auth.dart +++ b/lib/utils/services/authentication/email_password_auth.dart @@ -1,107 +1,107 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:zenith_monitor/utils/mixins/class_local_user.dart'; -import 'package:zenith_monitor/utils/mixins/class_user_file.dart'; -import 'package:zenith_monitor/utils/services/authentication/authentication.dart'; -import 'package:zenith_monitor/utils/services/authentication/authentication_exceptions.dart'; -import 'package:zenith_monitor/utils/services/user_firestore/user_document_exceptions.dart'; +// import 'package:cloud_firestore/cloud_firestore.dart'; +// import 'package:firebase_auth/firebase_auth.dart'; +// import 'package:zenith_monitor/utils/mixins/class_local_user.dart'; +// import 'package:zenith_monitor/utils/mixins/class_user_file.dart'; +// import 'package:zenith_monitor/utils/services/authentication/authentication.dart'; +// import 'package:zenith_monitor/utils/services/authentication/authentication_exceptions.dart'; +// import 'package:zenith_monitor/utils/services/user_firestore/user_document_exceptions.dart'; -class EmailAndPasswordAuth extends Authentication { - final FirebaseAuth _auth = FirebaseAuth.instance; - final UserFile userFile = UserFile(); +// class EmailAndPasswordAuth extends Authentication { +// final FirebaseAuth _auth = FirebaseAuth.instance; +// final UserFile userFile = UserFile(); - EmailAndPasswordAuth() { - type = "Email and Password"; - } +// EmailAndPasswordAuth() { +// type = "Email and Password"; +// } - Future register(LocalUser newUser, String password) async { - try { - await _auth.createUserWithEmailAndPassword( - email: newUser.getEmail(), password: password); - User? firebaseUser = _auth.currentUser; +// Future register(LocalUser newUser, String password) async { +// try { +// await _auth.createUserWithEmailAndPassword( +// email: newUser.getEmail(), password: password); +// User? firebaseUser = _auth.currentUser; - if (firebaseUser != null) { - if (!firebaseUser.emailVerified) { - await firebaseUser.sendEmailVerification(); - } +// if (firebaseUser != null) { +// if (!firebaseUser.emailVerified) { +// await firebaseUser.sendEmailVerification(); +// } - firebaseUser.updateDisplayName(newUser.getCompleteName()); - firebaseUser.updateEmail(newUser.getEmail()); - firebaseUser.updatePhotoURL(newUser.getImageLink()); - userFile.writeUser(newUser); - _auth.signOut(); - } - } on FirebaseAuthException catch (e) { - if (e.code == 'weak-password') { - throw WeakPassword(); - } else if (e.code == 'email-already-in-use') { - throw EmailAlreadyInUse(); - } else if (e.code == "invalid-email") { - throw EmailBadlyFormatted(); - } else if (e.code == "invalid-email-verified") { - print("Erro invalid-email-verified"); - } - throw FirebaseProblem(isFirebaseException: true, errorMsg: e.toString()); - } catch (e) { - throw FirebaseProblem(isFirebaseException: false, errorMsg: e.toString()); - } - } +// firebaseUser.updateDisplayName(newUser.getCompleteName()); +// firebaseUser.updateEmail(newUser.getEmail()); +// firebaseUser.updatePhotoURL(newUser.getImageLink()); +// userFile.writeUser(newUser); +// _auth.signOut(); +// } +// } on FirebaseAuthException catch (e) { +// if (e.code == 'weak-password') { +// throw WeakPassword(); +// } else if (e.code == 'email-already-in-use') { +// throw EmailAlreadyInUse(); +// } else if (e.code == "invalid-email") { +// throw EmailBadlyFormatted(); +// } else if (e.code == "invalid-email-verified") { +// print("Erro invalid-email-verified"); +// } +// throw FirebaseProblem(isFirebaseException: true, errorMsg: e.toString()); +// } catch (e) { +// throw FirebaseProblem(isFirebaseException: false, errorMsg: e.toString()); +// } +// } - Future signIn(String email, String password) async { - try { - await _auth.signInWithEmailAndPassword(email: email, password: password); - } on FirebaseAuthException catch (e) { - if (e.code == "wrong-password") { - throw WrongPassword(); - } else if (e.code == "user-not-found") { - throw UserNotFound(); - } else if (e.code == "invalid-email") { - throw EmailBadlyFormatted(); - } - throw FirebaseProblem(isFirebaseException: true, errorMsg: e.toString()); - } catch (e) { - throw FirebaseProblem(isFirebaseException: false, errorMsg: e.toString()); - } - } +// Future signIn(String email, String password) async { +// try { +// await _auth.signInWithEmailAndPassword(email: email, password: password); +// } on FirebaseAuthException catch (e) { +// if (e.code == "wrong-password") { +// throw WrongPassword(); +// } else if (e.code == "user-not-found") { +// throw UserNotFound(); +// } else if (e.code == "invalid-email") { +// throw EmailBadlyFormatted(); +// } +// throw FirebaseProblem(isFirebaseException: true, errorMsg: e.toString()); +// } catch (e) { +// throw FirebaseProblem(isFirebaseException: false, errorMsg: e.toString()); +// } +// } - Future resetPassword(String email) async { - try { - await _auth.sendPasswordResetEmail(email: email); - } on FirebaseAuthException catch (e) { - if (e.code == "user-not-found") { - throw UserNotFound(); - } else if (e.code == "invalid-email") { - throw EmailBadlyFormatted(); - } - throw FirebaseProblem(isFirebaseException: true, errorMsg: e.toString()); - } catch (e) { - throw FirebaseProblem(isFirebaseException: false, errorMsg: e.toString()); - } - } +// Future resetPassword(String email) async { +// try { +// await _auth.sendPasswordResetEmail(email: email); +// } on FirebaseAuthException catch (e) { +// if (e.code == "user-not-found") { +// throw UserNotFound(); +// } else if (e.code == "invalid-email") { +// throw EmailBadlyFormatted(); +// } +// throw FirebaseProblem(isFirebaseException: true, errorMsg: e.toString()); +// } catch (e) { +// throw FirebaseProblem(isFirebaseException: false, errorMsg: e.toString()); +// } +// } - @override - Future userCreationConditions(DocumentSnapshot? userDoc) async { - if (userDoc == null || - !(userDoc.exists) || - userDoc.get('created_with') != "Email and Password") { - LocalUser newUser = await getUserAuthentication(); +// @override +// Future userCreationConditions(DocumentSnapshot? userDoc) async { +// if (userDoc == null || +// !(userDoc.exists) || +// userDoc.get('created_with') != "Email and Password") { +// LocalUser newUser = await getUserAuthentication(); - return newUser; - } +// return newUser; +// } - return null; - } +// return null; +// } - @override - Future getUserAuthentication() async { - UserFile file = UserFile(); - LocalUser? newUser = await file.readUser(); - if (newUser == null) throw UserFileNotFound(); - return newUser; - } +// @override +// Future getUserAuthentication() async { +// UserFile file = UserFile(); +// LocalUser? newUser = await file.readUser(); +// if (newUser == null) throw UserFileNotFound(); +// return newUser; +// } - @override - Future signOut() async { - await _auth.signOut(); - } -} +// @override +// Future signOut() async { +// await _auth.signOut(); +// } +// } diff --git a/lib/utils/services/authentication/facebook_auth.dart b/lib/utils/services/authentication/facebook_auth.dart index e3537e1..ca72b42 100644 --- a/lib/utils/services/authentication/facebook_auth.dart +++ b/lib/utils/services/authentication/facebook_auth.dart @@ -1,75 +1,75 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter_facebook_auth/flutter_facebook_auth.dart' - as facebook_auth_method; -import 'package:zenith_monitor/utils/mixins/class_local_user.dart'; -import 'package:zenith_monitor/utils/services/user_firestore/user_document_exceptions.dart'; -import 'package:zenith_monitor/utils/services/authentication/authentication_exceptions.dart'; -import 'authentication.dart'; - -/// Facebook's authentication has the problem that the current User has -/// its verified email property set to false. That's why it was chosen -/// not to use the user provided by Facebook as a way to write Firebase -/// documents. - -class FacebookAuth extends Authentication { - final FirebaseAuth _auth = FirebaseAuth.instance; - final facebook_auth_method.FacebookAuth _facebookAuth = - facebook_auth_method.FacebookAuth.instance; - - FacebookAuth() { - type = "Facebook"; - } - - Future signInWithFacebook() async { - try { - final facebook_auth_method.LoginResult result = - await _facebookAuth.login(); - - if (result.status == facebook_auth_method.LoginStatus.success) { - final AuthCredential facebookCredential = - FacebookAuthProvider.credential(result.accessToken!.token); - await _auth.signInWithCredential(facebookCredential); - } else if (result.status == facebook_auth_method.LoginStatus.cancelled) { - print(result.message); - } else if (result.status == facebook_auth_method.LoginStatus.failed) { - print(result.message); - } - } on FirebaseAuthException catch (e) { - if (e.code == "account-exists-with-different-credential") { - throw AnotherCredentialUsed(); - } - - throw FirebaseProblem(isFirebaseException: true, errorMsg: e.toString()); - } catch (e) { - print(e.toString()); - throw FirebaseProblem(isFirebaseException: false, errorMsg: e.toString()); - } - } - - @override - Future signOut() async { - await _facebookAuth.logOut(); - await _auth.signOut(); - } - - /// This method is not even necessary here, - /// it's used in Google and EmailAndPassword - /// authentication only. - @override - Future userCreationConditions(DocumentSnapshot? userDoc) async { - return null; - } - - @override - Future getUserAuthentication() async { - if (_auth.currentUser == null) throw NullUser(); - - final Map userData = await _facebookAuth.getUserData(); - - LocalUser user = LocalUser(userData["name"], "", userData["email"], - imageLink: userData["picture"]["data"]["url"]); - user.setAccessLevel("Normal User"); - return user; - } -} +// import 'package:cloud_firestore/cloud_firestore.dart'; +// import 'package:firebase_auth/firebase_auth.dart'; +// import 'package:flutter_facebook_auth/flutter_facebook_auth.dart' +// as facebook_auth_method; +// import 'package:zenith_monitor/utils/mixins/class_local_user.dart'; +// import 'package:zenith_monitor/utils/services/user_firestore/user_document_exceptions.dart'; +// import 'package:zenith_monitor/utils/services/authentication/authentication_exceptions.dart'; +// import 'authentication.dart'; +// +// /// Facebook's authentication has the problem that the current User has +// /// its verified email property set to false. That's why it was chosen +// /// not to use the user provided by Facebook as a way to write Firebase +// /// documents. +// +// class FacebookAuth extends Authentication { +// final FirebaseAuth _auth = FirebaseAuth.instance; +// final facebook_auth_method.FacebookAuth _facebookAuth = +// facebook_auth_method.FacebookAuth.instance; +// +// FacebookAuth() { +// type = "Facebook"; +// } +// +// Future signInWithFacebook() async { +// try { +// final facebook_auth_method.LoginResult result = +// await _facebookAuth.login(); +// +// if (result.status == facebook_auth_method.LoginStatus.success) { +// final AuthCredential facebookCredential = +// FacebookAuthProvider.credential(result.accessToken!.token); +// await _auth.signInWithCredential(facebookCredential); +// } else if (result.status == facebook_auth_method.LoginStatus.cancelled) { +// print(result.message); +// } else if (result.status == facebook_auth_method.LoginStatus.failed) { +// print(result.message); +// } +// } on FirebaseAuthException catch (e) { +// if (e.code == "account-exists-with-different-credential") { +// throw AnotherCredentialUsed(); +// } +// +// throw FirebaseProblem(isFirebaseException: true, errorMsg: e.toString()); +// } catch (e) { +// print(e.toString()); +// throw FirebaseProblem(isFirebaseException: false, errorMsg: e.toString()); +// } +// } +// +// @override +// Future signOut() async { +// await _facebookAuth.logOut(); +// await _auth.signOut(); +// } +// +// /// This method is not even necessary here, +// /// it's used in Google and EmailAndPassword +// /// authentication only. +// @override +// Future userCreationConditions(DocumentSnapshot? userDoc) async { +// return null; +// } +// +// @override +// Future getUserAuthentication() async { +// if (_auth.currentUser == null) throw NullUser(); +// +// final Map userData = await _facebookAuth.getUserData(); +// +// LocalUser user = LocalUser(userData["name"], "", userData["email"], +// imageLink: userData["picture"]["data"]["url"]); +// user.setAccessLevel("Normal User"); +// return user; +// } +// } diff --git a/lib/utils/services/authentication/google_auth.dart b/lib/utils/services/authentication/google_auth.dart index 61eac86..fad548a 100644 --- a/lib/utils/services/authentication/google_auth.dart +++ b/lib/utils/services/authentication/google_auth.dart @@ -13,24 +13,23 @@ class GoogleAuth extends Authentication { type = "Google"; } - Future signInwithGoogle() async { - try { - final GoogleSignInAccount? googleSignInAccount = - await _googleSignIn.signIn(); - final GoogleSignInAuthentication googleSignInAuthentication = - await googleSignInAccount!.authentication; - final AuthCredential credential = GoogleAuthProvider.credential( - accessToken: googleSignInAuthentication.accessToken, - idToken: googleSignInAuthentication.idToken, - ); + @override + Future signInWithGoogle() async { + // Trigger the authentication flow + final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn(); - await _auth.signInWithCredential(credential); - } on FirebaseAuthException catch (e) { - throw FirebaseProblem(isFirebaseException: true, errorMsg: e.toString()); - } catch (e) { - print(e.toString()); - throw FirebaseProblem(isFirebaseException: false, errorMsg: e.toString()); - } + // Obtain the auth details from the request + final GoogleSignInAuthentication? googleAuth = + await googleUser?.authentication; + + // Create a new credential + final credential = GoogleAuthProvider.credential( + accessToken: googleAuth?.accessToken, + idToken: googleAuth?.idToken, + ); + + // Once signed in, return the UserCredential + return await FirebaseAuth.instance.signInWithCredential(credential); } @override diff --git a/lib/utils/services/user_storage/user_storage.dart b/lib/utils/services/user_storage/user_storage.dart index 9596c13..9b5b8a1 100644 --- a/lib/utils/services/user_storage/user_storage.dart +++ b/lib/utils/services/user_storage/user_storage.dart @@ -1,12 +1,10 @@ import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_storage/firebase_storage.dart'; -import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:image_cropper/image_cropper.dart'; import 'package:image_picker/image_picker.dart'; import 'dart:io'; -import 'package:zenith_monitor/constants/colors_constants.dart'; import 'package:zenith_monitor/utils/mixins/class_local_user.dart'; import 'package:zenith_monitor/utils/mixins/class_user_file.dart'; import 'package:zenith_monitor/utils/services/user_firestore/user_document_exceptions.dart'; @@ -64,19 +62,19 @@ class UserStorage { Future cropImage(File image) async { try { - File? cropped = await ImageCropper().cropImage( + File? cropped = (await ImageCropper().cropImage( sourcePath: image.path, - cropStyle: CropStyle.circle, + // cropStyle: CropStyle.circle, aspectRatio: const CropAspectRatio(ratioX: 1, ratioY: 1), compressQuality: 100, maxHeight: 700, maxWidth: 700, - androidUiSettings: const AndroidUiSettings( - toolbarColor: Colors.black, - toolbarWidgetColor: white, - hideBottomControls: true, - ), - ); + // androidUiSettings: const AndroidUiSettings( + // toolbarColor: Colors.black, + // toolbarWidgetColor: white, + // hideBottomControls: true, + // ), + )) as File?; return cropped; } catch (e) { print(e.toString()); diff --git a/lib/widgets/elevated_button_container.dart b/lib/widgets/elevated_button_container.dart index 2db420f..e8b7f79 100644 --- a/lib/widgets/elevated_button_container.dart +++ b/lib/widgets/elevated_button_container.dart @@ -35,8 +35,8 @@ class ElevatedButtonContainer extends StatelessWidget { )), onPressed: buttonFunction, style: ElevatedButton.styleFrom( - primary: buttonColor, - onPrimary: Colors.grey, + // primary: buttonColor, + // onPrimary: Colors.grey, shape: RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(borderRadius))), diff --git a/lib/widgets/forgot_my_password.dart b/lib/widgets/forgot_my_password.dart index 217a183..4b3a2c5 100644 --- a/lib/widgets/forgot_my_password.dart +++ b/lib/widgets/forgot_my_password.dart @@ -1,85 +1,85 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:zenith_monitor/constants/colors_constants.dart'; -import 'package:zenith_monitor/modules/forget_password/bloc/forgot_pwd_bloc.dart'; -import 'package:zenith_monitor/widgets/status_message.dart'; -import 'package:zenith_monitor/widgets/forgot_my_password_email_buttons_section.dart'; -import 'package:zenith_monitor/widgets/forgot_my_password_title_section.dart'; +// import 'package:flutter/material.dart'; +// import 'package:flutter_bloc/flutter_bloc.dart'; +// import 'package:zenith_monitor/constants/colors_constants.dart'; +// import 'package:zenith_monitor/modules/forget_password/bloc/forgot_pwd_bloc.dart'; +// import 'package:zenith_monitor/widgets/status_message.dart'; +// import 'package:zenith_monitor/widgets/forgot_my_password_email_buttons_section.dart'; +// import 'package:zenith_monitor/widgets/forgot_my_password_title_section.dart'; -class ForgotMyPasswordBody extends StatefulWidget { - final double screenWidth; - final double screenHeight; - final Orientation deviceOrientation; - final String? statusMessage; - final Color? messageColor; +// class ForgotMyPasswordBody extends StatefulWidget { +// final double screenWidth; +// final double screenHeight; +// final Orientation deviceOrientation; +// final String? statusMessage; +// final Color? messageColor; - const ForgotMyPasswordBody( - {required this.screenWidth, - required this.screenHeight, - required this.deviceOrientation, - required this.statusMessage, - required this.messageColor}); +// const ForgotMyPasswordBody( +// {required this.screenWidth, +// required this.screenHeight, +// required this.deviceOrientation, +// required this.statusMessage, +// required this.messageColor}); - @override - _FMyPWDBodyState createState() => _FMyPWDBodyState(); -} +// @override +// _FMyPWDBodyState createState() => _FMyPWDBodyState(); +// } -class _FMyPWDBodyState extends State { - late TextEditingController emailController; - @override - void initState() { - super.initState(); +// class _FMyPWDBodyState extends State { +// late TextEditingController emailController; +// @override +// void initState() { +// super.initState(); - emailController = TextEditingController(); - } +// emailController = TextEditingController(); +// } - @override - void dispose() { - super.dispose(); +// @override +// void dispose() { +// super.dispose(); - emailController.dispose(); - } +// emailController.dispose(); +// } - @override - Widget build(BuildContext context) { - return SingleChildScrollView( - child: Column(mainAxisAlignment: MainAxisAlignment.start, children: [ - TitleSection( - screenWidth: widget.screenWidth, - screenHeight: widget.screenHeight, - deviceOrientation: widget.deviceOrientation), - Container( - height: 0.50 * widget.screenHeight, - margin: EdgeInsets.only(top: 0.27 * widget.screenHeight), - decoration: BoxDecoration( - gradient: const LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - raisingBlack, - black, - ]), - borderRadius: BorderRadius.only( - topLeft: Radius.circular(_boderRadius()), - topRight: Radius.circular(_boderRadius()))), - child: EmailButtonsSection( - screenWidth: widget.screenWidth, - screenHeight: widget.screenHeight, - deviceOrientation: widget.deviceOrientation, - emailController: emailController, - funcSubmit: () => BlocProvider.of(context) - .add(PwdResetEmail(email: emailController.text.trim())), - statusMessage: StatusMessage( - message: widget.statusMessage, - color: widget.messageColor, - ), - ), - alignment: Alignment.center) - ])); - } +// @override +// Widget build(BuildContext context) { +// return SingleChildScrollView( +// child: Column(mainAxisAlignment: MainAxisAlignment.start, children: [ +// TitleSection( +// screenWidth: widget.screenWidth, +// screenHeight: widget.screenHeight, +// deviceOrientation: widget.deviceOrientation), +// Container( +// height: 0.50 * widget.screenHeight, +// margin: EdgeInsets.only(top: 0.27 * widget.screenHeight), +// decoration: BoxDecoration( +// gradient: const LinearGradient( +// begin: Alignment.topCenter, +// end: Alignment.bottomCenter, +// colors: [ +// raisingBlack, +// black, +// ]), +// borderRadius: BorderRadius.only( +// topLeft: Radius.circular(_boderRadius()), +// topRight: Radius.circular(_boderRadius()))), +// child: EmailButtonsSection( +// screenWidth: widget.screenWidth, +// screenHeight: widget.screenHeight, +// deviceOrientation: widget.deviceOrientation, +// emailController: emailController, +// funcSubmit: () => BlocProvider.of(context) +// .add(PwdResetEmail(email: emailController.text.trim())), +// statusMessage: StatusMessage( +// message: widget.statusMessage, +// color: widget.messageColor, +// ), +// ), +// alignment: Alignment.center) +// ])); +// } - double _boderRadius() { - return widget.screenWidth * - ((widget.deviceOrientation == Orientation.portrait) ? 0.14 : 0.07); - } -} +// double _boderRadius() { +// return widget.screenWidth * +// ((widget.deviceOrientation == Orientation.portrait) ? 0.14 : 0.07); +// } +// } diff --git a/lib/widgets/login.dart b/lib/widgets/login.dart index bd7c3bc..fc97289 100644 --- a/lib/widgets/login.dart +++ b/lib/widgets/login.dart @@ -10,16 +10,14 @@ class LoginWidget extends StatefulWidget { const LoginWidget(); @override - _LoginWidgetState createState() => _LoginWidgetState(); + LoginWidgetState createState() => LoginWidgetState(); } -class _LoginWidgetState extends State { - TextEditingController emailController = TextEditingController(); - TextEditingController passwordController = TextEditingController(); +class LoginWidgetState extends State { + // final ButtonStyle style = + // ElevatedButton.styleFrom(textStyle: const TextStyle(fontSize: 20)); - late rive.RiveAnimationController _githubLoginController; - late rive.RiveAnimationController _facebookLoginController; - late rive.RiveAnimationController _googleLoginController; + // late rive.RiveAnimationController _googleLoginController; void _toggleAnimation(rive.RiveAnimationController controller) { if (controller.isActive == false) { @@ -30,19 +28,10 @@ class _LoginWidgetState extends State { @override void initState() { super.initState(); - _githubLoginController = rive.OneShotAnimation( - 'changeColors', - autoplay: false, - ); - - _facebookLoginController = rive.OneShotAnimation( - 'changeColors', - autoplay: false, - ); - _googleLoginController = rive.OneShotAnimation( - 'changeColors', - autoplay: false, - ); + // _googleLoginController = rive.OneShotAnimation( + // 'changeColors', + // autoplay: false, + // ); } @override @@ -80,10 +69,12 @@ class _LoginWidgetState extends State { BoxConstraints(maxHeight: MediaQuery.of(context).size.height), child: Container( decoration: const BoxDecoration( - color: eerieBlack, - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(40.0), - bottomRight: Radius.circular(40.0))), + color: eerieBlack, + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(40.0), + bottomRight: Radius.circular(40.0), + ), + ), width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height * ((MediaQuery.of(context).orientation == Orientation.portrait) @@ -99,10 +90,8 @@ class _LoginWidgetState extends State { child: const rive.RiveAnimation.asset( "assets/animations/zenithlogo.riv"), ), - emailPasswordForgotPasswordColumn(), StatusMessage(message: errorMsg, color: lightCoral), - singUpLoginRow(), - otherMethodsOfLoginRow() + loginWithGoogleButton(), ], ), ), @@ -110,132 +99,27 @@ class _LoginWidgetState extends State { ); } - Row otherMethodsOfLoginRow() { - return Row(mainAxisAlignment: MainAxisAlignment.center, children: [ - otherMethodsOfLoginButton("github", _githubLoginController, - null), // github auth service doesn't exist yet, later github auth event must be passed as a parameter - otherMethodsOfLoginButton( - "facebook", _facebookLoginController, FacebookLoginEvent()), - otherMethodsOfLoginButton( - "google", _googleLoginController, GoogleLoginEvent()) - ]); - } - - Row singUpLoginRow() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [singInLoginButton("Sign Up"), singInLoginButton("Login")], - ); - } - - Column emailPasswordForgotPasswordColumn() { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - textField("Email", emailController, false), - const Divider(), - textField("Senha", passwordController, true), - forgotPasswordButton(), - ], - ); - } - - ElevatedButton otherMethodsOfLoginButton(String animationPathForType, - rive.RiveAnimationController _controller, AuthenticationEvent? event) { - String animationPath = - "assets/animations/" + animationPathForType + "_icon.riv"; - - return ElevatedButton( - onPressed: () => { - if (event != null) - { - _toggleAnimation(_controller), - BlocProvider.of(context).add(event), - } - }, - child: Container( - width: 40, - height: 40, - child: rive.RiveAnimation.asset( - animationPath, - controllers: [_controller], - )), - style: ButtonStyle( - shape: MaterialStateProperty.all(const CircleBorder()), - backgroundColor: MaterialStateProperty.all(white.withOpacity(0.001))), - ); - } - - Container singInLoginButton(String buttonText) { - return Container( - decoration: BoxDecoration( - color: lightBrown, borderRadius: BorderRadius.circular(30)), - width: MediaQuery.of(context).size.width * 0.35, - child: TextButton( - onPressed: () { - if (buttonText == "Login") { - BlocProvider.of(context).add(EmailLoginEvent( - email: emailController.text.trim(), - password: passwordController.text)); - passwordController.clear(); - } else { - Navigator.pushNamed(context, '/signup'); - } - }, - child: Text(buttonText, - style: const TextStyle( - color: white, fontFamily: 'DMSans', fontSize: 18))), - ); - } - - Align forgotPasswordButton() { - return Align( - alignment: const Alignment(0.7, 0), - child: TextButton( - onPressed: () { - Navigator.pushNamed(context, '/forgotPwd'); - }, - child: const Text( - "Esqueci a Senha", - textAlign: TextAlign.right, - style: TextStyle(color: white), - )), - ); - } - - Container textField( - String hintText, TextEditingController controller, bool hideText) { - return Container( - width: MediaQuery.of(context).size.width * 0.8, - decoration: const BoxDecoration( - gradient: LinearGradient( - begin: Alignment.centerRight, - end: Alignment.centerLeft, - colors: [ - Color(0xff404245), - Color(0xff212325), - ]), - borderRadius: BorderRadius.all(Radius.circular(10)), - ), - child: TextField( - controller: controller, - cursorColor: white, - obscureText: hideText, - style: const TextStyle( - color: white, - fontWeight: FontWeight.normal, - fontFamily: 'DMSans', - fontSize: 20.0), - decoration: InputDecoration( - isDense: true, - hintStyle: const TextStyle(color: white), - focusedBorder: const OutlineInputBorder( - borderSide: BorderSide(color: raisingBlack), - borderRadius: BorderRadius.all(Radius.circular(10))), - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide(color: raisingBlack), - borderRadius: BorderRadius.all(Radius.circular(10))), - hintText: hintText), + Center loginWithGoogleButton() { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ElevatedButton( + // style: const TextStyle(fontSize: 20)), + onPressed: () { + // _toggleAnimation(_googleLoginController); + BlocProvider.of(context).add(GoogleLoginEvent()); + }, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Image.asset('assets/images/devicon_google.png', height: 20), + const SizedBox(width: 10), + const Text('Entrar com o Google'), + ], + ), + ), + ], ), ); } diff --git a/lib/widgets/mission_creation.dart b/lib/widgets/mission_creation.dart index a2a54d5..5dae140 100644 --- a/lib/widgets/mission_creation.dart +++ b/lib/widgets/mission_creation.dart @@ -182,13 +182,13 @@ class _MissionCreationState extends State { height: 30, child: ElevatedButton( style: ButtonStyle( - backgroundColor: MaterialStateProperty.resolveWith( - (Set states) { - if (states.contains(MaterialState.pressed)) return Colors.green; + backgroundColor: WidgetStateProperty.resolveWith( + (Set states) { + if (states.contains(WidgetState.pressed)) return Colors.green; return mantisGreen; }, ), - shape: MaterialStateProperty.all( + shape: WidgetStateProperty.all( RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.0), ))), diff --git a/lib/widgets/not_found_screen.dart b/lib/widgets/not_found_screen.dart index 1c007f1..c20da71 100644 --- a/lib/widgets/not_found_screen.dart +++ b/lib/widgets/not_found_screen.dart @@ -5,12 +5,12 @@ import 'package:zenith_monitor/widgets/standard_app_bar.dart'; class NotFoundScreen extends StatelessWidget { @override Widget build(BuildContext context) { - return Scaffold( + return const Scaffold( backgroundColor: eerieBlack, - appBar: const StandardAppBar(title: "404"), + appBar: StandardAppBar(title: "404"), body: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, - children: const [ + children: [ Align( alignment: Alignment.topLeft, child: Padding( diff --git a/lib/widgets/terminal.dart b/lib/widgets/terminal.dart index 6546ec1..7e31f2c 100644 --- a/lib/widgets/terminal.dart +++ b/lib/widgets/terminal.dart @@ -20,9 +20,7 @@ class Terminal extends StatelessWidget { @override Widget build(BuildContext context) { - if (WidgetsBinding.instance != null) { - WidgetsBinding.instance!.addPostFrameCallback((_) => _scrollToEnd()); - } + WidgetsBinding.instance.addPostFrameCallback((_) => _scrollToEnd()); return Scaffold( body: SafeArea( child: CustomScrollView( diff --git a/pubspec.lock b/pubspec.lock index c44583c..9afc3d1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,230 +1,278 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _flutterfire_internals: + dependency: transitive + description: + name: _flutterfire_internals + sha256: b46f62516902afb04befa4b30eb6a12ac1f58ca8cb25fb9d632407259555dd3d + url: "https://pub.dev" + source: hosted + version: "1.3.39" args: dependency: transitive description: name: args - url: "https://pub.dartlang.org" + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.5.0" async: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" source: hosted - version: "2.8.2" + version: "2.11.0" bloc: dependency: "direct main" description: name: bloc - url: "https://pub.dartlang.org" + sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e" + url: "https://pub.dev" source: hosted - version: "7.2.1" + version: "8.1.4" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" 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" + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.0" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" cloud_firestore: dependency: "direct main" description: name: cloud_firestore - url: "https://pub.dartlang.org" + sha256: "240c1c3598e62ad58ee665b6df9c65172d2fbe4742770c21ed060e013cb8e037" + url: "https://pub.dev" source: hosted - version: "2.5.4" + version: "5.1.0" cloud_firestore_platform_interface: - dependency: transitive + dependency: "direct main" description: name: cloud_firestore_platform_interface - url: "https://pub.dartlang.org" + sha256: "5b5a9c2b5a85bf995f12e7447c4197d7ad659533d642d3d904ccbb509f83d62a" + url: "https://pub.dev" source: hosted - version: "5.5.7" + version: "6.2.9" cloud_firestore_web: dependency: transitive description: name: cloud_firestore_web - url: "https://pub.dartlang.org" + sha256: "898e9f65548df65ca7b2cff9f32cf233424ceccff1d870b8819ea2b8050d5b39" + url: "https://pub.dev" source: hosted - version: "2.6.16" + version: "4.0.3" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.18.0" connectivity_plus: dependency: "direct main" description: name: connectivity_plus - url: "https://pub.dartlang.org" - source: hosted - version: "2.3.5" - connectivity_plus_linux: - dependency: transitive - description: - name: connectivity_plus_linux - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.1" - connectivity_plus_macos: - dependency: transitive - description: - name: connectivity_plus_macos - url: "https://pub.dartlang.org" + sha256: db7a4e143dc72cc3cb2044ef9b052a7ebfe729513e6a82943bc3526f784365b8 + url: "https://pub.dev" source: hosted - version: "1.2.4" + version: "6.0.3" connectivity_plus_platform_interface: dependency: transitive description: name: connectivity_plus_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.1" - connectivity_plus_web: - dependency: transitive - description: - name: connectivity_plus_web - url: "https://pub.dartlang.org" + sha256: b6a56efe1e6675be240de39107281d4034b64ac23438026355b4234042a35adb + url: "https://pub.dev" source: hosted - version: "1.2.2" - connectivity_plus_windows: + version: "2.0.0" + cross_file: dependency: transitive description: - name: connectivity_plus_windows - url: "https://pub.dartlang.org" + name: cross_file + sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32" + url: "https://pub.dev" source: hosted - version: "1.2.2" - cross_file: + version: "0.3.4+1" + csslib: dependency: transitive description: - name: cross_file - url: "https://pub.dartlang.org" + name: csslib + sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" + url: "https://pub.dev" source: hosted - version: "0.3.3+1" + version: "1.0.0" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - url: "https://pub.dartlang.org" + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.8" dbus: dependency: transitive description: name: dbus - url: "https://pub.dartlang.org" + sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" + url: "https://pub.dev" + source: hosted + version: "0.7.10" + equatable: + dependency: "direct main" + description: + name: equatable + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" source: hosted - version: "0.7.3" + version: "2.0.5" + facebook_auth_desktop: + dependency: transitive + description: + name: facebook_auth_desktop + sha256: "0e4f147a57de8fdb8eaaee4836e6b9859482921143af0c350ffbf2a9bbd531a0" + url: "https://pub.dev" + source: hosted + version: "2.0.0" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.1" ffi: dependency: transitive description: name: ffi - url: "https://pub.dartlang.org" + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" + url: "https://pub.dev" source: hosted - version: "1.2.1" - file: + version: "2.1.2" + file_selector_linux: dependency: transitive description: - name: file - url: "https://pub.dartlang.org" + name: file_selector_linux + sha256: "045d372bf19b02aeb69cacf8b4009555fb5f6f0b7ad8016e5f46dd1387ddd492" + url: "https://pub.dev" source: hosted - version: "6.1.2" + version: "0.9.2+1" + file_selector_macos: + dependency: transitive + description: + name: file_selector_macos + sha256: f42eacb83b318e183b1ae24eead1373ab1334084404c8c16e0354f9a3e55d385 + url: "https://pub.dev" + source: hosted + version: "0.9.4" + file_selector_platform_interface: + dependency: transitive + description: + name: file_selector_platform_interface + sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b + url: "https://pub.dev" + source: hosted + version: "2.6.2" + file_selector_windows: + dependency: transitive + description: + name: file_selector_windows + sha256: d3547240c20cabf205c7c7f01a50ecdbc413755814d6677f3cb366f04abcead0 + url: "https://pub.dev" + source: hosted + version: "0.9.3+1" firebase_auth: dependency: "direct main" description: name: firebase_auth - url: "https://pub.dartlang.org" + sha256: a41b56878fa6aef3ea52962329b47eee333672d4b0ecc406e071b9fc729f242c + url: "https://pub.dev" source: hosted - version: "3.3.19" + version: "5.1.2" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface - url: "https://pub.dartlang.org" + sha256: d1c68097588f3b75ef79a22102ff96c311735c254353bccf6824d19f1a7e86b9 + url: "https://pub.dev" source: hosted - version: "6.2.7" + version: "7.4.2" firebase_auth_web: dependency: transitive description: name: firebase_auth_web - url: "https://pub.dartlang.org" + sha256: e66ec0ae5697ee39ccd4865d6887cb0df220dd4ea0b21404910c68ca4c1a731a + url: "https://pub.dev" source: hosted - version: "3.3.16" + version: "5.12.4" firebase_core: dependency: "direct main" description: name: firebase_core - url: "https://pub.dartlang.org" + sha256: "5159984ce9b70727473eb388394650677c02c925aaa6c9439905e1f30966a4d5" + url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "3.2.0" firebase_core_platform_interface: - dependency: transitive + dependency: "direct overridden" description: name: firebase_core_platform_interface - url: "https://pub.dartlang.org" + sha256: "1003a5a03a61fc9a22ef49f37cbcb9e46c86313a7b2e7029b9390cf8c6fc32cb" + url: "https://pub.dev" source: hosted - version: "4.4.0" + version: "5.1.0" firebase_core_web: dependency: transitive description: name: firebase_core_web - url: "https://pub.dartlang.org" + sha256: "23509cb3cddfb3c910c143279ac3f07f06d3120f7d835e4a5d4b42558e978712" + url: "https://pub.dev" source: hosted - version: "1.6.4" + version: "2.17.3" firebase_storage: dependency: "direct main" description: name: firebase_storage - url: "https://pub.dartlang.org" + sha256: "472e8532dfdcffd00ed8cc1ff57fda76af6f0a8a26547908bc20f25079922408" + url: "https://pub.dev" source: hosted - version: "10.2.17" + version: "12.1.1" firebase_storage_platform_interface: dependency: transitive description: name: firebase_storage_platform_interface - url: "https://pub.dartlang.org" + sha256: f07b5dbf0efcaab0f7d38910851791f7509b8864d491bb11c079f67339f31b27 + url: "https://pub.dev" source: hosted - version: "4.1.7" + version: "5.1.26" firebase_storage_web: dependency: transitive description: name: firebase_storage_web - url: "https://pub.dartlang.org" + sha256: ef9d27afcd68a1c2b90d0cad2b02cf6333a053655988f4a2f6cba2796134cae5 + url: "https://pub.dev" source: hosted - version: "3.2.16" + version: "3.9.11" flutter: dependency: "direct main" description: flutter @@ -234,58 +282,114 @@ packages: dependency: "direct main" description: name: flutter_bloc - url: "https://pub.dartlang.org" + sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a + url: "https://pub.dev" source: hosted - version: "7.3.3" + version: "8.1.6" flutter_dotenv: dependency: "direct main" description: name: flutter_dotenv - url: "https://pub.dartlang.org" + sha256: "9357883bdd153ab78cbf9ffa07656e336b8bbb2b5a3ca596b0b27e119f7c7d77" + url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "5.1.0" flutter_facebook_auth: dependency: "direct main" description: name: flutter_facebook_auth - url: "https://pub.dartlang.org" + sha256: "48aab06f3b1d3494a33b0c10c11ecbc226fdcf0bf37b478e91d9de57cce840f5" + url: "https://pub.dev" source: hosted - version: "3.5.7" + version: "7.0.1" flutter_facebook_auth_platform_interface: dependency: transitive description: name: flutter_facebook_auth_platform_interface - url: "https://pub.dartlang.org" + sha256: dc9d621dd45c4f0b341173a16e94f4b77155fa9c0f4326743f1251f2f445ba38 + url: "https://pub.dev" source: hosted - version: "2.7.1" + version: "6.0.0" flutter_facebook_auth_web: dependency: transitive description: name: flutter_facebook_auth_web - url: "https://pub.dartlang.org" + sha256: "947d93fc5a7cc5db1ce0274505254bb3b619cdd98176954f125f742964696804" + url: "https://pub.dev" source: hosted - version: "2.6.0+2" + version: "6.0.0" flutter_lints: dependency: "direct dev" description: name: flutter_lints - url: "https://pub.dartlang.org" + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" + url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "4.0.0" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle - url: "https://pub.dartlang.org" + sha256: c6b0b4c05c458e1c01ad9bcc14041dd7b1f6783d487be4386f793f47a8a4d03e + url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.0.20" flutter_polyline_points: dependency: "direct main" description: name: flutter_polyline_points - url: "https://pub.dartlang.org" + sha256: "3a1c8c30abee9fb0fbe44c70d5d1cedb10ef28ec7ea285c669f02b3e183483aa" + url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "2.1.0" + flutter_secure_storage: + dependency: transitive + description: + name: flutter_secure_storage + sha256: "165164745e6afb5c0e3e3fcc72a012fb9e58496fb26ffb92cf22e16a821e85d0" + url: "https://pub.dev" + source: hosted + version: "9.2.2" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: "4d91bfc23047422cbcd73ac684bc169859ee766482517c22172c86596bf1464b" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "1693ab11121a5f925bbea0be725abfcfbbcf36c1e29e571f84a0c0f436147a81" + url: "https://pub.dev" + source: hosted + version: "3.1.2" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + url: "https://pub.dev" + source: hosted + version: "3.1.2" flutter_test: dependency: "direct dev" description: flutter @@ -296,321 +400,462 @@ packages: description: flutter source: sdk version: "0.0.0" - geoflutterfire: - dependency: "direct main" - description: - name: geoflutterfire - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.1" get: dependency: "direct main" description: name: get - url: "https://pub.dartlang.org" + sha256: e4e7335ede17452b391ed3b2ede016545706c01a02292a6c97619705e7d2a85e + url: "https://pub.dev" + source: hosted + version: "4.6.6" + google_identity_services_web: + dependency: transitive + description: + name: google_identity_services_web + sha256: "9482364c9f8b7bd36902572ebc3a7c2b5c8ee57a9c93e6eb5099c1a9ec5265d8" + url: "https://pub.dev" source: hosted - version: "4.6.5" + version: "0.3.1+1" + google_maps: + dependency: transitive + description: + name: google_maps + sha256: "47eef3836b49bb030d5cb3afc60b8451408bf34cf753e571b645d6529eb4251a" + url: "https://pub.dev" + source: hosted + version: "7.1.0" google_maps_flutter: dependency: "direct main" description: name: google_maps_flutter - url: "https://pub.dartlang.org" + sha256: acf0ec482d86b2ac55ade80597ce7f797a47971f5210ebfd030f0d58130e0a94 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + google_maps_flutter_android: + dependency: transitive + description: + name: google_maps_flutter_android + sha256: f6306d83edddba7aa017ca6f547d6f36a1443f90ed49d91d48ef70d7aa86e2e1 + url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.12.0" + google_maps_flutter_ios: + dependency: transitive + description: + name: google_maps_flutter_ios + sha256: a6e3c6ecdda6c985053f944be13a0645ebb919da2ef0f5bc579c5e1670a5b2a8 + url: "https://pub.dev" + source: hosted + version: "2.10.0" google_maps_flutter_platform_interface: dependency: transitive description: name: google_maps_flutter_platform_interface - url: "https://pub.dartlang.org" + sha256: bd60ca330e3c7763b95b477054adec338a522d982af73ecc520b232474063ac5 + url: "https://pub.dev" + source: hosted + version: "2.8.0" + google_maps_flutter_web: + dependency: transitive + description: + name: google_maps_flutter_web + sha256: f3155c12119d8a5c2732fdf39ceb5cc095bc662059a03b4ea23294ecebe1d199 + url: "https://pub.dev" source: hosted - version: "2.1.7" + version: "0.5.8" google_sign_in: dependency: "direct main" description: name: google_sign_in - url: "https://pub.dartlang.org" + sha256: "0b8787cb9c1a68ad398e8010e8c8766bfa33556d2ab97c439fb4137756d7308f" + url: "https://pub.dev" source: hosted - version: "5.3.2" + version: "6.2.1" google_sign_in_android: dependency: transitive description: name: google_sign_in_android - url: "https://pub.dartlang.org" + sha256: d30fb34b659679ea74397e9748b4ab5d720720d57dcc79538f1b3c4a68654cb3 + url: "https://pub.dev" source: hosted - version: "5.2.8" + version: "6.1.27" google_sign_in_ios: dependency: transitive description: name: google_sign_in_ios - url: "https://pub.dartlang.org" + sha256: a058c9880be456f21e2e8571c1126eaacd570bdc5b6c6d9d15aea4bdf22ca9fe + url: "https://pub.dev" source: hosted - version: "5.3.1" + version: "5.7.6" google_sign_in_platform_interface: dependency: transitive description: name: google_sign_in_platform_interface - url: "https://pub.dartlang.org" + sha256: "1f6e5787d7a120cc0359ddf315c92309069171306242e181c09472d1b00a2971" + url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.4.5" google_sign_in_web: dependency: transitive description: name: google_sign_in_web - url: "https://pub.dartlang.org" + sha256: d606264c7a1a526a3aa79d938b85a601d8589731a478bd4a3dcbdeb14a572228 + url: "https://pub.dev" source: hosted - version: "0.10.1+2" + version: "0.12.4+1" graphs: dependency: transitive description: name: graphs - url: "https://pub.dartlang.org" + sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.3.1" + html: + dependency: transitive + description: + name: html + sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" + url: "https://pub.dev" + source: hosted + version: "0.15.4" http: dependency: "direct main" description: name: http - url: "https://pub.dartlang.org" + sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 + url: "https://pub.dev" source: hosted - version: "0.13.4" + version: "1.2.2" http_parser: dependency: "direct main" description: name: http_parser - url: "https://pub.dartlang.org" + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" source: hosted - version: "4.0.1" + version: "4.0.2" image_cropper: dependency: "direct main" description: name: image_cropper - url: "https://pub.dartlang.org" + sha256: d31be025c744ac1bf52d1f49cfdd92fd421e7e45ddadaaac0b39901f67c2a7e3 + url: "https://pub.dev" + source: hosted + version: "7.1.0" + image_cropper_for_web: + dependency: transitive + description: + name: image_cropper_for_web + sha256: "6386e64908ce5d5df404e01c750a99b633dfcea88da69b3efcd3b3811d639760" + url: "https://pub.dev" source: hosted - version: "1.5.1" + version: "5.1.0" + image_cropper_platform_interface: + dependency: transitive + description: + name: image_cropper_platform_interface + sha256: "39c6539571bda7ce666e0a2f450246a5d42187406eef8f486a3d64f1d9381637" + url: "https://pub.dev" + source: hosted + version: "6.1.0" image_picker: dependency: "direct main" description: name: image_picker - url: "https://pub.dartlang.org" + sha256: "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a" + url: "https://pub.dev" source: hosted - version: "0.8.5+3" + version: "1.1.2" image_picker_android: dependency: transitive description: name: image_picker_android - url: "https://pub.dartlang.org" + sha256: ff39a10ab4f48f4ac70776d0494a97bf073cd2570892cd46bc8a5cac162c25db + url: "https://pub.dev" source: hosted - version: "0.8.4+13" + version: "0.8.12+4" image_picker_for_web: dependency: transitive description: name: image_picker_for_web - url: "https://pub.dartlang.org" + sha256: "5d6eb13048cd47b60dbf1a5495424dea226c5faf3950e20bf8120a58efb5b5f3" + url: "https://pub.dev" source: hosted - version: "2.1.8" + version: "3.0.4" image_picker_ios: dependency: transitive description: name: image_picker_ios - url: "https://pub.dartlang.org" + sha256: "6703696ad49f5c3c8356d576d7ace84d1faf459afb07accbb0fae780753ff447" + url: "https://pub.dev" source: hosted - version: "0.8.5+5" + version: "0.8.12" + image_picker_linux: + dependency: transitive + description: + name: image_picker_linux + sha256: "4ed1d9bb36f7cd60aa6e6cd479779cc56a4cb4e4de8f49d487b1aaad831300fa" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" + image_picker_macos: + dependency: transitive + description: + name: image_picker_macos + sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" image_picker_platform_interface: dependency: transitive description: name: image_picker_platform_interface - url: "https://pub.dartlang.org" + sha256: "9ec26d410ff46f483c5519c29c02ef0e02e13a543f882b152d4bfd2f06802f80" + url: "https://pub.dev" source: hosted - version: "2.5.0" - intl: + version: "2.10.0" + image_picker_windows: dependency: transitive description: - name: intl - url: "https://pub.dartlang.org" + name: image_picker_windows + sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb" + url: "https://pub.dev" source: hosted - version: "0.17.0" + version: "0.2.1+1" js: dependency: transitive description: name: js - url: "https://pub.dartlang.org" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + js_wrapping: + dependency: transitive + description: + name: js_wrapping + sha256: e385980f7c76a8c1c9a560dfb623b890975841542471eade630b2871d243851c + url: "https://pub.dev" + source: hosted + version: "0.7.4" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + url: "https://pub.dev" + source: hosted + version: "10.0.4" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" source: hosted - version: "0.6.3" + version: "3.0.1" lints: dependency: transitive description: name: lints - url: "https://pub.dartlang.org" + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" + url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "4.0.0" location: dependency: "direct main" description: name: location - url: "https://pub.dartlang.org" + sha256: "6463a242973bf247e3fb1c7722919521b98026978ee3b5177202e103a39c145e" + url: "https://pub.dev" source: hosted - version: "4.4.0" + version: "7.0.0" location_platform_interface: dependency: transitive description: name: location_platform_interface - url: "https://pub.dartlang.org" + sha256: "1e535ccc8b4a9612de4e4319871136b45d2b5d1fb0c2a8bf99687242bf7ca5f7" + url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "5.0.0" location_web: dependency: transitive description: name: location_web - url: "https://pub.dartlang.org" + sha256: "613597b489beb396f658c6f4358dd383c5ed0a1402d95e287642a5f2d8171cb0" + url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "5.0.3" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" source: hosted - version: "0.12.11" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - url: "https://pub.dartlang.org" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + url: "https://pub.dev" source: hosted - version: "0.1.3" + version: "0.8.0" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + url: "https://pub.dev" + source: hosted + version: "1.12.0" + mime: + dependency: transitive + description: + name: mime + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" + url: "https://pub.dev" source: hosted - version: "1.7.0" + version: "1.0.5" nested: dependency: transitive description: name: nested - url: "https://pub.dartlang.org" + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" source: hosted version: "1.0.0" nm: dependency: transitive description: name: nm - url: "https://pub.dartlang.org" + sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" + url: "https://pub.dev" source: hosted version: "0.5.0" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.0" path_provider: dependency: "direct main" description: name: path_provider - url: "https://pub.dartlang.org" + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 + url: "https://pub.dev" source: hosted - version: "2.0.10" + version: "2.1.3" path_provider_android: dependency: transitive description: name: path_provider_android - url: "https://pub.dartlang.org" + sha256: "30c5aa827a6ae95ce2853cdc5fe3971daaac00f6f081c419c013f7f57bff2f5e" + url: "https://pub.dev" source: hosted - version: "2.0.14" - path_provider_ios: + version: "2.2.7" + path_provider_foundation: dependency: transitive description: - name: path_provider_ios - url: "https://pub.dartlang.org" + name: path_provider_foundation + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 + url: "https://pub.dev" source: hosted - version: "2.0.9" + version: "2.4.0" path_provider_linux: dependency: transitive description: name: path_provider_linux - url: "https://pub.dartlang.org" + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" source: hosted - version: "2.1.7" - path_provider_macos: - dependency: transitive - description: - name: path_provider_macos - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.6" + version: "2.2.1" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - url: "https://pub.dartlang.org" + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" source: hosted - version: "2.0.4" + version: "2.1.2" path_provider_windows: dependency: transitive description: name: path_provider_windows - url: "https://pub.dartlang.org" + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" source: hosted - version: "2.0.7" + version: "2.3.0" petitparser: dependency: transitive description: name: petitparser - url: "https://pub.dartlang.org" + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + url: "https://pub.dev" source: hosted - version: "4.4.0" + version: "6.0.2" platform: dependency: transitive description: name: platform - url: "https://pub.dartlang.org" + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" + url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.5" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.2" - process: - dependency: transitive - description: - name: process - url: "https://pub.dartlang.org" + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" source: hosted - version: "4.2.4" + version: "2.1.8" provider: dependency: transitive description: name: provider - url: "https://pub.dartlang.org" + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + url: "https://pub.dev" source: hosted - version: "6.0.3" - quiver: - dependency: transitive - description: - name: quiver - url: "https://pub.dartlang.org" - source: hosted - version: "3.1.0" + version: "6.1.2" rive: dependency: "direct main" description: name: rive - url: "https://pub.dartlang.org" + sha256: "3c0047e636ebe8e4044087e239dffdd026cf839fe9aecf55d53431b255668bcf" + url: "https://pub.dev" source: hosted - version: "0.7.33" - rxdart: + version: "0.13.9" + rive_common: dependency: transitive description: - name: rxdart - url: "https://pub.dartlang.org" + name: rive_common + sha256: "3fe76ba4680787741688ee393e47b63417e8643816795e4eac01021683af1d84" + url: "https://pub.dev" source: hosted - version: "0.27.4" + version: "0.4.9" + sanitize_html: + dependency: transitive + description: + name: sanitize_html + sha256: "12669c4a913688a26555323fb9cec373d8f9fbe091f2d01c40c723b33caa8989" + url: "https://pub.dev" + source: hosted + version: "2.1.0" sky_engine: dependency: transitive description: flutter @@ -620,93 +865,122 @@ packages: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" source: hosted - version: "1.8.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.2" stream_transform: dependency: transitive description: name: stream_transform - url: "https://pub.dartlang.org" + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + url: "https://pub.dev" source: hosted - version: "0.4.8" + version: "0.7.0" typed_data: dependency: transitive description: name: typed_data - url: "https://pub.dartlang.org" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.3.2" usb_serial: dependency: "direct main" description: name: usb_serial - url: "https://pub.dartlang.org" + sha256: a605a600e34e7f28d4e80851ca3999ef747e42e406138887b8a88b8c382a8b07 + url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.5.2" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.dartlang.org" + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + url: "https://pub.dev" + source: hosted + version: "14.2.1" + web: + dependency: transitive + description: + name: web + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + url: "https://pub.dev" + source: hosted + version: "0.5.1" win32: dependency: transitive description: name: win32 - url: "https://pub.dartlang.org" + sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 + url: "https://pub.dev" source: hosted - version: "2.5.2" + version: "5.5.1" xdg_directories: dependency: transitive description: name: xdg_directories - url: "https://pub.dartlang.org" + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + url: "https://pub.dev" source: hosted - version: "0.2.0+1" + version: "1.0.4" xml: dependency: transitive description: name: xml - url: "https://pub.dartlang.org" + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + url: "https://pub.dev" source: hosted - version: "5.3.1" + version: "6.5.0" sdks: - dart: ">=2.16.0 <3.0.0" - flutter: ">=2.8.1" + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index 0fe36f4..a3df3b0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -29,30 +29,34 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 - http: ^0.13.3 - flutter_polyline_points: ^1.0.0 - bloc: ^7.2.1 - flutter_bloc: ^7.3.1 + http: ^1.2.1 + flutter_polyline_points: ^2.1.0 + bloc: ^8.1.4 + flutter_bloc: ^8.1.6 path_provider: ^2.0.3 - http_parser: ^4.0.0 - google_sign_in: ^5.1.0 - firebase_auth: ^3.0.2 - firebase_core: ^1.7.0 - cloud_firestore: ^2.5.3 - geoflutterfire: ^3.0.0-nullsafety.2 - google_maps_flutter: ^2.0.10 - location: ^4.3.0 - flutter_facebook_auth: ^3.5.2 - rive: ^0.7.33 - image_picker: ^0.8.4+10 - image_cropper: ^1.5.0 - firebase_storage: ^10.2.9 - usb_serial: ^0.3.0 - connectivity_plus: ^2.3.5 + http_parser: ^4.0.2 + google_sign_in: ^6.2.1 + firebase_auth: ^5.1.2 + firebase_core: ^3.2.0 + cloud_firestore: ^5.1.0 + google_maps_flutter: ^2.2.5 + location: ^7.0.0 + flutter_facebook_auth: ^7.0.1 + rive: ^0.13.9 + image_picker: ^1.1.2 + image_cropper: ^7.1.0 + firebase_storage: ^12.1.1 + usb_serial: ^0.5.2 + connectivity_plus: ^6.0.3 flutter_dotenv: ^5.0.2 + cloud_firestore_platform_interface: ^6.2.9 + equatable: ^2.0.5 + +dependency_overrides: + firebase_core_platform_interface: 5.1.0 dev_dependencies: - flutter_lints: ^1.0.4 + flutter_lints: ^4.0.0 flutter_test: sdk: flutter From 04f3090c5e93f5fcbf7bbcc7a86d6fb76130126a Mon Sep 17 00:00:00 2001 From: Italo Matos Date: Wed, 21 Aug 2024 19:59:07 -0300 Subject: [PATCH 02/12] hide firebase options --- lib/firebase_options.dart | 61 --------------------------------------- 1 file changed, 61 deletions(-) diff --git a/lib/firebase_options.dart b/lib/firebase_options.dart index 7d9c2f2..8b13789 100644 --- a/lib/firebase_options.dart +++ b/lib/firebase_options.dart @@ -1,62 +1 @@ -// File generated by FlutterFire CLI. -// ignore_for_file: type=lint -import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; -import 'package:flutter/foundation.dart' - show defaultTargetPlatform, kIsWeb, TargetPlatform; -/// Default [FirebaseOptions] for use with your Firebase apps. -/// -/// Example: -/// ```dart -/// import 'firebase_options.dart'; -/// // ... -/// await Firebase.initializeApp( -/// options: DefaultFirebaseOptions.currentPlatform, -/// ); -/// ``` -class DefaultFirebaseOptions { - static FirebaseOptions get currentPlatform { - if (kIsWeb) { - throw UnsupportedError( - 'DefaultFirebaseOptions have not been configured for web - ' - 'you can reconfigure this by running the FlutterFire CLI again.', - ); - } - switch (defaultTargetPlatform) { - case TargetPlatform.android: - return android; - case TargetPlatform.iOS: - throw UnsupportedError( - 'DefaultFirebaseOptions have not been configured for ios - ' - 'you can reconfigure this by running the FlutterFire CLI again.', - ); - case TargetPlatform.macOS: - throw UnsupportedError( - 'DefaultFirebaseOptions have not been configured for macos - ' - 'you can reconfigure this by running the FlutterFire CLI again.', - ); - case TargetPlatform.windows: - throw UnsupportedError( - 'DefaultFirebaseOptions have not been configured for windows - ' - 'you can reconfigure this by running the FlutterFire CLI again.', - ); - case TargetPlatform.linux: - throw UnsupportedError( - 'DefaultFirebaseOptions have not been configured for linux - ' - 'you can reconfigure this by running the FlutterFire CLI again.', - ); - default: - throw UnsupportedError( - 'DefaultFirebaseOptions are not supported for this platform.', - ); - } - } - - static const FirebaseOptions android = FirebaseOptions( - apiKey: 'AIzaSyAeOmRf75mRRVoXO1g4cm6wqVUoOuUf97I', - appId: '1:767599767515:android:f96dbb868c7ca38e1e81fe', - messagingSenderId: '767599767515', - projectId: 'teste-firebase-zenith', - storageBucket: 'teste-firebase-zenith.appspot.com', - ); -} From efd2c9e4179200557c409c8669d9120318581879 Mon Sep 17 00:00:00 2001 From: Italo Matos Date: Wed, 21 Aug 2024 20:01:36 -0300 Subject: [PATCH 03/12] update gitignore --- .gitignore | 3 ++ lib/firebase_options.dart | 61 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/.gitignore b/.gitignore index 78f046e..4724cb2 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,6 @@ android/local_maps.properties #.env file .env + +#Firebase options file +lib/firebase_options.dart diff --git a/lib/firebase_options.dart b/lib/firebase_options.dart index 8b13789..7d9c2f2 100644 --- a/lib/firebase_options.dart +++ b/lib/firebase_options.dart @@ -1 +1,62 @@ +// File generated by FlutterFire CLI. +// ignore_for_file: type=lint +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; +import 'package:flutter/foundation.dart' + show defaultTargetPlatform, kIsWeb, TargetPlatform; +/// Default [FirebaseOptions] for use with your Firebase apps. +/// +/// Example: +/// ```dart +/// import 'firebase_options.dart'; +/// // ... +/// await Firebase.initializeApp( +/// options: DefaultFirebaseOptions.currentPlatform, +/// ); +/// ``` +class DefaultFirebaseOptions { + static FirebaseOptions get currentPlatform { + if (kIsWeb) { + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for web - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + } + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return android; + case TargetPlatform.iOS: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for ios - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + case TargetPlatform.macOS: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for macos - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + case TargetPlatform.windows: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for windows - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + case TargetPlatform.linux: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for linux - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + default: + throw UnsupportedError( + 'DefaultFirebaseOptions are not supported for this platform.', + ); + } + } + + static const FirebaseOptions android = FirebaseOptions( + apiKey: 'AIzaSyAeOmRf75mRRVoXO1g4cm6wqVUoOuUf97I', + appId: '1:767599767515:android:f96dbb868c7ca38e1e81fe', + messagingSenderId: '767599767515', + projectId: 'teste-firebase-zenith', + storageBucket: 'teste-firebase-zenith.appspot.com', + ); +} From e4b463e565bcd8f0116acde8ad4dd41ddf4718c8 Mon Sep 17 00:00:00 2001 From: Italo Matos Date: Sun, 25 Aug 2024 21:49:40 -0300 Subject: [PATCH 04/12] configurando a ajustando chamada de variaveis de ambiente --- .gitignore | 2 +- android/.project | 28 ------------------------- android/app/build.gradle | 44 +++++++++++++++++++++------------------ firebase.json | 2 +- lib/firebase_options.dart | 10 ++++----- pubspec.lock | 24 ++++++++++----------- pubspec.yaml | 4 ++-- 7 files changed, 45 insertions(+), 69 deletions(-) delete mode 100644 android/.project diff --git a/.gitignore b/.gitignore index 4724cb2..401cbb6 100644 --- a/.gitignore +++ b/.gitignore @@ -52,7 +52,7 @@ android/app/google-services.json android/local_maps.properties #.env file -.env +*.env #Firebase options file lib/firebase_options.dart diff --git a/android/.project b/android/.project deleted file mode 100644 index 79bb12b..0000000 --- a/android/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - android - Project android created by Buildship. - - - - - org.eclipse.buildship.core.gradleprojectbuilder - - - - - - org.eclipse.buildship.core.gradleprojectnature - - - - 1721135133511 - - 30 - - org.eclipse.core.resources.regexFilterMatcher - node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ - - - - diff --git a/android/app/build.gradle b/android/app/build.gradle index 33afcee..af16ff6 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -2,38 +2,44 @@ plugins { id "com.android.application" id "kotlin-android" id "dev.flutter.flutter-gradle-plugin" - id "com.google.gms.google-services" + id "com.google.gms.google-services" } def localProperties = new Properties() - def localPropertiesFile = rootProject.file('local.properties') - if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } - } +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' +// Carregar variáveis do arquivo .env +def dotenvPropertiesFile = rootProject.file('.env') +if (dotenvPropertiesFile.exists()) { + dotenvPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } } -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' +// Obter a chave da API do Maps do local.properties ou .env +def mapsApiKey = localProperties.getProperty('MAPS_API_KEY') +if (mapsApiKey == null) { + throw new GradleException("MAPS_API_KEY not found. Please define it in your .env or local.properties file.") } +def flutterVersionCode = localProperties.getProperty('flutter.versionCode', '1') +def flutterVersionName = localProperties.getProperty('flutter.versionName', '1.0') android { namespace 'com.zenithaerospace.monitor' - compileSdkVersion 34 + compileSdkVersion 34 ndkVersion "25.2.9519653" - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } defaultConfig { applicationId "com.zenithaerospace.monitor" @@ -42,13 +48,11 @@ android { multiDexEnabled true versionCode flutterVersionCode.toInteger() versionName flutterVersionName -// manifestPlaceholders = [MAPS_API_KEY: mapsApiKey] + manifestPlaceholders = [MAPS_API_KEY: mapsApiKey] } buildTypes { release { - // Configuração de assinatura para o build de release - // (Substitua com sua própria configuração de assinatura) signingConfig signingConfigs.debug } } diff --git a/firebase.json b/firebase.json index 03fb71f..b565715 100644 --- a/firebase.json +++ b/firebase.json @@ -1 +1 @@ -{"flutter":{"platforms":{"android":{"default":{"projectId":"teste-firebase-zenith","appId":"1:767599767515:android:f96dbb868c7ca38e1e81fe","fileOutput":"android/app/google-services.json"}},"dart":{"lib/firebase_options.dart":{"projectId":"teste-firebase-zenith","configurations":{"android":"1:767599767515:android:f96dbb868c7ca38e1e81fe"}}}}}} \ No newline at end of file +{"flutter":{"platforms":{"android":{"default":{"projectId":"zenith-monitor-dev-a8116","appId":"1:187164656992:android:967f7a157146fd37fb8a05","fileOutput":"android/app/google-services.json"}},"dart":{"lib/firebase_options.dart":{"projectId":"zenith-monitor-dev-a8116","configurations":{"android":"1:187164656992:android:967f7a157146fd37fb8a05"}}}}}} \ No newline at end of file diff --git a/lib/firebase_options.dart b/lib/firebase_options.dart index 7d9c2f2..cf05519 100644 --- a/lib/firebase_options.dart +++ b/lib/firebase_options.dart @@ -53,10 +53,10 @@ class DefaultFirebaseOptions { } static const FirebaseOptions android = FirebaseOptions( - apiKey: 'AIzaSyAeOmRf75mRRVoXO1g4cm6wqVUoOuUf97I', - appId: '1:767599767515:android:f96dbb868c7ca38e1e81fe', - messagingSenderId: '767599767515', - projectId: 'teste-firebase-zenith', - storageBucket: 'teste-firebase-zenith.appspot.com', + apiKey: 'AIzaSyCyuxwGpTVrR_rjAxqZoOFc1I-41GtzNaU', + appId: '1:187164656992:android:967f7a157146fd37fb8a05', + messagingSenderId: '187164656992', + projectId: 'zenith-monitor-dev-a8116', + storageBucket: 'zenith-monitor-dev-a8116.appspot.com', ); } diff --git a/pubspec.lock b/pubspec.lock index 9afc3d1..d572dff 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: b46f62516902afb04befa4b30eb6a12ac1f58ca8cb25fb9d632407259555dd3d + sha256: b1595874fbc8f7a50da90f5d8f327bb0bfd6a95dc906c390efe991540c3b54aa url: "https://pub.dev" source: hosted - version: "1.3.39" + version: "1.3.40" args: dependency: transitive description: @@ -205,34 +205,34 @@ packages: dependency: "direct main" description: name: firebase_auth - sha256: a41b56878fa6aef3ea52962329b47eee333672d4b0ecc406e071b9fc729f242c + sha256: "2457ac6cbc152fa464aad3fb35f98039b0c4ab8e9bedf476672508b291bdbc3a" url: "https://pub.dev" source: hosted - version: "5.1.2" + version: "5.1.4" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface - sha256: d1c68097588f3b75ef79a22102ff96c311735c254353bccf6824d19f1a7e86b9 + sha256: "0408e2ed74b1afa0490a93aa041fe90d7573af7ffc59a641edc6c5b5c1b8d2a4" url: "https://pub.dev" source: hosted - version: "7.4.2" + version: "7.4.3" firebase_auth_web: dependency: transitive description: name: firebase_auth_web - sha256: e66ec0ae5697ee39ccd4865d6887cb0df220dd4ea0b21404910c68ca4c1a731a + sha256: "7e0c6d0fa8c5c1b2ae126a78f2d1a206a77a913f78d20f155487bf746162dccc" url: "https://pub.dev" source: hosted - version: "5.12.4" + version: "5.12.5" firebase_core: dependency: "direct main" description: name: firebase_core - sha256: "5159984ce9b70727473eb388394650677c02c925aaa6c9439905e1f30966a4d5" + sha256: "3187f4f8e49968573fd7403011dca67ba95aae419bc0d8131500fae160d94f92" url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.3.0" firebase_core_platform_interface: dependency: "direct overridden" description: @@ -245,10 +245,10 @@ packages: dependency: transitive description: name: firebase_core_web - sha256: "23509cb3cddfb3c910c143279ac3f07f06d3120f7d835e4a5d4b42558e978712" + sha256: e8d1e22de72cb21cdcfc5eed7acddab3e99cd83f3b317f54f7a96c32f25fd11e url: "https://pub.dev" source: hosted - version: "2.17.3" + version: "2.17.4" firebase_storage: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index a3df3b0..615161f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,7 +36,7 @@ dependencies: path_provider: ^2.0.3 http_parser: ^4.0.2 google_sign_in: ^6.2.1 - firebase_auth: ^5.1.2 + firebase_auth: ^5.1.4 firebase_core: ^3.2.0 cloud_firestore: ^5.1.0 google_maps_flutter: ^2.2.5 @@ -48,7 +48,7 @@ dependencies: firebase_storage: ^12.1.1 usb_serial: ^0.5.2 connectivity_plus: ^6.0.3 - flutter_dotenv: ^5.0.2 + flutter_dotenv: ^5.1.0 cloud_firestore_platform_interface: ^6.2.9 equatable: ^2.0.5 From 9ee5725011fedd02f1c0fcd3ba5282e702fb8380 Mon Sep 17 00:00:00 2001 From: Italo Matos Date: Sun, 25 Aug 2024 21:57:19 -0300 Subject: [PATCH 05/12] criando nova tela inicial --- lib/main.dart | 2 ++ lib/modules/home_page/screen/home_screen.dart | 15 +++++++++++++++ lib/widgets/login.dart | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 lib/modules/home_page/screen/home_screen.dart diff --git a/lib/main.dart b/lib/main.dart index 8f7d4c1..651d02c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:zenith_monitor/modules/home_page/screen/home_screen.dart'; import 'package:zenith_monitor/utils/services/authentication/google_auth.dart'; import 'package:zenith_monitor/utils/services/firestore_services/firestore_services.dart'; import 'package:zenith_monitor/widgets/not_found_screen.dart'; @@ -79,6 +80,7 @@ class Application extends StatelessWidget { '/login': (context) => const LoginScreen(), '/signup': (context) => const SignUpScreen(), // '/forgotPwd': (context) => const ForgotMyPassword(), + '/home': (context) => HomeScreen(), '/map': (context) => const MapScreen(), '/configuration': (context) => ConfigurationScreen(), '/terminal': (context) => const TerminalScreen(), diff --git a/lib/modules/home_page/screen/home_screen.dart b/lib/modules/home_page/screen/home_screen.dart new file mode 100644 index 0000000..8262465 --- /dev/null +++ b/lib/modules/home_page/screen/home_screen.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +class HomeScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Home Screen'), + ), + body: const Center( + child: Text('Welcome to the Home Screen!'), + ), + ); + } +} \ No newline at end of file diff --git a/lib/widgets/login.dart b/lib/widgets/login.dart index fc97289..6bc2764 100644 --- a/lib/widgets/login.dart +++ b/lib/widgets/login.dart @@ -41,7 +41,7 @@ class LoginWidgetState extends State { body: BlocConsumer( listener: (context, state) { if (state is LoginSuccess) { - Navigator.popAndPushNamed(context, '/map'); + Navigator.popAndPushNamed(context, '/home'); } }, builder: (context, state) { From 3d11df04536c740c41858e692919e93928acebca Mon Sep 17 00:00:00 2001 From: Italo Matos Date: Thu, 29 Aug 2024 17:09:27 -0300 Subject: [PATCH 06/12] add bluetooth screen --- android/app/proguard-rules.pro | 1 + android/app/src/main/AndroidManifest.xml | 153 +++-- lib/main.dart | 1 - .../bluetooth/BackgroundCollectedPage.dart | 145 +++++ .../bluetooth/BackgroundCollectingTask.dart | 124 ++++ .../bluetooth/BluetoothDeviceListEntry.dart | 74 +++ lib/modules/bluetooth/ChatPage.dart | 239 +++++++ lib/modules/bluetooth/DiscoveryPage.dart | 163 +++++ lib/modules/bluetooth/MainPage.dart | 354 ++++++++++ .../bluetooth/SelectBondedDevicePage.dart | 146 +++++ lib/modules/bluetooth/bluetooth_screen.dart | 20 + lib/modules/bluetooth/helpers/LineChart.dart | 605 ++++++++++++++++++ lib/modules/bluetooth/helpers/PaintStyle.dart | 263 ++++++++ lib/modules/home_page/screen/home_screen.dart | 12 +- pubspec.lock | 196 +++--- pubspec.yaml | 8 +- 16 files changed, 2339 insertions(+), 165 deletions(-) create mode 100644 android/app/proguard-rules.pro create mode 100644 lib/modules/bluetooth/BackgroundCollectedPage.dart create mode 100644 lib/modules/bluetooth/BackgroundCollectingTask.dart create mode 100644 lib/modules/bluetooth/BluetoothDeviceListEntry.dart create mode 100644 lib/modules/bluetooth/ChatPage.dart create mode 100644 lib/modules/bluetooth/DiscoveryPage.dart create mode 100644 lib/modules/bluetooth/MainPage.dart create mode 100644 lib/modules/bluetooth/SelectBondedDevicePage.dart create mode 100644 lib/modules/bluetooth/bluetooth_screen.dart create mode 100644 lib/modules/bluetooth/helpers/LineChart.dart create mode 100644 lib/modules/bluetooth/helpers/PaintStyle.dart diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro new file mode 100644 index 0000000..ad4f552 --- /dev/null +++ b/android/app/proguard-rules.pro @@ -0,0 +1 @@ +-keep class com.lib.flutter_blue_plus.* { *; } \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 5e74459..9b63373 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,68 +1,93 @@ - - - + package="com.zenithaerospace.monitor"> - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/main.dart b/lib/main.dart index 651d02c..fdc23fa 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -13,7 +13,6 @@ import 'package:zenith_monitor/modules/terminal/bloc/terminal_bloc.dart'; import 'package:zenith_monitor/modules/terminal/screen/terminal_screen.dart'; import 'package:zenith_monitor/utils/services/usb/usb.dart'; import 'package:zenith_monitor/core/pipelines/data_pipeline/data_bloc.dart'; -import 'package:zenith_monitor/modules/forget_password/screen/forgot_my_password_screen.dart'; import 'package:zenith_monitor/modules/login/bloc/login_bloc.dart'; import 'package:zenith_monitor/modules/map/screen/map_screen.dart'; import 'package:zenith_monitor/modules/signup/screen/sign_up_screen.dart'; diff --git a/lib/modules/bluetooth/BackgroundCollectedPage.dart b/lib/modules/bluetooth/BackgroundCollectedPage.dart new file mode 100644 index 0000000..298cc16 --- /dev/null +++ b/lib/modules/bluetooth/BackgroundCollectedPage.dart @@ -0,0 +1,145 @@ +import 'package:flutter/material.dart'; + +import './BackgroundCollectingTask.dart'; +import './helpers/LineChart.dart'; +import './helpers/PaintStyle.dart'; + +class BackgroundCollectedPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + final BackgroundCollectingTask task = + BackgroundCollectingTask.of(context, rebuildOnChange: true); + + // Arguments shift is needed for timestamps as miliseconds in double could loose precision. + final int argumentsShift = + task.samples.first.timestamp.millisecondsSinceEpoch; + + final Duration showDuration = + Duration(hours: 2); // @TODO . show duration should be configurable + final Iterable lastSamples = task.getLastOf(showDuration); + + final Iterable arguments = lastSamples.map((sample) { + return (sample.timestamp.millisecondsSinceEpoch - argumentsShift) + .toDouble(); + }); + + // Step for argument labels + final Duration argumentsStep = + Duration(minutes: 15); // @TODO . step duration should be configurable + + // Find first timestamp floored to step before + final DateTime beginningArguments = lastSamples.first.timestamp; + DateTime beginningArgumentsStep = DateTime(beginningArguments.year, + beginningArguments.month, beginningArguments.day); + while (beginningArgumentsStep.isBefore(beginningArguments)) { + beginningArgumentsStep = beginningArgumentsStep.add(argumentsStep); + } + beginningArgumentsStep = beginningArgumentsStep.subtract(argumentsStep); + final DateTime endingArguments = lastSamples.last.timestamp; + + // Generate list of timestamps of labels + final Iterable argumentsLabelsTimestamps = () sync* { + DateTime timestamp = beginningArgumentsStep; + yield timestamp; + while (timestamp.isBefore(endingArguments)) { + timestamp = timestamp.add(argumentsStep); + yield timestamp; + } + }(); + + // Map strings for labels + final Iterable argumentsLabels = + argumentsLabelsTimestamps.map((timestamp) { + return LabelEntry( + (timestamp.millisecondsSinceEpoch - argumentsShift).toDouble(), + ((timestamp.hour <= 9 ? '0' : '') + + timestamp.hour.toString() + + ':' + + (timestamp.minute <= 9 ? '0' : '') + + timestamp.minute.toString())); + }); + + return Scaffold( + appBar: AppBar( + title: Text('Collected data'), + actions: [ + // Progress circle + (task.inProgress + ? FittedBox( + child: Container( + margin: new EdgeInsets.all(16.0), + child: CircularProgressIndicator( + valueColor: + AlwaysStoppedAnimation(Colors.white)))) + : Container(/* Dummy */)), + // Start/stop buttons + (task.inProgress + ? IconButton(icon: Icon(Icons.pause), onPressed: task.pause) + : IconButton( + icon: Icon(Icons.play_arrow), onPressed: task.reasume)), + ], + ), + body: ListView( + children: [ + Divider(), + ListTile( + leading: const Icon(Icons.brightness_7), + title: const Text('Temperatures'), + subtitle: const Text('In Celsius'), + ), + LineChart( + constraints: const BoxConstraints.expand(height: 350), + arguments: arguments, + argumentsLabels: argumentsLabels, + values: [ + lastSamples.map((sample) => sample.temperature1), + lastSamples.map((sample) => sample.temperature2), + ], + verticalLinesStyle: const PaintStyle(color: Colors.grey), + additionalMinimalHorizontalLabelsInterval: 0, + additionalMinimalVerticalLablesInterval: 0, + seriesPointsStyles: [ + null, + null, + //const PaintStyle(style: PaintingStyle.stroke, strokeWidth: 1.7*3, color: Colors.indigo, strokeCap: StrokeCap.round), + ], + seriesLinesStyles: [ + const PaintStyle( + style: PaintingStyle.stroke, + strokeWidth: 1.7, + color: Colors.indigoAccent), + const PaintStyle( + style: PaintingStyle.stroke, + strokeWidth: 1.7, + color: Colors.redAccent), + ], + ), + Divider(), + ListTile( + leading: const Icon(Icons.filter_vintage), + title: const Text('Water pH level'), + ), + LineChart( + constraints: const BoxConstraints.expand(height: 200), + arguments: arguments, + argumentsLabels: argumentsLabels, + values: [ + lastSamples.map((sample) => sample.waterpHlevel), + ], + verticalLinesStyle: const PaintStyle(color: Colors.grey), + additionalMinimalHorizontalLabelsInterval: 0, + additionalMinimalVerticalLablesInterval: 0, + seriesPointsStyles: [ + null, + ], + seriesLinesStyles: [ + const PaintStyle( + style: PaintingStyle.stroke, + strokeWidth: 1.7, + color: Colors.greenAccent), + ], + ), + ], + )); + } +} diff --git a/lib/modules/bluetooth/BackgroundCollectingTask.dart b/lib/modules/bluetooth/BackgroundCollectingTask.dart new file mode 100644 index 0000000..502423a --- /dev/null +++ b/lib/modules/bluetooth/BackgroundCollectingTask.dart @@ -0,0 +1,124 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart'; +import 'package:scoped_model/scoped_model.dart'; + +class DataSample { + double temperature1; + double temperature2; + double waterpHlevel; + DateTime timestamp; + + DataSample({ + required this.temperature1, + required this.temperature2, + required this.waterpHlevel, + required this.timestamp, + }); +} + +class BackgroundCollectingTask extends Model { + static BackgroundCollectingTask of( + BuildContext context, { + bool rebuildOnChange = false, + }) => + ScopedModel.of( + context, + rebuildOnChange: rebuildOnChange, + ); + + final BluetoothConnection _connection; + List _buffer = List.empty(growable: true); + + // @TODO , Such sample collection in real code should be delegated + // (via `Stream` preferably) and then saved for later + // displaying on chart (or even stright prepare for displaying). + // @TODO ? should be shrinked at some point, endless colleting data would cause memory shortage. + List samples = List.empty(growable: true); + + bool inProgress = false; + + BackgroundCollectingTask._fromConnection(this._connection) { + _connection.input!.listen((data) { + _buffer += data; + + while (true) { + // If there is a sample, and it is full sent + int index = _buffer.indexOf('t'.codeUnitAt(0)); + if (index >= 0 && _buffer.length - index >= 7) { + final DataSample sample = DataSample( + temperature1: (_buffer[index + 1] + _buffer[index + 2] / 100), + temperature2: (_buffer[index + 3] + _buffer[index + 4] / 100), + waterpHlevel: (_buffer[index + 5] + _buffer[index + 6] / 100), + timestamp: DateTime.now()); + _buffer.removeRange(0, index + 7); + + samples.add(sample); + notifyListeners(); // Note: It shouldn't be invoked very often - in this example data comes at every second, but if there would be more data, it should update (including repaint of graphs) in some fixed interval instead of after every sample. + //print("${sample.timestamp.toString()} -> ${sample.temperature1} / ${sample.temperature2}"); + } + // Otherwise break + else { + break; + } + } + }).onDone(() { + inProgress = false; + notifyListeners(); + }); + } + + static Future connect( + BluetoothDevice server) async { + final BluetoothConnection connection = + await BluetoothConnection.toAddress(server.address); + return BackgroundCollectingTask._fromConnection(connection); + } + + void dispose() { + _connection.dispose(); + } + + Future start() async { + inProgress = true; + _buffer.clear(); + samples.clear(); + notifyListeners(); + _connection.output.add(ascii.encode('start')); + await _connection.output.allSent; + } + + Future cancel() async { + inProgress = false; + notifyListeners(); + _connection.output.add(ascii.encode('stop')); + await _connection.finish(); + } + + Future pause() async { + inProgress = false; + notifyListeners(); + _connection.output.add(ascii.encode('stop')); + await _connection.output.allSent; + } + + Future reasume() async { + inProgress = true; + notifyListeners(); + _connection.output.add(ascii.encode('start')); + await _connection.output.allSent; + } + + Iterable getLastOf(Duration duration) { + DateTime startingTime = DateTime.now().subtract(duration); + int i = samples.length; + do { + i -= 1; + if (i <= 0) { + break; + } + } while (samples[i].timestamp.isAfter(startingTime)); + return samples.getRange(i, samples.length); + } +} diff --git a/lib/modules/bluetooth/BluetoothDeviceListEntry.dart b/lib/modules/bluetooth/BluetoothDeviceListEntry.dart new file mode 100644 index 0000000..3915353 --- /dev/null +++ b/lib/modules/bluetooth/BluetoothDeviceListEntry.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart'; + +class BluetoothDeviceListEntry extends ListTile { + BluetoothDeviceListEntry({ + required BluetoothDevice device, + int? rssi, + GestureTapCallback? onTap, + GestureLongPressCallback? onLongPress, + bool enabled = true, + }) : super( + onTap: onTap, + onLongPress: onLongPress, + enabled: enabled, + leading: const Icon( + Icons.devices), // @TODO . !BluetoothClass! class aware icon + title: Text(device.name ?? ""), + subtitle: Text(device.address.toString()), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + rssi != null + ? Container( + margin: const EdgeInsets.all(8.0), + child: DefaultTextStyle( + style: _computeTextStyle(rssi), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text(rssi.toString()), + const Text('dBm'), + ], + ), + ), + ) + : Container(width: 0, height: 0), + device.isConnected + ? const Icon(Icons.import_export) + : Container(width: 0, height: 0), + device.isBonded + ? const Icon(Icons.link) + : Container(width: 0, height: 0), + ], + ), + ); + + static TextStyle _computeTextStyle(int rssi) { + /**/ if (rssi >= -35) { + return TextStyle(color: Colors.greenAccent[700]); + } else if (rssi >= -45) { + return TextStyle( + color: Color.lerp( + Colors.greenAccent[700], Colors.lightGreen, -(rssi + 35) / 10)); + } else if (rssi >= -55) { + return TextStyle( + color: Color.lerp( + Colors.lightGreen, Colors.lime[600], -(rssi + 45) / 10)); + } else if (rssi >= -65) { + return TextStyle( + color: Color.lerp(Colors.lime[600], Colors.amber, -(rssi + 55) / 10)); + } else if (rssi >= -75) { + return TextStyle( + color: Color.lerp( + Colors.amber, Colors.deepOrangeAccent, -(rssi + 65) / 10)); + } else if (rssi >= -85) { + return TextStyle( + color: Color.lerp( + Colors.deepOrangeAccent, Colors.redAccent, -(rssi + 75) / 10)); + } else { + /*code symmetry*/ + return const TextStyle(color: Colors.redAccent); + } + } +} diff --git a/lib/modules/bluetooth/ChatPage.dart b/lib/modules/bluetooth/ChatPage.dart new file mode 100644 index 0000000..67af307 --- /dev/null +++ b/lib/modules/bluetooth/ChatPage.dart @@ -0,0 +1,239 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart'; + +class ChatPage extends StatefulWidget { + final BluetoothDevice server; + + const ChatPage({required this.server}); + + @override + _ChatPage createState() => new _ChatPage(); +} + +class _Message { + int whom; + String text; + + _Message(this.whom, this.text); +} + +class _ChatPage extends State { + static final clientID = 0; + BluetoothConnection? connection; + + List<_Message> messages = List<_Message>.empty(growable: true); + String _messageBuffer = ''; + + final TextEditingController textEditingController = + new TextEditingController(); + final ScrollController listScrollController = new ScrollController(); + + bool isConnecting = true; + bool get isConnected => (connection?.isConnected ?? false); + + bool isDisconnecting = false; + + @override + void initState() { + super.initState(); + + BluetoothConnection.toAddress(widget.server.address).then((_connection) { + print('Connected to the device'); + connection = _connection; + setState(() { + isConnecting = false; + isDisconnecting = false; + }); + + connection!.input!.listen(_onDataReceived).onDone(() { + // Example: Detect which side closed the connection + // There should be `isDisconnecting` flag to show are we are (locally) + // in middle of disconnecting process, should be set before calling + // `dispose`, `finish` or `close`, which all causes to disconnect. + // If we except the disconnection, `onDone` should be fired as result. + // If we didn't except this (no flag set), it means closing by remote. + if (isDisconnecting) { + print('Disconnecting locally!'); + } else { + print('Disconnected remotely!'); + } + if (this.mounted) { + setState(() {}); + } + }); + }).catchError((error) { + print('Cannot connect, exception occured'); + print(error); + }); + } + + @override + void dispose() { + // Avoid memory leak (`setState` after dispose) and disconnect + if (isConnected) { + isDisconnecting = true; + connection?.dispose(); + connection = null; + } + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final List list = messages.map((_message) { + return Row( + children: [ + Container( + child: Text( + (text) { + return text == '/shrug' ? '¯\\_(ツ)_/¯' : text; + }(_message.text.trim()), + style: TextStyle(color: Colors.white)), + padding: EdgeInsets.all(12.0), + margin: EdgeInsets.only(bottom: 8.0, left: 8.0, right: 8.0), + width: 222.0, + decoration: BoxDecoration( + color: + _message.whom == clientID ? Colors.blueAccent : Colors.grey, + borderRadius: BorderRadius.circular(7.0)), + ), + ], + mainAxisAlignment: _message.whom == clientID + ? MainAxisAlignment.end + : MainAxisAlignment.start, + ); + }).toList(); + + final serverName = widget.server.name ?? "Unknown"; + return Scaffold( + appBar: AppBar( + title: (isConnecting + ? Text('Connecting chat to ' + serverName + '...') + : isConnected + ? Text('Live chat with ' + serverName) + : Text('Chat log with ' + serverName))), + body: SafeArea( + child: Column( + children: [ + Flexible( + child: ListView( + padding: const EdgeInsets.all(12.0), + controller: listScrollController, + children: list), + ), + Row( + children: [ + Flexible( + child: Container( + margin: const EdgeInsets.only(left: 16.0), + child: TextField( + style: const TextStyle(fontSize: 15.0), + controller: textEditingController, + decoration: InputDecoration.collapsed( + hintText: isConnecting + ? 'Wait until connected...' + : isConnected + ? 'Type your message...' + : 'Chat got disconnected', + hintStyle: const TextStyle(color: Colors.grey), + ), + enabled: isConnected, + ), + ), + ), + Container( + margin: const EdgeInsets.all(8.0), + child: IconButton( + icon: const Icon(Icons.send), + onPressed: isConnected + ? () => _sendMessage(textEditingController.text) + : null), + ), + ], + ) + ], + ), + ), + ); + } + + void _onDataReceived(Uint8List data) { + // Allocate buffer for parsed data + int backspacesCounter = 0; + data.forEach((byte) { + if (byte == 8 || byte == 127) { + backspacesCounter++; + } + }); + Uint8List buffer = Uint8List(data.length - backspacesCounter); + int bufferIndex = buffer.length; + + // Apply backspace control character + backspacesCounter = 0; + for (int i = data.length - 1; i >= 0; i--) { + if (data[i] == 8 || data[i] == 127) { + backspacesCounter++; + } else { + if (backspacesCounter > 0) { + backspacesCounter--; + } else { + buffer[--bufferIndex] = data[i]; + } + } + } + + // Create message if there is new line character + String dataString = String.fromCharCodes(buffer); + int index = buffer.indexOf(13); + if (~index != 0) { + setState(() { + messages.add( + _Message( + 1, + backspacesCounter > 0 + ? _messageBuffer.substring( + 0, _messageBuffer.length - backspacesCounter) + : _messageBuffer + dataString.substring(0, index), + ), + ); + _messageBuffer = dataString.substring(index); + }); + } else { + _messageBuffer = (backspacesCounter > 0 + ? _messageBuffer.substring( + 0, _messageBuffer.length - backspacesCounter) + : _messageBuffer + dataString); + } + } + + void _sendMessage(String text) async { + text = text.trim(); + textEditingController.clear(); + + if (text.length > 0) { + try { + connection!.output.add(Uint8List.fromList(utf8.encode(text + "\r\n"))); + await connection!.output.allSent; + + setState(() { + messages.add(_Message(clientID, text)); + }); + + Future.delayed(Duration(milliseconds: 333)).then((_) { + listScrollController.animateTo( + listScrollController.position.maxScrollExtent, + duration: Duration(milliseconds: 333), + curve: Curves.easeOut); + }); + } catch (e) { + // Ignore error, but notify state + setState(() {}); + } + } + } +} diff --git a/lib/modules/bluetooth/DiscoveryPage.dart b/lib/modules/bluetooth/DiscoveryPage.dart new file mode 100644 index 0000000..200dc0d --- /dev/null +++ b/lib/modules/bluetooth/DiscoveryPage.dart @@ -0,0 +1,163 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart'; + +import './BluetoothDeviceListEntry.dart'; + +class DiscoveryPage extends StatefulWidget { + /// If true, discovery starts on page start, otherwise user must press action button. + final bool start; + + const DiscoveryPage({this.start = true}); + + @override + DiscoveryPageState createState() => DiscoveryPageState(); +} + +class DiscoveryPageState extends State { + StreamSubscription? _streamSubscription; + List results = + List.empty(growable: true); + bool isDiscovering = false; + + DiscoveryPageState(); + + @override + void initState() { + super.initState(); + + isDiscovering = widget.start; + if (isDiscovering) { + _startDiscovery(); + } + } + + void _restartDiscovery() { + setState(() { + results.clear(); + isDiscovering = true; + }); + + _startDiscovery(); + } + + void _startDiscovery() { + _streamSubscription = + FlutterBluetoothSerial.instance.startDiscovery().listen((r) { + setState(() { + final existingIndex = results.indexWhere( + (element) => element.device.address == r.device.address); + if (existingIndex >= 0) { + results[existingIndex] = r; + } else { + results.add(r); + } + }); + }); + + _streamSubscription!.onDone(() { + setState(() { + isDiscovering = false; + }); + }); + } + + // @TODO . One day there should be `_pairDevice` on long tap on something... ;) + + @override + void dispose() { + // Avoid memory leak (`setState` after dispose) and cancel discovery + _streamSubscription?.cancel(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: isDiscovering + ? const Text('Discovering devices') + : const Text('Discovered devices'), + actions: [ + isDiscovering + ? FittedBox( + child: Container( + margin: const EdgeInsets.all(16.0), + child: const CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(Colors.white), + ), + ), + ) + : IconButton( + icon: const Icon(Icons.replay), + onPressed: _restartDiscovery, + ) + ], + ), + body: ListView.builder( + itemCount: results.length, + itemBuilder: (BuildContext context, index) { + BluetoothDiscoveryResult result = results[index]; + final device = result.device; + final address = device.address; + return BluetoothDeviceListEntry( + device: device, + rssi: result.rssi, + onTap: () { + Navigator.of(context).pop(result.device); + }, + onLongPress: () async { + try { + bool bonded = false; + if (device.isBonded) { + print('Unbonding from ${device.address}...'); + await FlutterBluetoothSerial.instance + .removeDeviceBondWithAddress(address); + print('Unbonding from ${device.address} has succed'); + } else { + print('Bonding with ${device.address}...'); + bonded = (await FlutterBluetoothSerial.instance + .bondDeviceAtAddress(address))!; + print( + 'Bonding with ${device.address} has ${bonded ? 'succed' : 'failed'}.'); + } + setState(() { + results[results.indexOf(result)] = BluetoothDiscoveryResult( + device: BluetoothDevice( + name: device.name ?? '', + address: address, + type: device.type, + bondState: bonded + ? BluetoothBondState.bonded + : BluetoothBondState.none, + ), + rssi: result.rssi); + }); + } catch (ex) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Error occured while bonding'), + content: Text("${ex.toString()}"), + actions: [ + new TextButton( + child: const Text("Close"), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } + }, + ); + }, + ), + ); + } +} diff --git a/lib/modules/bluetooth/MainPage.dart b/lib/modules/bluetooth/MainPage.dart new file mode 100644 index 0000000..6030d8f --- /dev/null +++ b/lib/modules/bluetooth/MainPage.dart @@ -0,0 +1,354 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart'; +import 'package:scoped_model/scoped_model.dart'; + +import './BackgroundCollectedPage.dart'; +import './BackgroundCollectingTask.dart'; +import './ChatPage.dart'; +import './DiscoveryPage.dart'; +import './SelectBondedDevicePage.dart'; + +// import './helpers/LineChart.dart'; + +class MainPage extends StatefulWidget { + @override + MainPageState createState() => MainPageState(); +} + +class MainPageState extends State { + BluetoothState _bluetoothState = BluetoothState.UNKNOWN; + + String _address = "..."; + String _name = "..."; + + Timer? _discoverableTimeoutTimer; + int _discoverableTimeoutSecondsLeft = 0; + + BackgroundCollectingTask? _collectingTask; + + bool _autoAcceptPairingRequests = false; + + @override + void initState() { + super.initState(); + + // Get current state + FlutterBluetoothSerial.instance.state.then((state) { + setState(() { + _bluetoothState = state; + }); + }); + + Future.doWhile(() async { + // Wait if adapter not enabled + if ((await FlutterBluetoothSerial.instance.isEnabled) ?? false) { + return false; + } + await Future.delayed(const Duration(milliseconds: 0xDD)); + return true; + }).then((_) { + // Update the address field + FlutterBluetoothSerial.instance.address.then((address) { + setState(() { + _address = address!; + }); + }); + }); + + FlutterBluetoothSerial.instance.name.then((name) { + setState(() { + _name = name!; + }); + }); + + // Listen for futher state changes + FlutterBluetoothSerial.instance + .onStateChanged() + .listen((BluetoothState state) { + setState(() { + _bluetoothState = state; + + // Discoverable mode is disabled when Bluetooth gets disabled + _discoverableTimeoutTimer = null; + _discoverableTimeoutSecondsLeft = 0; + }); + }); + } + + @override + void dispose() { + FlutterBluetoothSerial.instance.setPairingRequestHandler(null); + _collectingTask?.dispose(); + _discoverableTimeoutTimer?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Flutter Bluetooth Serial'), + ), + body: ListView( + children: [ + const Divider(), + const ListTile(title: Text('General')), + SwitchListTile( + title: const Text('Enable Bluetooth'), + value: _bluetoothState.isEnabled, + onChanged: (bool value) { + // Do the request and update with the true value then + future() async { + // async lambda seems to not working + if (value) { + await FlutterBluetoothSerial.instance.requestEnable(); + } else { + await FlutterBluetoothSerial.instance.requestDisable(); + } + } + + future().then((_) { + setState(() {}); + }); + }, + ), + ListTile( + title: const Text('Bluetooth status'), + subtitle: Text(_bluetoothState.toString()), + trailing: ElevatedButton( + child: const Text('Settings'), + onPressed: () { + FlutterBluetoothSerial.instance.openSettings(); + }, + ), + ), + ListTile( + title: const Text('Local adapter address'), + subtitle: Text(_address), + ), + ListTile( + title: const Text('Local adapter name'), + subtitle: Text(_name), + onLongPress: null, + ), + ListTile( + title: _discoverableTimeoutSecondsLeft == 0 + ? const Text("Discoverable") + : Text("Discoverable for ${_discoverableTimeoutSecondsLeft}s"), + subtitle: const Text("PsychoX-Luna"), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Checkbox( + value: _discoverableTimeoutSecondsLeft != 0, + onChanged: null, + ), + const IconButton( + icon: Icon(Icons.edit), + onPressed: null, + ), + IconButton( + icon: const Icon(Icons.refresh), + onPressed: () async { + print('Discoverable requested'); + final int timeout = (await FlutterBluetoothSerial.instance + .requestDiscoverable(60))!; + if (timeout < 0) { + print('Discoverable mode denied'); + } else { + print('Discoverable mode acquired for $timeout seconds'); + } + setState(() { + _discoverableTimeoutTimer?.cancel(); + _discoverableTimeoutSecondsLeft = timeout; + _discoverableTimeoutTimer = Timer.periodic( + const Duration(seconds: 1), (Timer timer) { + setState(() { + if (_discoverableTimeoutSecondsLeft < 0) { + FlutterBluetoothSerial.instance.isDiscoverable + .then((isDiscoverable) { + if (isDiscoverable ?? false) { + print( + "Discoverable after timeout... might be infinity timeout :F"); + _discoverableTimeoutSecondsLeft += 1; + } + }); + timer.cancel(); + _discoverableTimeoutSecondsLeft = 0; + } else { + _discoverableTimeoutSecondsLeft -= 1; + } + }); + }); + }); + }, + ) + ], + ), + ), + const Divider(), + const ListTile(title: Text('Devices discovery and connection')), + SwitchListTile( + title: const Text('Auto-try specific pin when pairing'), + subtitle: const Text('Pin 1234'), + value: _autoAcceptPairingRequests, + onChanged: (bool value) { + setState(() { + _autoAcceptPairingRequests = value; + }); + if (value) { + FlutterBluetoothSerial.instance.setPairingRequestHandler( + (BluetoothPairingRequest request) { + print("Trying to auto-pair with Pin 1234"); + if (request.pairingVariant == PairingVariant.Pin) { + return Future.value("1234"); + } + return Future.value(null); + }); + } else { + FlutterBluetoothSerial.instance.setPairingRequestHandler(null); + } + }, + ), + ListTile( + title: ElevatedButton( + child: const Text('Explore discovered devices'), + onPressed: () async { + final BluetoothDevice? selectedDevice = + await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) { + return const DiscoveryPage(); + }, + ), + ); + + if (selectedDevice != null) { + print('Discovery -> selected ${selectedDevice.address}'); + } else { + print('Discovery -> no device selected'); + } + }), + ), + ListTile( + title: ElevatedButton( + child: const Text('Connect to paired device to chat'), + onPressed: () async { + final BluetoothDevice? selectedDevice = + await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) { + return const SelectBondedDevicePage( + checkAvailability: false); + }, + ), + ); + + if (selectedDevice != null) { + print('Connect -> selected ${selectedDevice.address}'); + _startChat(context, selectedDevice); + } else { + print('Connect -> no device selected'); + } + }, + ), + ), + const Divider(), + const ListTile(title: Text('Multiple connections example')), + ListTile( + title: ElevatedButton( + child: ((_collectingTask?.inProgress ?? false) + ? const Text('Disconnect and stop background collecting') + : const Text('Connect to start background collecting')), + onPressed: () async { + if (_collectingTask?.inProgress ?? false) { + await _collectingTask!.cancel(); + setState(() { + /* Update for `_collectingTask.inProgress` */ + }); + } else { + final BluetoothDevice? selectedDevice = + await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) { + return const SelectBondedDevicePage( + checkAvailability: false); + }, + ), + ); + + if (selectedDevice != null) { + await _startBackgroundTask(context, selectedDevice); + setState(() { + /* Update for `_collectingTask.inProgress` */ + }); + } + } + }, + ), + ), + ListTile( + title: ElevatedButton( + onPressed: (_collectingTask != null) + ? () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) { + return ScopedModel( + model: _collectingTask!, + child: BackgroundCollectedPage(), + ); + }, + ), + ); + } + : null, + child: const Text('View background collected data'), + ), + ), + ], + ), + ); + } + + void _startChat(BuildContext context, BluetoothDevice server) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) { + return ChatPage(server: server); + }, + ), + ); + } + + Future _startBackgroundTask( + BuildContext context, + BluetoothDevice server, + ) async { + try { + _collectingTask = await BackgroundCollectingTask.connect(server); + await _collectingTask!.start(); + } catch (ex) { + _collectingTask?.cancel(); + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Error occured while connecting'), + content: Text("${ex.toString()}"), + actions: [ + new TextButton( + child: const Text("Close"), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } + } +} diff --git a/lib/modules/bluetooth/SelectBondedDevicePage.dart b/lib/modules/bluetooth/SelectBondedDevicePage.dart new file mode 100644 index 0000000..01a26fa --- /dev/null +++ b/lib/modules/bluetooth/SelectBondedDevicePage.dart @@ -0,0 +1,146 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart'; + +import './BluetoothDeviceListEntry.dart'; + +class SelectBondedDevicePage extends StatefulWidget { + /// If true, on page start there is performed discovery upon the bonded devices. + /// Then, if they are not avaliable, they would be disabled from the selection. + final bool checkAvailability; + + const SelectBondedDevicePage({this.checkAvailability = true}); + + @override + SelectBondedDevicePageState createState() => SelectBondedDevicePageState(); +} + +enum DeviceAvailability { + no, + maybe, + yes, +} + +class DeviceWithAvailability { + BluetoothDevice device; + DeviceAvailability availability; + int? rssi; + + DeviceWithAvailability(this.device, this.availability, [this.rssi]); +} + +class SelectBondedDevicePageState extends State { + List devices = + List.empty(growable: true); + + // Availability + StreamSubscription? _discoveryStreamSubscription; + bool _isDiscovering = false; + + SelectBondedDevicePageState(); + + @override + void initState() { + super.initState(); + + _isDiscovering = widget.checkAvailability; + + if (_isDiscovering) { + _startDiscovery(); + } + + // Setup a list of the bonded devices + FlutterBluetoothSerial.instance + .getBondedDevices() + .then((List bondedDevices) { + setState(() { + devices = bondedDevices + .map( + (device) => DeviceWithAvailability( + device, + widget.checkAvailability + ? DeviceAvailability.maybe + : DeviceAvailability.yes, + ), + ) + .toList(); + }); + }); + } + + void _restartDiscovery() { + setState(() { + _isDiscovering = true; + }); + + _startDiscovery(); + } + + void _startDiscovery() { + _discoveryStreamSubscription = + FlutterBluetoothSerial.instance.startDiscovery().listen((r) { + setState(() { + Iterator i = devices.iterator; + while (i.moveNext()) { + var device = i.current; + if (device.device == r.device) { + device.availability = DeviceAvailability.yes; + device.rssi = r.rssi; + } + } + }); + }); + + _discoveryStreamSubscription?.onDone(() { + setState(() { + _isDiscovering = false; + }); + }); + } + + @override + void dispose() { + // Avoid memory leak (`setState` after dispose) and cancel discovery + _discoveryStreamSubscription?.cancel(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + List list = devices + .map((device) => BluetoothDeviceListEntry( + device: device.device, + rssi: device.rssi, + enabled: device.availability == DeviceAvailability.yes, + onTap: () { + Navigator.of(context).pop(device.device); + }, + )) + .toList(); + return Scaffold( + appBar: AppBar( + title: const Text('Select device'), + actions: [ + _isDiscovering + ? FittedBox( + child: Container( + margin: const EdgeInsets.all(16.0), + child: const CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Colors.white, + ), + ), + ), + ) + : IconButton( + icon: const Icon(Icons.replay), + onPressed: _restartDiscovery, + ) + ], + ), + body: ListView(children: list), + ); + } +} diff --git a/lib/modules/bluetooth/bluetooth_screen.dart b/lib/modules/bluetooth/bluetooth_screen.dart new file mode 100644 index 0000000..89359fc --- /dev/null +++ b/lib/modules/bluetooth/bluetooth_screen.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; +// import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +// import 'package:zenith_monitor/modules/bluetooth/device_caracteristics_screen.dart'; +import './MainPage.dart'; + +class BluetoothScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + home: ExampleApplication(), + ); + } +} + +class ExampleApplication extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp(home: MainPage()); + } +} diff --git a/lib/modules/bluetooth/helpers/LineChart.dart b/lib/modules/bluetooth/helpers/LineChart.dart new file mode 100644 index 0000000..e29ba97 --- /dev/null +++ b/lib/modules/bluetooth/helpers/LineChart.dart @@ -0,0 +1,605 @@ +/// @name LineChart +/// @version 0.0.5 +/// @description Simple line chart widget +/// @author Patryk "PsychoX" Ludwikowski +/// @license MIT License (see https://mit-license.org/) +import 'dart:math' as math show min, max; +import 'dart:ui' as ui; + +import 'package:flutter/material.dart'; + +import './PaintStyle.dart'; + +class LabelEntry { + final double value; + final String label; + + LabelEntry(this.value, this.label); +} + +/// Widget that allows to show data on line chart. +/// +/// All arguments, values and labels data should be sorted! +/// Since both the arguments and the values must be `double` type, +/// be aware of the precision. +class LineChart extends StatelessWidget { + /// Constraints for the line chart. + final BoxConstraints constraints; + + // @TODO ? Both `_LineChartPainter` and `LineChart` have most the same fields. + // `LineChart` is just mainly passing them to the painter. Shouldn't there be + // only one class containing these data? Some `LineChartData` forged inside here + // and then passed and used by the painter? :thinking: + + /// Padding around main drawng area. Necessary for displaying labels (around the chart). + final EdgeInsets padding; + + /* Arguments */ + /// Collection of doubles as arguments. + final Iterable arguments; + + /// Mappings of strings for doubles arguments, which allow to specify custom + /// strings as labels for certain arguments. + final Iterable argumentsLabels; + + /* Values */ + /// Collection of data series as collections of next values on corresponding arguments. + final Iterable> values; + + /// Mappings of string for doubles values, which allow to specify custom + /// string as labels for certain values. + final Iterable? valuesLabels; + + /* Labels & lines styles */ + /// Style of horizontal lines labels + final TextStyle? horizontalLabelsTextStyle; + + /// Style of vertical lines labels + final TextStyle? verticalLabelsTextStyle; + + /// Defines style of horizontal lines. Might be null in order to prevent lines from drawing. + final Paint? horizontalLinesPaint; + + /// Defines style of vertical lines. Might be null in order to prevent lines from drawing. + final Paint? verticalLinesPaint; + + // @TODO . expose it + final bool snapToLeftLabel = false; + final bool snapToTopLabel = true; + final bool snapToRightLabel = false; + final bool snapToBottomLabel = true; + + /* Series points & lines styles */ + /// List of paint styles for series values points. + /// + /// On whole list null would use predefined set of styles. + /// On list entry null there will be no points for certain series. + final List seriesPointsPaints; + + /// List of paint styles for lines between next series points. + /// + /// On null there will be no lines. + final List? seriesLinesPaints; + + final double additionalMinimalHorizontalLabelsInterval; + final double additionalMinimalVerticalLablesInterval; + + LineChart({ + required this.constraints, + this.padding = const EdgeInsets.fromLTRB(32, 12, 20, 28), + required this.arguments, + required this.argumentsLabels, + required this.values, + this.valuesLabels, + this.horizontalLabelsTextStyle, + this.verticalLabelsTextStyle, + PaintStyle horizontalLinesStyle = const PaintStyle(color: Colors.grey), + PaintStyle? verticalLinesStyle, // null for default + + this.additionalMinimalHorizontalLabelsInterval = 8, + this.additionalMinimalVerticalLablesInterval = 8, + Iterable? + seriesPointsStyles, // null would use predefined set of styles + Iterable? seriesLinesStyles, // null for default + }) : horizontalLinesPaint = horizontalLinesStyle.toPaint(), + verticalLinesPaint = verticalLinesStyle?.toPaint(), + seriesPointsPaints = _prepareSeriesPointsPaints(seriesPointsStyles), + seriesLinesPaints = _prepareSeriesLinesPaints(seriesLinesStyles) { + if ((seriesPointsStyles?.length ?? values.length) < values.length && + 12 /* default paints */ < values.length) { + throw "Too few `seriesPointsPaintStyle`s! Try define more or limit number of displayed series"; + } + if ((seriesLinesStyles?.length ?? values.length) < values.length) { + throw "Too few `seriesLinesStyles`s! Try define more or limit number of displayed series"; + } + } + + static List _prepareSeriesPointsPaints( + Iterable? seriesPointsStyles) { + if (seriesPointsStyles == null) { + // Default paint for points + return List.unmodifiable([ + PaintStyle(strokeWidth: 1.7, color: Colors.blue).toPaint(), + PaintStyle(strokeWidth: 1.7, color: Colors.red).toPaint(), + PaintStyle(strokeWidth: 1.7, color: Colors.yellow).toPaint(), + PaintStyle(strokeWidth: 1.7, color: Colors.green).toPaint(), + + PaintStyle(strokeWidth: 1.7, color: Colors.purple).toPaint(), + PaintStyle(strokeWidth: 1.7, color: Colors.deepOrange).toPaint(), + PaintStyle(strokeWidth: 1.7, color: Colors.brown).toPaint(), + PaintStyle(strokeWidth: 1.7, color: Colors.lime).toPaint(), + + PaintStyle(strokeWidth: 1.7, color: Colors.indigo).toPaint(), + PaintStyle(strokeWidth: 1.7, color: Colors.pink).toPaint(), + PaintStyle(strokeWidth: 1.7, color: Colors.amber).toPaint(), + PaintStyle(strokeWidth: 1.7, color: Colors.teal).toPaint(), + + // For more, user should specify them :F + ]); + } else { + return seriesPointsStyles.map((style) => style?.toPaint()).toList(); + } + } + + static List? _prepareSeriesLinesPaints( + Iterable? seriesLinesStyles) { + if (seriesLinesStyles == null) { + return null; + } else { + return seriesLinesStyles.map((style) => style.toPaint()).toList(); + } + } + + @override + Widget build(BuildContext context) { + return ConstrainedBox( + constraints: this.constraints, + child: CustomPaint( + painter: _LineChartPainter( + padding: padding, + arguments: arguments, + argumentsLabels: argumentsLabels, + values: values, + valuesLabels: valuesLabels, + horizontalLabelsTextStyle: horizontalLabelsTextStyle ?? + Theme.of(context).textTheme.bodySmall, + verticalLabelsTextStyle: + verticalLabelsTextStyle ?? Theme.of(context).textTheme.bodySmall, + horizontalLinesPaint: horizontalLinesPaint, + verticalLinesPaint: verticalLinesPaint, + additionalMinimalHorizontalLabelsInterval: + additionalMinimalHorizontalLabelsInterval, + additionalMinimalVerticalLablesInterval: + additionalMinimalVerticalLablesInterval, + seriesPointsPaints: seriesPointsPaints, + seriesLinesPaints: seriesLinesPaints, + ))); + } +} + +class _LineChartPainter extends CustomPainter { + /// Padding around main drawng area. Necessary for displaying labels (around the chart). + final EdgeInsets padding; + + /* Arguments */ + /// Collection of doubles as arguments. + final Iterable arguments; + + /// Mappings of strings for doubles arguments, which allow to specify custom + /// strings as labels for certain arguments. + final Iterable? argumentsLabels; + + /* Values */ + /// Collection of data series as collections of next values on corresponding arguments. + final Iterable> values; + + /// Mappings of string for doubles values, which allow to specify custom + /// string as labels for certain values. + final Iterable? valuesLabels; + + /* Labels & lines styles */ + /// Style of horizontal lines labels + final TextStyle? horizontalLabelsTextStyle; + + /// Style of vertical lines labels + final TextStyle? verticalLabelsTextStyle; + + /// Defines style of horizontal lines. Might be null in order to prevent lines from drawing. + final Paint? horizontalLinesPaint; + + /// Defines style of vertical lines. Might be null in order to prevent lines from drawing. + final Paint? verticalLinesPaint; + + // @TODO . expose it + final bool snapToLeftLabel = false; + final bool snapToTopLabel = true; + final bool snapToRightLabel = false; + final bool snapToBottomLabel = true; + + /* Series points & lines styles */ + /// Collection of paint styles for series values points. + /// + /// On whole argument null would use predefined set of styles. + /// On collection entry null there will be no points for certain series. + final Iterable seriesPointsPaints; + + /// Collection of paint styles for lines between next series points. + /// + /// On null there will be no lines. + final Iterable? seriesLinesPaints; + + /* Runtime */ + /// Minimal allowed interval between horizontal lines. Calculated from font size. + final double minimalHorizontalLabelsInterval; + + /// Maximal value of all data series values + double maxValue = -double.maxFinite; + + /// Minimal value of all data series values + double minValue = double.maxFinite; + + double _minimalHorizontalRatio = 0; + double _minimalVerticalRatio = 0; + + /// Creates `_LineChartPainter` (`CustomPainter`) with given data and styling. + _LineChartPainter({ + this.padding = const EdgeInsets.fromLTRB(40, 8, 8, 32), + required this.arguments, + required this.argumentsLabels, + required this.values, + required this.valuesLabels, + required this.horizontalLabelsTextStyle, + required this.verticalLabelsTextStyle, + required this.horizontalLinesPaint, + required this.verticalLinesPaint, + double additionalMinimalHorizontalLabelsInterval = 8, + double additionalMinimalVerticalLablesInterval = 8, + required this.seriesPointsPaints, + required this.seriesLinesPaints, + }) : this.minimalHorizontalLabelsInterval = + (horizontalLabelsTextStyle?.fontSize ?? 12) + + additionalMinimalHorizontalLabelsInterval { + // Find max & min values of data to be show + for (Iterable series in values) { + for (double value in series) { + if (value > maxValue) { + maxValue = value; + } else if (value < minValue) { + minValue = value; + } + } + } + + if (valuesLabels != null) { + // Find minimal vertical ratio to fit all provided values labels + Iterator entry = valuesLabels!.iterator; + entry.moveNext(); + double lastValue = entry.current.value; + + while (entry.moveNext()) { + final double goodRatio = + minimalHorizontalLabelsInterval / (entry.current.value - lastValue); + if (goodRatio > _minimalVerticalRatio) { + _minimalVerticalRatio = goodRatio; + } + + lastValue = entry.current.value; + } + } + + if (argumentsLabels != null) { + // Find minimal horizontal ratio to fit all provided arguments labels + Iterator entry = argumentsLabels!.iterator; + entry.moveNext(); + double lastValue = entry.current.value; + double lastWidth = + _getLabelTextPainter(entry.current.label, verticalLabelsTextStyle) + .width; + + while (entry.moveNext()) { + final double nextValue = entry.current.value; + final double nextWidth = + _getLabelTextPainter(entry.current.label, verticalLabelsTextStyle) + .width; + + final double goodRatio = ((lastWidth + nextWidth) / 2 + + additionalMinimalVerticalLablesInterval) / + (nextValue - lastValue); + if (goodRatio > _minimalHorizontalRatio) { + _minimalHorizontalRatio = goodRatio; + } + + lastValue = nextValue; + lastWidth = nextWidth; + } + } + } + + @override + void paint(Canvas canvas, Size size) { + final double width = size.width - padding.left - padding.right; + final double height = size.height - padding.top - padding.bottom; + + /* Horizontal lines with labels */ + double valuesOffset = 0; // @TODO ? could be used in future for scrolling + double verticalRatio; + + { + Iterable labels; + + // If no labels provided - generate them! + if (valuesLabels == null) { + final double optimalStepValue = + _calculateOptimalStepValue(maxValue - minValue, height); + int stepsNumber = 1; + + // Find bottom line value + double bottomValue = 0; + if (minValue > 0) { + while (bottomValue < minValue) { + bottomValue += optimalStepValue; + } + bottomValue -= optimalStepValue; + } else { + while (bottomValue > minValue) { + bottomValue -= optimalStepValue; + } + } + valuesOffset = bottomValue; + + // Find top line value + double topValue = bottomValue; + while (topValue < maxValue) { + topValue += optimalStepValue; + stepsNumber += 1; + } + + // Set labels iterable from prepared generator + Iterable generator(double optimalStepValue, int stepsNumber, + [double value = 0.0]) sync* { + //double value = _bottomValue; + for (int i = 0; i < stepsNumber; i++) { + yield LabelEntry( + value, + value + .toString()); // @TODO , choose better precision based on optimal step value while parsing to string + value += optimalStepValue; + } + } + + labels = generator(optimalStepValue, stepsNumber, bottomValue); + + if (!snapToTopLabel) { + topValue = maxValue; + } + if (!snapToBottomLabel) { + bottomValue = valuesOffset = minValue; + } + + // Calculate vertical ratio of pixels per value + // Note: There is no empty space already + verticalRatio = height / (topValue - bottomValue); + } + // If labels provided - use them + else { + // Set labels iterable as the provided list + labels = valuesLabels!; + + // Use minimal visible value as offset. + // Note: `minValue` is calculated in constructor and includes miniaml labels values. + valuesOffset = minValue; + + // Calculate vertical ratio of pixels per value + // Note: `_minimalVerticalRatio` is calculated in constructor + final double topValue = + snapToTopLabel ? math.max(maxValue, labels.last.value) : maxValue; + final double bottomValue = snapToBottomLabel + ? math.min(minValue, labels.first.value) + : minValue; + final double noEmptySpaceRatio = height / (topValue - bottomValue); + verticalRatio = math.max(_minimalVerticalRatio, noEmptySpaceRatio); + } + + // Draw the horizontal lines and labels + for (LabelEntry tuple in labels) { + if (tuple.value < valuesOffset) continue; + final double yOffset = (size.height - + padding.bottom - + (tuple.value - valuesOffset) * verticalRatio); + if (yOffset < padding.top) break; + + // Draw line + if (horizontalLinesPaint != null) { + canvas.drawLine( + Offset(padding.left, yOffset), + Offset(size.width - padding.right, yOffset), + horizontalLinesPaint!); + } + + // Draw label + TextPainter( + text: TextSpan(text: tuple.label, style: horizontalLabelsTextStyle), + textAlign: TextAlign.right, + textDirection: TextDirection.ltr) + ..layout(minWidth: padding.left - 4) + ..paint( + canvas, + Offset( + 0, + yOffset - + (horizontalLabelsTextStyle?.fontSize ?? 12) / 2 - + 1)); + } + } + + /* Vertical lines with labels */ + double argumentsOffset = 0; + final double xOffsetLimit = size.width - padding.right; + double horizontalRatio; + + { + Iterable labels; + + // If no labels provided - generate them! + if (argumentsLabels == null) { + throw "not implemented"; + // @TODO . after few hot days of thinking about the problem for 1-2 hour a day, I just gave up. + // The hardest in the problem is that there must be trade-off between space for labels and max lines, + // but keep in mind that the label values should be in some human-readable steps (0.5, 10, 0.02...). + } + // If labels provided - use them + else { + // Set labels iterable as the provided list + labels = argumentsLabels!; + + // Use first visible argument as arguments offset + argumentsOffset = labels.first.value; + + if (!snapToLeftLabel) { + argumentsOffset = arguments.first; + } + + // Calculate vertical ratio of pixels per value + // Note: `_minimalHorizontalRatio` is calculated in constructor + final double leftMost = snapToLeftLabel + ? math.min(arguments.first, labels.first.value) + : arguments.first; + final double rightMost = snapToRightLabel + ? math.max(arguments.last, labels.last.value) + : arguments.last; + final double noEmptySpaceRatio = width / (rightMost - leftMost); + horizontalRatio = math.max(_minimalHorizontalRatio, noEmptySpaceRatio); + } + + // Draw the vertical lines and labels + for (LabelEntry tuple in labels) { + if (tuple.value < argumentsOffset) continue; + final double xOffset = + padding.left + (tuple.value - argumentsOffset) * horizontalRatio; + if (xOffset > xOffsetLimit) break; + + // Draw line + if (verticalLinesPaint != null) { + canvas.drawLine( + Offset(xOffset, padding.top), + Offset(xOffset, size.height - padding.bottom), + verticalLinesPaint!); + } + + // Draw label + final TextPainter textPainter = TextPainter( + text: TextSpan(text: tuple.label, style: verticalLabelsTextStyle), + textDirection: TextDirection.ltr) + ..layout(); + textPainter.paint( + canvas, + Offset(xOffset - textPainter.width / 2, + size.height - (verticalLabelsTextStyle?.fontSize ?? 12) - 8)); + } + } + + /* Points and lines between subsequent */ + Iterator> series = values.iterator; + Iterator linesPaints = seriesLinesPaints == null + ? [].iterator + : (seriesLinesPaints ?? []).iterator; + Iterator pointsPaints = seriesPointsPaints.iterator; + while (series.moveNext()) { + List points = []; + Iterator value = series.current.iterator; + Iterator argument = arguments.iterator; + while (value.moveNext()) { + argument.moveNext(); + if (value.current == null || value.current == double.nan) continue; + + if (argument.current < argumentsOffset) continue; + final double xOffset = padding.left + + (argument.current - argumentsOffset) * horizontalRatio; + if (xOffset > xOffsetLimit) break; + + if (value.current! < valuesOffset) continue; + final double yOffset = size.height - + padding.bottom - + (value.current! - valuesOffset) * verticalRatio; + if (yOffset < padding.top) continue; + + points.add(Offset(xOffset, yOffset)); + } + + // Lines + if (linesPaints.moveNext() && linesPaints.current != null) { + canvas.drawPath( + Path()..addPolygon(points, false), linesPaints.current!); + } + + // Points + if (pointsPaints.moveNext() && pointsPaints.current != null) { + canvas.drawPoints(ui.PointMode.points, points, pointsPaints.current!); + } + } + } + + @override + bool shouldRepaint(_LineChartPainter old) => + (this.arguments != old.arguments || + this.values != old.values || + this.argumentsLabels != old.argumentsLabels || + this.valuesLabels != old.valuesLabels || + this.seriesPointsPaints != old.seriesPointsPaints || + this.seriesLinesPaints != old.seriesLinesPaints || + this.horizontalLabelsTextStyle != old.horizontalLabelsTextStyle || + this.verticalLabelsTextStyle != old.verticalLabelsTextStyle || + this.padding != old.padding // + ); + + // ..., 0.01, 0.02, 0.05, 0.1, [0.125], 0.2, [0.25], 0.5, 1, 2, 5, 10, 20, 50, 100, 200, 500, ... + double _calculateOptimalStepValue(double valueRange, double height) { + final int maxSteps = height ~/ minimalHorizontalLabelsInterval; + if (maxSteps <= 0) { + throw "invalid max lines!"; + } + double interval = valueRange / maxSteps; + if (interval > 1) { + int zeros = 0; + while (interval >= 10) { + interval = interval / 10; + zeros += 1; + } + /**/ if (interval <= 1) { + interval = 1; + } else if (interval <= 2) { + interval = 2; + } else if (interval <= 5) { + interval = 5; + } + for (; zeros-- != 0;) { + interval *= 10; + } + } else { + // @TODO ! not working at all for lower :C + int zeros = 0; + while (interval < 0) { + interval = interval * 10; + zeros += 1; + } + /**/ if (interval <= 1) { + interval = 1; + } else if (interval <= 2) { + interval = 2; + } else if (interval <= 5) { + interval = 5; + } + for (; zeros-- != 0;) { + interval /= 10; + } + } + return interval; + } + + TextPainter _getLabelTextPainter(String text, TextStyle? style) { + return TextPainter( + text: TextSpan(text: text, style: style), + textDirection: TextDirection.ltr) + ..layout(); + } +} diff --git a/lib/modules/bluetooth/helpers/PaintStyle.dart b/lib/modules/bluetooth/helpers/PaintStyle.dart new file mode 100644 index 0000000..6479cb7 --- /dev/null +++ b/lib/modules/bluetooth/helpers/PaintStyle.dart @@ -0,0 +1,263 @@ +import 'dart:ui'; + +/// A description of the style to use when drawing on a [Canvas]. +/// +/// Most APIs on [Canvas] take a [Paint] object to describe the style +/// to use for that operation. [PaintStyle] allows to be const +/// constructed and later in runtime forged into the [Paint] object. +class PaintStyle { + /// Whether to apply anti-aliasing to lines and images drawn on the + /// canvas. + /// + /// Defaults to true. + final bool isAntiAlias; + + // Must be kept in sync with the default in paint.cc. + static const int _kColorDefault = 0xFF000000; + + /// The color to use when stroking or filling a shape. + /// + /// Defaults to opaque black. + /// + /// See also: + /// + /// * [style], which controls whether to stroke or fill (or both). + /// * [colorFilter], which overrides [color]. + /// * [shader], which overrides [color] with more elaborate effects. + /// + /// This color is not used when compositing. To colorize a layer, use + /// [colorFilter]. + final Color? color; + + // Must be kept in sync with the default in paint.cc. + static final int _kBlendModeDefault = BlendMode.srcOver.index; + + /// A blend mode to apply when a shape is drawn or a layer is composited. + /// + /// The source colors are from the shape being drawn (e.g. from + /// [Canvas.drawPath]) or layer being composited (the graphics that were drawn + /// between the [Canvas.saveLayer] and [Canvas.restore] calls), after applying + /// the [colorFilter], if any. + /// + /// The destination colors are from the background onto which the shape or + /// layer is being composited. + /// + /// Defaults to [BlendMode.srcOver]. + /// + /// See also: + /// + /// * [Canvas.saveLayer], which uses its [Paint]'s [blendMode] to composite + /// the layer when [restore] is called. + /// * [BlendMode], which discusses the user of [saveLayer] with [blendMode]. + final BlendMode blendMode; + + /// Whether to paint inside shapes, the edges of shapes, or both. + /// + /// Defaults to [PaintingStyle.fill]. + final PaintingStyle style; + + /// How wide to make edges drawn when [style] is set to + /// [PaintingStyle.stroke]. The width is given in logical pixels measured in + /// the direction orthogonal to the direction of the path. + /// + /// Defaults to 0.0, which correspond to a hairline width. + final double strokeWidth; + + /// The kind of finish to place on the end of lines drawn when + /// [style] is set to [PaintingStyle.stroke]. + /// + /// Defaults to [StrokeCap.butt], i.e. no caps. + final StrokeCap strokeCap; + + /// The kind of finish to place on the joins between segments. + /// + /// This applies to paths drawn when [style] is set to [PaintingStyle.stroke], + /// It does not apply to points drawn as lines with [Canvas.drawPoints]. + /// + /// Defaults to [StrokeJoin.miter], i.e. sharp corners. + /// + /// Some examples of joins: + /// + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_4_join.mp4} + /// + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/round_join.mp4} + /// + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/bevel_join.mp4} + /// + /// The centers of the line segments are colored in the diagrams above to + /// highlight the joins, but in normal usage the join is the same color as the + /// line. + /// + /// See also: + /// + /// * [strokeMiterLimit] to control when miters are replaced by bevels when + /// this is set to [StrokeJoin.miter]. + /// * [strokeCap] to control what is drawn at the ends of the stroke. + /// * [StrokeJoin] for the definitive list of stroke joins. + final StrokeJoin strokeJoin; + + // Must be kept in sync with the default in paint.cc. + static const double _kStrokeMiterLimitDefault = 4.0; + + /// The limit for miters to be drawn on segments when the join is set to + /// [StrokeJoin.miter] and the [style] is set to [PaintingStyle.stroke]. If + /// this limit is exceeded, then a [StrokeJoin.bevel] join will be drawn + /// instead. This may cause some 'popping' of the corners of a path if the + /// angle between line segments is animated, as seen in the diagrams below. + /// + /// This limit is expressed as a limit on the length of the miter. + /// + /// Defaults to 4.0. Using zero as a limit will cause a [StrokeJoin.bevel] + /// join to be used all the time. + /// + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_0_join.mp4} + /// + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_4_join.mp4} + /// + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_6_join.mp4} + /// + /// The centers of the line segments are colored in the diagrams above to + /// highlight the joins, but in normal usage the join is the same color as the + /// line. + /// + /// See also: + /// + /// * [strokeJoin] to control the kind of finish to place on the joins + /// between segments. + /// * [strokeCap] to control what is drawn at the ends of the stroke. + final double strokeMiterLimit; + + /// A mask filter (for example, a blur) to apply to a shape after it has been + /// drawn but before it has been composited into the image. + /// + /// See [MaskFilter] for details. + final MaskFilter? maskFilter; + + /// Controls the performance vs quality trade-off to use when applying + /// filters, such as [maskFilter], or when drawing images, as with + /// [Canvas.drawImageRect] or [Canvas.drawImageNine]. + /// + /// Defaults to [FilterQuality.none]. + // TODO(ianh): verify that the image drawing methods actually respect this + final FilterQuality filterQuality; + + /// The shader to use when stroking or filling a shape. + /// + /// When this is null, the [color] is used instead. + /// + /// See also: + /// + /// * [Gradient], a shader that paints a color gradient. + /// * [ImageShader], a shader that tiles an [Image]. + /// * [colorFilter], which overrides [shader]. + /// * [color], which is used if [shader] and [colorFilter] are null. + final Shader? shader; + + /// A color filter to apply when a shape is drawn or when a layer is + /// composited. + /// + /// See [ColorFilter] for details. + /// + /// When a shape is being drawn, [colorFilter] overrides [color] and [shader]. + final ColorFilter? colorFilter; + + /// Whether the colors of the image are inverted when drawn. + /// + /// Inverting the colors of an image applies a new color filter that will + /// be composed with any user provided color filters. This is primarily + /// used for implementing smart invert on iOS. + final bool invertColors; + + const PaintStyle({ + this.isAntiAlias = true, + this.color = const Color(_kColorDefault), + this.blendMode = BlendMode.srcOver, + this.style = PaintingStyle.fill, + this.strokeWidth = 0.0, + this.strokeCap = StrokeCap.butt, + this.strokeJoin = StrokeJoin.miter, + this.strokeMiterLimit = 4.0, + this.maskFilter, // null + this.filterQuality = FilterQuality.none, + this.shader, // null + this.colorFilter, // null + this.invertColors = false, + }); + + @override + String toString() { + final StringBuffer result = StringBuffer(); + String semicolon = ''; + result.write('PaintStyle('); + if (style == PaintingStyle.stroke) { + result.write('$style'); + if (strokeWidth != 0.0) + result.write(' ${strokeWidth.toStringAsFixed(1)}'); + else + result.write(' hairline'); + if (strokeCap != StrokeCap.butt) result.write(' $strokeCap'); + if (strokeJoin == StrokeJoin.miter) { + if (strokeMiterLimit != _kStrokeMiterLimitDefault) + result.write( + ' $strokeJoin up to ${strokeMiterLimit.toStringAsFixed(1)}'); + } else { + result.write(' $strokeJoin'); + } + semicolon = '; '; + } + if (isAntiAlias != true) { + result.write('${semicolon}antialias off'); + semicolon = '; '; + } + if (color != const Color(_kColorDefault)) { + if (color != null) + result.write('$semicolon$color'); + else + result.write('${semicolon}no color'); + semicolon = '; '; + } + if (blendMode.index != _kBlendModeDefault) { + result.write('$semicolon$blendMode'); + semicolon = '; '; + } + if (colorFilter != null) { + result.write('${semicolon}colorFilter: $colorFilter'); + semicolon = '; '; + } + if (maskFilter != null) { + result.write('${semicolon}maskFilter: $maskFilter'); + semicolon = '; '; + } + if (filterQuality != FilterQuality.none) { + result.write('${semicolon}filterQuality: $filterQuality'); + semicolon = '; '; + } + if (shader != null) { + result.write('${semicolon}shader: $shader'); + semicolon = '; '; + } + if (invertColors) result.write('${semicolon}invert: $invertColors'); + result.write(')'); + return result.toString(); + } + + Paint toPaint() { + Paint paint = Paint(); + if (this.isAntiAlias != true) paint.isAntiAlias = this.isAntiAlias; + if (this.color != const Color(_kColorDefault)) paint.color = this.color!; + if (this.blendMode != BlendMode.srcOver) paint.blendMode = this.blendMode; + if (this.style != PaintingStyle.fill) paint.style = this.style; + if (this.strokeWidth != 0.0) paint.strokeWidth = this.strokeWidth; + if (this.strokeCap != StrokeCap.butt) paint.strokeCap = this.strokeCap; + if (this.strokeJoin != StrokeJoin.miter) paint.strokeJoin = this.strokeJoin; + if (this.strokeMiterLimit != 4.0) + paint.strokeMiterLimit = this.strokeMiterLimit; + if (this.maskFilter != null) paint.maskFilter = this.maskFilter; + if (this.filterQuality != FilterQuality.none) + paint.filterQuality = this.filterQuality; + if (this.shader != null) paint.shader = this.shader; + if (this.colorFilter != null) paint.colorFilter = this.colorFilter; + if (this.invertColors != false) paint.invertColors = this.invertColors; + return paint; + } +} diff --git a/lib/modules/home_page/screen/home_screen.dart b/lib/modules/home_page/screen/home_screen.dart index 8262465..ef52383 100644 --- a/lib/modules/home_page/screen/home_screen.dart +++ b/lib/modules/home_page/screen/home_screen.dart @@ -1,15 +1,15 @@ import 'package:flutter/material.dart'; +import 'package:zenith_monitor/modules/bluetooth/bluetooth_screen.dart'; class HomeScreen extends StatelessWidget { @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('Home Screen'), - ), - body: const Center( - child: Text('Welcome to the Home Screen!'), + return MaterialApp( + title: 'Flutter Bluetooth App', + theme: ThemeData( + primarySwatch: Colors.blue, ), + home: BluetoothScreen(), // Defina a tela inicial como BluetoothScreen ); } } \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index d572dff..0c78173 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: b1595874fbc8f7a50da90f5d8f327bb0bfd6a95dc906c390efe991540c3b54aa + sha256: "9371d13b8ee442e3bfc08a24e3a1b3742c839abbfaf5eef11b79c4b862c89bf7" url: "https://pub.dev" source: hosted - version: "1.3.40" + version: "1.3.41" args: dependency: transitive description: @@ -61,26 +61,26 @@ packages: dependency: "direct main" description: name: cloud_firestore - sha256: "240c1c3598e62ad58ee665b6df9c65172d2fbe4742770c21ed060e013cb8e037" + sha256: "77ac40d66a0fd585ca1e80ef3cbfd9df34462103bd4c1cf5950cb7d1f4c2e188" url: "https://pub.dev" source: hosted - version: "5.1.0" + version: "5.3.0" cloud_firestore_platform_interface: dependency: "direct main" description: name: cloud_firestore_platform_interface - sha256: "5b5a9c2b5a85bf995f12e7447c4197d7ad659533d642d3d904ccbb509f83d62a" + sha256: da0fa07284bc845b4a517449365eac0346a084676b3ed399f75a0ed25c3ec4c8 url: "https://pub.dev" source: hosted - version: "6.2.9" + version: "6.3.2" cloud_firestore_web: dependency: transitive description: name: cloud_firestore_web - sha256: "898e9f65548df65ca7b2cff9f32cf233424ceccff1d870b8819ea2b8050d5b39" + sha256: "98dca6f864141cd3f79cf42a036f45233b8e2a3940b0011e6a370dd2283c45db" url: "https://pub.dev" source: hosted - version: "4.0.3" + version: "4.1.2" collection: dependency: transitive description: @@ -93,26 +93,26 @@ packages: dependency: "direct main" description: name: connectivity_plus - sha256: db7a4e143dc72cc3cb2044ef9b052a7ebfe729513e6a82943bc3526f784365b8 + sha256: "2056db5241f96cdc0126bd94459fc4cdc13876753768fc7a31c425e50a7177d0" url: "https://pub.dev" source: hosted - version: "6.0.3" + version: "6.0.5" connectivity_plus_platform_interface: dependency: transitive description: name: connectivity_plus_platform_interface - sha256: b6a56efe1e6675be240de39107281d4034b64ac23438026355b4234042a35adb + sha256: "42657c1715d48b167930d5f34d00222ac100475f73d10162ddf43e714932f204" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.0.1" cross_file: dependency: transitive description: name: cross_file - sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32" + sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" url: "https://pub.dev" source: hosted - version: "0.3.4+1" + version: "0.3.4+2" csslib: dependency: transitive description: @@ -149,10 +149,10 @@ packages: dependency: transitive description: name: facebook_auth_desktop - sha256: "0e4f147a57de8fdb8eaaee4836e6b9859482921143af0c350ffbf2a9bbd531a0" + sha256: "223e04414e078271682dadd2a694da1f4d329634137414b30217b2f478457fdb" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.1.0" fake_async: dependency: transitive description: @@ -165,10 +165,10 @@ packages: dependency: transitive description: name: ffi - sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" file_selector_linux: dependency: transitive description: @@ -197,42 +197,42 @@ packages: dependency: transitive description: name: file_selector_windows - sha256: d3547240c20cabf205c7c7f01a50ecdbc413755814d6677f3cb366f04abcead0 + sha256: "2ad726953f6e8affbc4df8dc78b77c3b4a060967a291e528ef72ae846c60fb69" url: "https://pub.dev" source: hosted - version: "0.9.3+1" + version: "0.9.3+2" firebase_auth: dependency: "direct main" description: name: firebase_auth - sha256: "2457ac6cbc152fa464aad3fb35f98039b0c4ab8e9bedf476672508b291bdbc3a" + sha256: "6f5792bdc208416bfdfbfe3363b78ce01667b6ebc4c5cb47cfa891f2fca45ab7" url: "https://pub.dev" source: hosted - version: "5.1.4" + version: "5.2.0" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface - sha256: "0408e2ed74b1afa0490a93aa041fe90d7573af7ffc59a641edc6c5b5c1b8d2a4" + sha256: "80237bb8a92bb0a5e3b40de1c8dbc80254e49ac9e3907b4b47b8e95ac3dd3fad" url: "https://pub.dev" source: hosted - version: "7.4.3" + version: "7.4.4" firebase_auth_web: dependency: transitive description: name: firebase_auth_web - sha256: "7e0c6d0fa8c5c1b2ae126a78f2d1a206a77a913f78d20f155487bf746162dccc" + sha256: "9d315491a6be65ea83511cb0e078544a309c39dd54c0ee355c51dbd6d8c03cc8" url: "https://pub.dev" source: hosted - version: "5.12.5" + version: "5.12.6" firebase_core: dependency: "direct main" description: name: firebase_core - sha256: "3187f4f8e49968573fd7403011dca67ba95aae419bc0d8131500fae160d94f92" + sha256: "06537da27db981947fa535bb91ca120b4e9cb59cb87278dbdde718558cafc9ff" url: "https://pub.dev" source: hosted - version: "3.3.0" + version: "3.4.0" firebase_core_platform_interface: dependency: "direct overridden" description: @@ -245,34 +245,34 @@ packages: dependency: transitive description: name: firebase_core_web - sha256: e8d1e22de72cb21cdcfc5eed7acddab3e99cd83f3b317f54f7a96c32f25fd11e + sha256: "362e52457ed2b7b180964769c1e04d1e0ea0259fdf7025fdfedd019d4ae2bd88" url: "https://pub.dev" source: hosted - version: "2.17.4" + version: "2.17.5" firebase_storage: dependency: "direct main" description: name: firebase_storage - sha256: "472e8532dfdcffd00ed8cc1ff57fda76af6f0a8a26547908bc20f25079922408" + sha256: dfc06d783dbc0b6200a4b936d8cdbd826bd1571c959854d14a70259188d96e85 url: "https://pub.dev" source: hosted - version: "12.1.1" + version: "12.2.0" firebase_storage_platform_interface: dependency: transitive description: name: firebase_storage_platform_interface - sha256: f07b5dbf0efcaab0f7d38910851791f7509b8864d491bb11c079f67339f31b27 + sha256: "3da511301b77514dee5370281923fbbc6d5725c2a0b96004c5c45415e067f234" url: "https://pub.dev" source: hosted - version: "5.1.26" + version: "5.1.28" firebase_storage_web: dependency: transitive description: name: firebase_storage_web - sha256: ef9d27afcd68a1c2b90d0cad2b02cf6333a053655988f4a2f6cba2796134cae5 + sha256: "7ad67b1c1c46c995a6bd4f225d240fc9a5fb277fade583631ae38750ffd9be17" url: "https://pub.dev" source: hosted - version: "3.9.11" + version: "3.9.13" flutter: dependency: "direct main" description: flutter @@ -286,6 +286,22 @@ packages: url: "https://pub.dev" source: hosted version: "8.1.6" + flutter_blue_plus: + dependency: "direct main" + description: + name: flutter_blue_plus + sha256: "55d37e9339765fef9439f3759a2bddaf9c8db1f90d46ba5a5d834fb28e0ab809" + url: "https://pub.dev" + source: hosted + version: "1.32.12" + flutter_bluetooth_serial: + dependency: "direct main" + description: + name: flutter_bluetooth_serial + sha256: "85ae82c4099b2b1facdc54e75e1bcfa88dc7f719e55dc886bb0b648cb16636b1" + url: "https://pub.dev" + source: hosted + version: "0.4.0" flutter_dotenv: dependency: "direct main" description: @@ -298,26 +314,26 @@ packages: dependency: "direct main" description: name: flutter_facebook_auth - sha256: "48aab06f3b1d3494a33b0c10c11ecbc226fdcf0bf37b478e91d9de57cce840f5" + sha256: faa92fb7626c230837f30d02de7d92b6af334b77ba744867c63545d4ad7e171f url: "https://pub.dev" source: hosted - version: "7.0.1" + version: "7.1.0" flutter_facebook_auth_platform_interface: dependency: transitive description: name: flutter_facebook_auth_platform_interface - sha256: dc9d621dd45c4f0b341173a16e94f4b77155fa9c0f4326743f1251f2f445ba38 + sha256: a416f89de881ba98188942060aecf17b434b06677d4557731f19e859343df54c url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "6.1.0" flutter_facebook_auth_web: dependency: transitive description: name: flutter_facebook_auth_web - sha256: "947d93fc5a7cc5db1ce0274505254bb3b619cdd98176954f125f742964696804" + sha256: "8a6890a98522604169ca9f958d7189c9f579dbf05ba4d3d7adf26eca4c4c4c93" url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "6.1.1" flutter_lints: dependency: "direct dev" description: @@ -330,10 +346,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: c6b0b4c05c458e1c01ad9bcc14041dd7b1f6783d487be4386f793f47a8a4d03e + sha256: "9ee02950848f61c4129af3d6ec84a1cfc0e47931abc746b03e7a3bc3e8ff6eda" url: "https://pub.dev" source: hosted - version: "2.0.20" + version: "2.0.22" flutter_polyline_points: dependency: "direct main" description: @@ -412,58 +428,58 @@ packages: dependency: transitive description: name: google_identity_services_web - sha256: "9482364c9f8b7bd36902572ebc3a7c2b5c8ee57a9c93e6eb5099c1a9ec5265d8" + sha256: "5be191523702ba8d7a01ca97c17fca096822ccf246b0a9f11923a6ded06199b6" url: "https://pub.dev" source: hosted - version: "0.3.1+1" + version: "0.3.1+4" google_maps: dependency: transitive description: name: google_maps - sha256: "47eef3836b49bb030d5cb3afc60b8451408bf34cf753e571b645d6529eb4251a" + sha256: "463b38e5a92a05cde41220a11fd5eef3847031fef3e8cf295ac76ec453246907" url: "https://pub.dev" source: hosted - version: "7.1.0" + version: "8.0.0" google_maps_flutter: dependency: "direct main" description: name: google_maps_flutter - sha256: acf0ec482d86b2ac55ade80597ce7f797a47971f5210ebfd030f0d58130e0a94 + sha256: "2e302fa3aaf4e2a297f0342d83ebc5e8e9f826e9a716aef473fe7f404ec630a7" url: "https://pub.dev" source: hosted - version: "2.7.0" + version: "2.9.0" google_maps_flutter_android: dependency: transitive description: name: google_maps_flutter_android - sha256: f6306d83edddba7aa017ca6f547d6f36a1443f90ed49d91d48ef70d7aa86e2e1 + sha256: "60a005bf1ba8d178144e442f6e2d734b0ffc2cc800a05415388472f934ad6d6a" url: "https://pub.dev" source: hosted - version: "2.12.0" + version: "2.14.4" google_maps_flutter_ios: dependency: transitive description: name: google_maps_flutter_ios - sha256: a6e3c6ecdda6c985053f944be13a0645ebb919da2ef0f5bc579c5e1670a5b2a8 + sha256: "3a484846fc56f15e47e3de1f5ea80a7ff2b31721d2faa88f390f3b3cf580c953" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.13.0" google_maps_flutter_platform_interface: dependency: transitive description: name: google_maps_flutter_platform_interface - sha256: bd60ca330e3c7763b95b477054adec338a522d982af73ecc520b232474063ac5 + sha256: "4f6930fd668bf5d40feb2695d5695dbc0c35e5542b557a34ad35be491686d2ba" url: "https://pub.dev" source: hosted - version: "2.8.0" + version: "2.9.0" google_maps_flutter_web: dependency: transitive description: name: google_maps_flutter_web - sha256: f3155c12119d8a5c2732fdf39ceb5cc095bc662059a03b4ea23294ecebe1d199 + sha256: ff39211bd25d7fad125d19f757eba85bd154460907cd4d135e07e3d0f98a4130 url: "https://pub.dev" source: hosted - version: "0.5.8" + version: "0.5.10" google_sign_in: dependency: "direct main" description: @@ -476,18 +492,18 @@ packages: dependency: transitive description: name: google_sign_in_android - sha256: d30fb34b659679ea74397e9748b4ab5d720720d57dcc79538f1b3c4a68654cb3 + sha256: "5a47ebec9af97daf0822e800e4f101c3340b5ebc3f6898cf860c1a71b53cf077" url: "https://pub.dev" source: hosted - version: "6.1.27" + version: "6.1.28" google_sign_in_ios: dependency: transitive description: name: google_sign_in_ios - sha256: a058c9880be456f21e2e8571c1126eaacd570bdc5b6c6d9d15aea4bdf22ca9fe + sha256: "4898410f55440049e1ba8f15411612d9f89299d89c61cd9baf7e02d56ff81ac7" url: "https://pub.dev" source: hosted - version: "5.7.6" + version: "5.7.7" google_sign_in_platform_interface: dependency: transitive description: @@ -500,18 +516,18 @@ packages: dependency: transitive description: name: google_sign_in_web - sha256: d606264c7a1a526a3aa79d938b85a601d8589731a478bd4a3dcbdeb14a572228 + sha256: "042805a21127a85b0dc46bba98a37926f17d2439720e8a459d27045d8ef68055" url: "https://pub.dev" source: hosted - version: "0.12.4+1" + version: "0.12.4+2" graphs: dependency: transitive description: name: graphs - sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" html: dependency: transitive description: @@ -572,18 +588,18 @@ packages: dependency: transitive description: name: image_picker_android - sha256: ff39a10ab4f48f4ac70776d0494a97bf073cd2570892cd46bc8a5cac162c25db + sha256: "8c5abf0dcc24fe6e8e0b4a5c0b51a5cf30cefdf6407a3213dae61edc75a70f56" url: "https://pub.dev" source: hosted - version: "0.8.12+4" + version: "0.8.12+12" image_picker_for_web: dependency: transitive description: name: image_picker_for_web - sha256: "5d6eb13048cd47b60dbf1a5495424dea226c5faf3950e20bf8120a58efb5b5f3" + sha256: "65d94623e15372c5c51bebbcb820848d7bcb323836e12dfdba60b5d3a8b39e50" url: "https://pub.dev" source: hosted - version: "3.0.4" + version: "3.0.5" image_picker_ios: dependency: transitive description: @@ -632,14 +648,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.7" - js_wrapping: - dependency: transitive - description: - name: js_wrapping - sha256: e385980f7c76a8c1c9a560dfb623b890975841542471eade630b2871d243851c - url: "https://pub.dev" - source: hosted - version: "0.7.4" leak_tracker: dependency: transitive description: @@ -724,10 +732,10 @@ packages: dependency: transitive description: name: mime - sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" + sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a" url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "1.0.6" nested: dependency: transitive description: @@ -756,18 +764,18 @@ packages: dependency: "direct main" description: name: path_provider - sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 + sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "30c5aa827a6ae95ce2853cdc5fe3971daaac00f6f081c419c013f7f57bff2f5e" + sha256: "6f01f8e37ec30b07bc424b4deabac37cacb1bc7e2e515ad74486039918a37eb7" url: "https://pub.dev" source: hosted - version: "2.2.7" + version: "2.2.10" path_provider_foundation: dependency: transitive description: @@ -836,18 +844,18 @@ packages: dependency: "direct main" description: name: rive - sha256: "3c0047e636ebe8e4044087e239dffdd026cf839fe9aecf55d53431b255668bcf" + sha256: daa5394a7d064b4997b39e9afa02f6882c479c38b19fa0dd60f052b99c105400 url: "https://pub.dev" source: hosted - version: "0.13.9" + version: "0.13.13" rive_common: dependency: transitive description: name: rive_common - sha256: "3fe76ba4680787741688ee393e47b63417e8643816795e4eac01021683af1d84" + sha256: c7bf0781b1621629361579c300ac2f8aa1a238227a242202a596e82becc244d7 url: "https://pub.dev" source: hosted - version: "0.4.9" + version: "0.4.11" sanitize_html: dependency: transitive description: @@ -856,6 +864,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + scoped_model: + dependency: "direct main" + description: + name: scoped_model + sha256: "8dacc77cb5de78d5e159d54cab883847491a73dfaa3d28c52f4ec8b0be32645b" + url: "https://pub.dev" + source: hosted + version: "2.0.0" sky_engine: dependency: transitive description: flutter @@ -961,10 +977,10 @@ packages: dependency: transitive description: name: win32 - sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 + sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a" url: "https://pub.dev" source: hosted - version: "5.5.1" + version: "5.5.4" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 615161f..2c20fd8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -21,10 +21,9 @@ environment: sdk: ">=2.12.0 <3.0.0" dependencies: - flutter: + flutter: sdk: flutter get: ^4.3.8 - # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. @@ -51,6 +50,9 @@ dependencies: flutter_dotenv: ^5.1.0 cloud_firestore_platform_interface: ^6.2.9 equatable: ^2.0.5 + flutter_blue_plus: ^1.32.12 + flutter_bluetooth_serial: ^0.4.0 + scoped_model: ^2.0.0 dependency_overrides: firebase_core_platform_interface: 5.1.0 @@ -70,7 +72,6 @@ flutter: # the material Icons class. uses-material-design: true - assets: - assets/images/ - assets/maps/ @@ -109,4 +110,3 @@ flutter: - asset: assets/fonts/DM_Sans/DMSans-Regular.ttf # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages - From 86c22cfbd58004f7a384606fd14cf25a67f0f1c6 Mon Sep 17 00:00:00 2001 From: Italo Matos Date: Fri, 30 Aug 2024 19:47:46 -0300 Subject: [PATCH 07/12] update chat page - bluetooth --- lib/modules/bluetooth/ChatPage.dart | 48 ++++++++++++++--------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/modules/bluetooth/ChatPage.dart b/lib/modules/bluetooth/ChatPage.dart index 67af307..cce7d10 100644 --- a/lib/modules/bluetooth/ChatPage.dart +++ b/lib/modules/bluetooth/ChatPage.dart @@ -11,26 +11,26 @@ class ChatPage extends StatefulWidget { const ChatPage({required this.server}); @override - _ChatPage createState() => new _ChatPage(); + ChatPageState createState() => ChatPageState(); } -class _Message { +class Message { int whom; String text; - _Message(this.whom, this.text); + Message(this.whom, this.text); } -class _ChatPage extends State { - static final clientID = 0; +class ChatPageState extends State { + static const clientID = 0; BluetoothConnection? connection; - List<_Message> messages = List<_Message>.empty(growable: true); + List messages = List.empty(growable: true); String _messageBuffer = ''; final TextEditingController textEditingController = new TextEditingController(); - final ScrollController listScrollController = new ScrollController(); + final ScrollController listScrollController = ScrollController(); bool isConnecting = true; bool get isConnected => (connection?.isConnected ?? false); @@ -87,25 +87,25 @@ class _ChatPage extends State { Widget build(BuildContext context) { final List list = messages.map((_message) { return Row( + mainAxisAlignment: _message.whom == clientID + ? MainAxisAlignment.end + : MainAxisAlignment.start, children: [ Container( - child: Text( - (text) { - return text == '/shrug' ? '¯\\_(ツ)_/¯' : text; - }(_message.text.trim()), - style: TextStyle(color: Colors.white)), - padding: EdgeInsets.all(12.0), - margin: EdgeInsets.only(bottom: 8.0, left: 8.0, right: 8.0), + padding: const EdgeInsets.all(12.0), + margin: const EdgeInsets.only(bottom: 8.0, left: 8.0, right: 8.0), width: 222.0, decoration: BoxDecoration( color: _message.whom == clientID ? Colors.blueAccent : Colors.grey, borderRadius: BorderRadius.circular(7.0)), + child: Text( + (text) { + return text == '/shrug' ? '¯\\_(ツ)_/¯' : text; + }(_message.text.trim()), + style: const TextStyle(color: Colors.white)), ), ], - mainAxisAlignment: _message.whom == clientID - ? MainAxisAlignment.end - : MainAxisAlignment.start, ); }).toList(); @@ -165,11 +165,11 @@ class _ChatPage extends State { void _onDataReceived(Uint8List data) { // Allocate buffer for parsed data int backspacesCounter = 0; - data.forEach((byte) { + for (var byte in data) { if (byte == 8 || byte == 127) { backspacesCounter++; } - }); + } Uint8List buffer = Uint8List(data.length - backspacesCounter); int bufferIndex = buffer.length; @@ -193,7 +193,7 @@ class _ChatPage extends State { if (~index != 0) { setState(() { messages.add( - _Message( + Message( 1, backspacesCounter > 0 ? _messageBuffer.substring( @@ -215,19 +215,19 @@ class _ChatPage extends State { text = text.trim(); textEditingController.clear(); - if (text.length > 0) { + if (text.isNotEmpty) { try { connection!.output.add(Uint8List.fromList(utf8.encode(text + "\r\n"))); await connection!.output.allSent; setState(() { - messages.add(_Message(clientID, text)); + messages.add(Message(clientID, text)); }); - Future.delayed(Duration(milliseconds: 333)).then((_) { + Future.delayed(const Duration(milliseconds: 333)).then((_) { listScrollController.animateTo( listScrollController.position.maxScrollExtent, - duration: Duration(milliseconds: 333), + duration: const Duration(milliseconds: 333), curve: Curves.easeOut); }); } catch (e) { From 40cb4c97d69473d739e5836e54b3059e873cc5be Mon Sep 17 00:00:00 2001 From: Italo Matos Date: Sun, 1 Sep 2024 21:40:31 -0300 Subject: [PATCH 08/12] add persistent user session --- lib/main.dart | 23 ++++++++++++++++------- lib/modules/login/bloc/login_bloc.dart | 13 +++++++------ lib/modules/login/bloc/login_state.dart | 4 ++++ lib/widgets/login.dart | 3 +++ 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index fdc23fa..b7b4dd0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -18,6 +18,7 @@ import 'package:zenith_monitor/modules/map/screen/map_screen.dart'; import 'package:zenith_monitor/modules/signup/screen/sign_up_screen.dart'; import 'package:zenith_monitor/utils/ui/animations/zenith_progress_indicator.dart'; import 'package:zenith_monitor/modules/login/screen/login_screen.dart'; +import 'package:firebase_auth/firebase_auth.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -42,17 +43,22 @@ class Application extends StatelessWidget { final Future _initialization = Firebase.initializeApp(); @override Widget build(BuildContext context) { - UsbManager usbManager = UsbManager(); - FirestoreServices fireServices = FirestoreServices(); + // UsbManager usbManager = UsbManager(); + // FirestoreServices fireServices = FirestoreServices(); return MultiBlocProvider( providers: [ BlocProvider(create: (context) => LoginBloc(auth: GoogleAuth())), + // BlocProvider( + // create: (context) => + // DataBloc(usbManager: usbManager, fireServices: fireServices)), + // BlocProvider( + // create: (context) => MapDataBloc( + // usbManager: usbManager, fireServices: fireServices)), BlocProvider( create: (context) => - DataBloc(usbManager: usbManager, fireServices: fireServices)), - BlocProvider( - create: (context) => MapDataBloc( - usbManager: usbManager, fireServices: fireServices)), + TerminalBloc(dataBloc: BlocProvider.of(context))), + + BlocProvider(create: (context) => LoginBloc(auth: GoogleAuth())), BlocProvider( create: (context) => TerminalBloc(dataBloc: BlocProvider.of(context))), @@ -65,6 +71,8 @@ class Application extends StatelessWidget { } if (snapshot.connectionState == ConnectionState.done) { + final User? currentUser = FirebaseAuth.instance.currentUser; + return MaterialApp( showPerformanceOverlay: false, // shows fps debugShowCheckedModeBanner: false, @@ -74,7 +82,8 @@ class Application extends StatelessWidget { backgroundColor: Colors.black.withOpacity(0)), primaryColor: Colors.black, ), - initialRoute: '/login', + initialRoute: currentUser != null ? '/home' : '/login', + // initialRoute: '/login', routes: { '/login': (context) => const LoginScreen(), '/signup': (context) => const SignUpScreen(), diff --git a/lib/modules/login/bloc/login_bloc.dart b/lib/modules/login/bloc/login_bloc.dart index 9b54350..6d6c18b 100644 --- a/lib/modules/login/bloc/login_bloc.dart +++ b/lib/modules/login/bloc/login_bloc.dart @@ -34,11 +34,12 @@ class LoginBloc extends Bloc { Future _onSignOut(SignOutEvent event, Emitter emit) async { await _auth.signOut(); - Navigator.pushAndRemoveUntil( - // ignore: use_build_context_synchronously - event.context, - MaterialPageRoute(builder: (BuildContext context) => const LoginScreen()), - ModalRoute.withName('/login'), - ); + emit(SignOutSuccess()); + // Navigator.pushAndRemoveUntil( + // // ignore: use_build_context_synchronously + // event.context, + // MaterialPageRoute(builder: (BuildContext context) => const LoginScreen()), + // ModalRoute.withName('/login'), + // ); } } diff --git a/lib/modules/login/bloc/login_state.dart b/lib/modules/login/bloc/login_state.dart index fe9d271..7fd28c8 100644 --- a/lib/modules/login/bloc/login_state.dart +++ b/lib/modules/login/bloc/login_state.dart @@ -19,3 +19,7 @@ class LoadingState extends LoginState {} class LoginSuccess extends LoginState { LoginSuccess(LocalUser newUser) : super(user: newUser); } + +class SignOutSuccess extends LoginState {} + +class CheckSessionEvent extends LoginEvent {} diff --git a/lib/widgets/login.dart b/lib/widgets/login.dart index 6bc2764..39b631d 100644 --- a/lib/widgets/login.dart +++ b/lib/widgets/login.dart @@ -43,6 +43,9 @@ class LoginWidgetState extends State { if (state is LoginSuccess) { Navigator.popAndPushNamed(context, '/home'); } + if (state is SignOutSuccess) { + Navigator.popAndPushNamed(context, '/login'); + } }, builder: (context, state) { if (state is LoadingState) { From 206e3b7819b915662fae6545e21a9c5983da230a Mon Sep 17 00:00:00 2001 From: Italo Matos Date: Sun, 1 Sep 2024 23:10:00 -0300 Subject: [PATCH 09/12] add google maps reference --- lib/main.dart | 18 ++++- lib/modules/bluetooth/ChatPage.dart | 72 +++++++++++++++---- lib/modules/bluetooth/MainPage.dart | 18 +++++ pubspec.lock | 104 ++++++++++++++++++++++++++++ pubspec.yaml | 2 + 5 files changed, 199 insertions(+), 15 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index b7b4dd0..f2c4a24 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -19,6 +19,7 @@ import 'package:zenith_monitor/modules/signup/screen/sign_up_screen.dart'; import 'package:zenith_monitor/utils/ui/animations/zenith_progress_indicator.dart'; import 'package:zenith_monitor/modules/login/screen/login_screen.dart'; import 'package:firebase_auth/firebase_auth.dart'; +import 'package:permission_handler/permission_handler.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -26,9 +27,22 @@ void main() async { options: DefaultFirebaseOptions.currentPlatform, ); await dotenv.load(fileName: ".env"); + await requestBluetoothPermissions(); runApp(const ZenithMonitor()); } +Future requestBluetoothPermissions() async { + final statusScan = await Permission.bluetoothScan.request(); + final statusConnect = await Permission.bluetoothConnect.request(); + final statusLocation = await Permission.locationWhenInUse.request(); + + if (!statusScan.isGranted || + !statusConnect.isGranted || + !statusLocation.isGranted) { + openAppSettings(); + } +} + class ZenithMonitor extends StatelessWidget { const ZenithMonitor({Key? key}) : super(key: key); @@ -82,8 +96,8 @@ class Application extends StatelessWidget { backgroundColor: Colors.black.withOpacity(0)), primaryColor: Colors.black, ), - initialRoute: currentUser != null ? '/home' : '/login', - // initialRoute: '/login', + //initialRoute: currentUser != null ? '/home' : '/login', + initialRoute: '/login', routes: { '/login': (context) => const LoginScreen(), '/signup': (context) => const SignUpScreen(), diff --git a/lib/modules/bluetooth/ChatPage.dart b/lib/modules/bluetooth/ChatPage.dart index cce7d10..77046b4 100644 --- a/lib/modules/bluetooth/ChatPage.dart +++ b/lib/modules/bluetooth/ChatPage.dart @@ -4,6 +4,7 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart'; +import 'package:url_launcher/url_launcher.dart'; class ChatPage extends StatefulWidget { final BluetoothDevice server; @@ -17,8 +18,10 @@ class ChatPage extends StatefulWidget { class Message { int whom; String text; + double? latitude; + double? longitude; - Message(this.whom, this.text); + Message(this.whom, this.text, {this.latitude, this.longitude}); } class ChatPageState extends State { @@ -28,8 +31,7 @@ class ChatPageState extends State { List messages = List.empty(growable: true); String _messageBuffer = ''; - final TextEditingController textEditingController = - new TextEditingController(); + final TextEditingController textEditingController = TextEditingController(); final ScrollController listScrollController = ScrollController(); bool isConnecting = true; @@ -99,11 +101,27 @@ class ChatPageState extends State { color: _message.whom == clientID ? Colors.blueAccent : Colors.grey, borderRadius: BorderRadius.circular(7.0)), - child: Text( - (text) { - return text == '/shrug' ? '¯\\_(ツ)_/¯' : text; - }(_message.text.trim()), - style: const TextStyle(color: Colors.white)), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + (text) { + return text == '/shrug' ? '¯\\_(ツ)_/¯' : text; + }(_message.text.trim()), + style: const TextStyle(color: Colors.white), + ), + if (_message.longitude != null && + _message.latitude != null) ...[ + const SizedBox(height: 8.0), + ElevatedButton( + onPressed: () { + openGoogleMaps(_message.latitude!, _message.longitude!); + }, + child: const Text('Abrir endereço no Google Maps'), + ), + ], + ], + ), ), ], ); @@ -113,10 +131,10 @@ class ChatPageState extends State { return Scaffold( appBar: AppBar( title: (isConnecting - ? Text('Connecting chat to ' + serverName + '...') + ? Text('Connecting chat to $serverName...') : isConnected - ? Text('Live chat with ' + serverName) - : Text('Chat log with ' + serverName))), + ? Text('Live chat with $serverName') + : Text('Chat log with $serverName'))), body: SafeArea( child: Column( children: [ @@ -162,6 +180,17 @@ class ChatPageState extends State { ); } + Future openGoogleMaps(double latitude, double longitude) async { + final Uri googleMapsUrl = Uri.parse( + 'https://www.google.com/maps/search/?api=1&query=$latitude,$longitude'); + + if (await canLaunchUrl(googleMapsUrl)) { + await launchUrl(googleMapsUrl); + } else { + throw 'Could not open the map.'; + } + } + void _onDataReceived(Uint8List data) { // Allocate buffer for parsed data int backspacesCounter = 0; @@ -187,8 +216,23 @@ class ChatPageState extends State { } } - // Create message if there is new line character + // Cria uma string a partir do buffer String dataString = String.fromCharCodes(buffer); + + // Divide a string em partes usando ponto e vírgula como delimitador + List parts = dataString.split(';'); + String message = dataString; // Mensagem original + double? longitude; + double? latitude; + + if (parts.length >= 3) { + // Extrai longitude e latitude (segundo e terceiro valores) + longitude = double.tryParse(parts[1].trim()); + latitude = double.tryParse(parts[2].trim()); + } + + // Create message if there is new line character + // String dataString = String.fromCharCodes(buffer); int index = buffer.indexOf(13); if (~index != 0) { setState(() { @@ -199,6 +243,8 @@ class ChatPageState extends State { ? _messageBuffer.substring( 0, _messageBuffer.length - backspacesCounter) : _messageBuffer + dataString.substring(0, index), + longitude: longitude, + latitude: latitude, ), ); _messageBuffer = dataString.substring(index); @@ -217,7 +263,7 @@ class ChatPageState extends State { if (text.isNotEmpty) { try { - connection!.output.add(Uint8List.fromList(utf8.encode(text + "\r\n"))); + connection!.output.add(Uint8List.fromList(utf8.encode("$text\r\n"))); await connection!.output.allSent; setState(() { diff --git a/lib/modules/bluetooth/MainPage.dart b/lib/modules/bluetooth/MainPage.dart index 6030d8f..845f882 100644 --- a/lib/modules/bluetooth/MainPage.dart +++ b/lib/modules/bluetooth/MainPage.dart @@ -9,6 +9,7 @@ import './BackgroundCollectingTask.dart'; import './ChatPage.dart'; import './DiscoveryPage.dart'; import './SelectBondedDevicePage.dart'; +import 'package:url_launcher/url_launcher.dart'; // import './helpers/LineChart.dart'; @@ -114,6 +115,12 @@ class MainPageState extends State { }); }, ), + ListTile( + title: const Text("Teste google maps"), + onTap: () async { + await openGoogleMaps(48.8584, 2.2945); + }, + ), ListTile( title: const Text('Bluetooth status'), subtitle: Text(_bluetoothState.toString()), @@ -323,6 +330,17 @@ class MainPageState extends State { ); } + Future openGoogleMaps(double latitude, double longitude) async { + final Uri googleMapsUrl = Uri.parse( + 'https://www.google.com/maps/search/?api=1&query=$latitude,$longitude'); + + if (await canLaunchUrl(googleMapsUrl)) { + await launchUrl(googleMapsUrl); + } else { + throw 'Could not open the map.'; + } + } + Future _startBackgroundTask( BuildContext context, BluetoothDevice server, diff --git a/pubspec.lock b/pubspec.lock index 0c78173..c077b49 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -808,6 +808,46 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + sha256: bc56bfe9d3f44c3c612d8d393bd9b174eb796d706759f9b495ac254e4294baa5 + url: "https://pub.dev" + source: hosted + version: "10.4.5" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: "59c6322171c29df93a22d150ad95f3aa19ed86542eaec409ab2691b8f35f9a47" + url: "https://pub.dev" + source: hosted + version: "10.3.6" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5" + url: "https://pub.dev" + source: hosted + version: "9.1.4" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4" + url: "https://pub.dev" + source: hosted + version: "3.12.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098 + url: "https://pub.dev" + source: hosted + version: "0.1.3" petitparser: dependency: transitive description: @@ -941,6 +981,70 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: "21b704ce5fa560ea9f3b525b43601c678728ba46725bab9b01187b4831377ed3" + url: "https://pub.dev" + source: hosted + version: "6.3.0" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: f0c73347dfcfa5b3db8bc06e1502668265d39c08f310c29bff4e28eea9699f79 + url: "https://pub.dev" + source: hosted + version: "6.3.9" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e + url: "https://pub.dev" + source: hosted + version: "6.3.1" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: e2b9622b4007f97f504cd64c0128309dfb978ae66adbe944125ed9e1750f06af + url: "https://pub.dev" + source: hosted + version: "3.2.0" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" + url: "https://pub.dev" + source: hosted + version: "2.3.3" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "49c10f879746271804767cb45551ec5592cdab00ee105c06dddde1a98f73b185" + url: "https://pub.dev" + source: hosted + version: "3.1.2" usb_serial: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 2c20fd8..3e91486 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -53,6 +53,8 @@ dependencies: flutter_blue_plus: ^1.32.12 flutter_bluetooth_serial: ^0.4.0 scoped_model: ^2.0.0 + url_launcher: ^6.1.10 + permission_handler: ^10.2.0 dependency_overrides: firebase_core_platform_interface: 5.1.0 From 0ad37164d52eb0b93395f11f5e15686ad7c7d02d Mon Sep 17 00:00:00 2001 From: Italo Matos Date: Mon, 9 Sep 2024 20:12:11 -0300 Subject: [PATCH 10/12] ajustando home page e tela de bluetooth --- lib/main.dart | 8 +- .../{MainPage.dart => BluetoothMainPage.dart} | 6 +- lib/modules/bluetooth/bluetooth_screen.dart | 13 +- lib/modules/home_page/screen/home_screen.dart | 115 ++++++++++++++++-- 4 files changed, 119 insertions(+), 23 deletions(-) rename lib/modules/bluetooth/{MainPage.dart => BluetoothMainPage.dart} (98%) diff --git a/lib/main.dart b/lib/main.dart index f2c4a24..95342ec 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:zenith_monitor/modules/bluetooth/bluetooth_screen.dart'; import 'package:zenith_monitor/modules/home_page/screen/home_screen.dart'; import 'package:zenith_monitor/utils/services/authentication/google_auth.dart'; import 'package:zenith_monitor/utils/services/firestore_services/firestore_services.dart'; @@ -96,13 +97,14 @@ class Application extends StatelessWidget { backgroundColor: Colors.black.withOpacity(0)), primaryColor: Colors.black, ), - //initialRoute: currentUser != null ? '/home' : '/login', - initialRoute: '/login', + initialRoute: currentUser != null ? '/home' : '/login', + //initialRoute: '/login', routes: { '/login': (context) => const LoginScreen(), '/signup': (context) => const SignUpScreen(), // '/forgotPwd': (context) => const ForgotMyPassword(), - '/home': (context) => HomeScreen(), + '/home': (context) => HomeScreen(currentUser: currentUser), + '/bluetooth': (context) => BluetoothScreen(), '/map': (context) => const MapScreen(), '/configuration': (context) => ConfigurationScreen(), '/terminal': (context) => const TerminalScreen(), diff --git a/lib/modules/bluetooth/MainPage.dart b/lib/modules/bluetooth/BluetoothMainPage.dart similarity index 98% rename from lib/modules/bluetooth/MainPage.dart rename to lib/modules/bluetooth/BluetoothMainPage.dart index 845f882..d8f04f9 100644 --- a/lib/modules/bluetooth/MainPage.dart +++ b/lib/modules/bluetooth/BluetoothMainPage.dart @@ -13,12 +13,12 @@ import 'package:url_launcher/url_launcher.dart'; // import './helpers/LineChart.dart'; -class MainPage extends StatefulWidget { +class BluetoothMainPage extends StatefulWidget { @override MainPageState createState() => MainPageState(); } -class MainPageState extends State { +class MainPageState extends State { BluetoothState _bluetoothState = BluetoothState.UNKNOWN; String _address = "..."; @@ -97,7 +97,7 @@ class MainPageState extends State { const Divider(), const ListTile(title: Text('General')), SwitchListTile( - title: const Text('Enable Bluetooth'), + title: const Text('Ativar Bluetooth'), value: _bluetoothState.isEnabled, onChanged: (bool value) { // Do the request and update with the true value then diff --git a/lib/modules/bluetooth/bluetooth_screen.dart b/lib/modules/bluetooth/bluetooth_screen.dart index 89359fc..557c047 100644 --- a/lib/modules/bluetooth/bluetooth_screen.dart +++ b/lib/modules/bluetooth/bluetooth_screen.dart @@ -1,20 +1,13 @@ import 'package:flutter/material.dart'; // import 'package:flutter_blue_plus/flutter_blue_plus.dart'; // import 'package:zenith_monitor/modules/bluetooth/device_caracteristics_screen.dart'; -import './MainPage.dart'; +import 'BluetoothMainPage.dart'; class BluetoothScreen extends StatelessWidget { @override Widget build(BuildContext context) { - return MaterialApp( - home: ExampleApplication(), + return Scaffold( + body: BluetoothMainPage(), ); } } - -class ExampleApplication extends StatelessWidget { - @override - Widget build(BuildContext context) { - return MaterialApp(home: MainPage()); - } -} diff --git a/lib/modules/home_page/screen/home_screen.dart b/lib/modules/home_page/screen/home_screen.dart index ef52383..d96f724 100644 --- a/lib/modules/home_page/screen/home_screen.dart +++ b/lib/modules/home_page/screen/home_screen.dart @@ -1,15 +1,116 @@ +import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; -import 'package:zenith_monitor/modules/bluetooth/bluetooth_screen.dart'; class HomeScreen extends StatelessWidget { + final User? currentUser; + + const HomeScreen({this.currentUser}); + @override Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Bluetooth App', - theme: ThemeData( - primarySwatch: Colors.blue, + return Scaffold( + appBar: AppBar( + title: const Text('Zenith Monitor'), + ), + drawer: Drawer( + child: ListView( + padding: EdgeInsets.zero, + children: [ + DrawerHeader( + decoration: const BoxDecoration( + color: Colors.blue, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (currentUser != null) + CircleAvatar( + backgroundImage: + NetworkImage(currentUser!.photoURL ?? ''), + radius: 30, + ), + const SizedBox(height: 10), + Text( + currentUser != null + ? currentUser!.displayName ?? 'Usuário' + : 'Resgate com Bluetooth', + style: const TextStyle( + color: Colors.white, + fontSize: 24, + ), + ), + const SizedBox(height: 5), + Text( + currentUser != null ? currentUser!.email ?? '' : '', + style: const TextStyle( + color: Colors.white70, + fontSize: 16, + ), + ), + ], + ), + ), + ListTile( + leading: const Icon(Icons.home), + title: const Text('Home'), + onTap: () { + Navigator.pop(context); + }, + ), + ListTile( + leading: const Icon(Icons.settings), + title: const Text('Configurações'), + onTap: () { + Navigator.pop(context); + }, + ), + ListTile( + leading: const Icon(Icons.exit_to_app), + title: const Text('Logout'), + onTap: () { + FirebaseAuth.instance.signOut(); + Navigator.pop(context); + Navigator.pushReplacementNamed(context, '/login'); + }, + ), + ], + ), + ), + body: Padding( + padding: const EdgeInsets.all(16), + child: ListView( + children: [ + _buildCard(context, Icons.bluetooth, + 'Acompanhar resgate com bluetooth', '/bluetooth'), + _buildCard(context, Icons.map, 'Mapa', '/map'), + _buildCard( + context, Icons.settings, 'Configurações', '/configuration'), + _buildCard(context, Icons.terminal, 'Terminal', '/terminal'), + ], + ), ), - home: BluetoothScreen(), // Defina a tela inicial como BluetoothScreen ); } -} \ No newline at end of file +} + +Widget _buildCard( + BuildContext context, IconData icon, String label, String route) { + return Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15), + ), + elevation: 4, + margin: const EdgeInsets.symmetric(vertical: 10), + child: ListTile( + contentPadding: const EdgeInsets.all(16), + leading: Icon(icon, size: 50, color: Colors.blue), + title: Text( + label, + style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + ), + onTap: () { + Navigator.pushNamed(context, route); + }, + ), + ); +} From b98314c1f495b09d241fb10bdcb9712cca9d1326 Mon Sep 17 00:00:00 2001 From: Italo Matos Date: Mon, 9 Sep 2024 20:36:53 -0300 Subject: [PATCH 11/12] configurando home screen para mostrar mapa --- lib/main.dart | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 95342ec..13bf39d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -58,21 +58,20 @@ class Application extends StatelessWidget { final Future _initialization = Firebase.initializeApp(); @override Widget build(BuildContext context) { - // UsbManager usbManager = UsbManager(); - // FirestoreServices fireServices = FirestoreServices(); + UsbManager usbManager = UsbManager(); + FirestoreServices fireServices = FirestoreServices(); return MultiBlocProvider( providers: [ BlocProvider(create: (context) => LoginBloc(auth: GoogleAuth())), - // BlocProvider( - // create: (context) => - // DataBloc(usbManager: usbManager, fireServices: fireServices)), - // BlocProvider( - // create: (context) => MapDataBloc( - // usbManager: usbManager, fireServices: fireServices)), + BlocProvider( + create: (context) => + DataBloc(usbManager: usbManager, fireServices: fireServices)), + BlocProvider( + create: (context) => MapDataBloc( + usbManager: usbManager, fireServices: fireServices)), BlocProvider( create: (context) => TerminalBloc(dataBloc: BlocProvider.of(context))), - BlocProvider(create: (context) => LoginBloc(auth: GoogleAuth())), BlocProvider( create: (context) => @@ -91,7 +90,7 @@ class Application extends StatelessWidget { return MaterialApp( showPerformanceOverlay: false, // shows fps debugShowCheckedModeBanner: false, - title: 'Main Screen', + title: 'Zenith Monitor Main Screen', theme: ThemeData( bottomSheetTheme: BottomSheetThemeData( backgroundColor: Colors.black.withOpacity(0)), From 6f53b09c7b2cc1e4ce629537599242aad6f38ba5 Mon Sep 17 00:00:00 2001 From: Italo Matos Date: Sat, 28 Sep 2024 12:09:31 -0300 Subject: [PATCH 12/12] fix parser message onDataReceived --- lib/modules/bluetooth/ChatPage.dart | 95 ++++++++++++----------------- 1 file changed, 40 insertions(+), 55 deletions(-) diff --git a/lib/modules/bluetooth/ChatPage.dart b/lib/modules/bluetooth/ChatPage.dart index 77046b4..0aa0448 100644 --- a/lib/modules/bluetooth/ChatPage.dart +++ b/lib/modules/bluetooth/ChatPage.dart @@ -192,69 +192,54 @@ class ChatPageState extends State { } void _onDataReceived(Uint8List data) { - // Allocate buffer for parsed data - int backspacesCounter = 0; - for (var byte in data) { - if (byte == 8 || byte == 127) { - backspacesCounter++; - } + // Converter buffer para string + String dataString = String.fromCharCodes(data); + print("Data received (len: ${data.length}): $dataString"); + if (dataString.isEmpty) { + // descartar se estiver em branco + return; } - Uint8List buffer = Uint8List(data.length - backspacesCounter); - int bufferIndex = buffer.length; - // Apply backspace control character - backspacesCounter = 0; - for (int i = data.length - 1; i >= 0; i--) { - if (data[i] == 8 || data[i] == 127) { - backspacesCounter++; - } else { - if (backspacesCounter > 0) { - backspacesCounter--; - } else { - buffer[--bufferIndex] = data[i]; - } - } + // Checar pacote + RegExp regExp = + RegExp(r'-?[0-9.]+;[0-9]+;-?[0-9.]+;-?[0-9.]+;[0-9.]+;"[0-9:]+"'); + if (regExp.hasMatch(dataString) == false) { + print("Pacote inválido: $dataString"); + return; } - // Cria uma string a partir do buffer - String dataString = String.fromCharCodes(buffer); - - // Divide a string em partes usando ponto e vírgula como delimitador + // Dividir pacote + // 85;6;-23.550501;-46.633301;760;"12:30:45" + // rssi;id;latitude;longitude;altitude List parts = dataString.split(';'); - String message = dataString; // Mensagem original - double? longitude; + + // Extrair dados + double? rssi; + double? id; double? latitude; + double? longitude; + double? altitude; + String? datahora; - if (parts.length >= 3) { - // Extrai longitude e latitude (segundo e terceiro valores) - longitude = double.tryParse(parts[1].trim()); - latitude = double.tryParse(parts[2].trim()); - } + if (parts.length >= 1) rssi = double.tryParse(parts[0].trim()); + if (parts.length >= 2) id = double.tryParse(parts[1].trim()); + if (parts.length >= 3) latitude = double.tryParse(parts[2].trim()); + if (parts.length >= 4) longitude = double.tryParse(parts[3].trim()); + if (parts.length >= 5) altitude = double.tryParse(parts[4].trim()); + if (parts.length >= 6) datahora = parts[5].trim(); + print( + "- RSSI: $rssi, ID: $id, Latitude: $latitude, Longitude: $longitude, Altitude: $altitude, Data/Hora: $datahora"); - // Create message if there is new line character - // String dataString = String.fromCharCodes(buffer); - int index = buffer.indexOf(13); - if (~index != 0) { - setState(() { - messages.add( - Message( - 1, - backspacesCounter > 0 - ? _messageBuffer.substring( - 0, _messageBuffer.length - backspacesCounter) - : _messageBuffer + dataString.substring(0, index), - longitude: longitude, - latitude: latitude, - ), - ); - _messageBuffer = dataString.substring(index); - }); - } else { - _messageBuffer = (backspacesCounter > 0 - ? _messageBuffer.substring( - 0, _messageBuffer.length - backspacesCounter) - : _messageBuffer + dataString); - } + setState(() { + messages.add( + Message( + 1, + "$id ($datahora, $rssi dB)", + longitude: longitude, + latitude: latitude, + ), + ); + }); } void _sendMessage(String text) async {