diff --git a/.gitignore b/.gitignore
index 78f046e..401cbb6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,4 +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/.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
deleted file mode 100644
index 3964dd3..0000000
--- a/android/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
- android
- Project android created by Buildship.
-
-
-
-
- org.eclipse.buildship.core.gradleprojectbuilder
-
-
-
-
-
- org.eclipse.buildship.core.gradleprojectnature
-
-
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..af16ff6 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -1,94 +1,75 @@
-def localProperties = new Properties()
- def localPropertiesFile = rootProject.file('local.properties')
- if (localPropertiesFile.exists()) {
- localPropertiesFile.withReader('UTF-8') { reader ->
- localProperties.load(reader)
- }
- }
-
-def flutterRoot = localProperties.getProperty('flutter.sdk')
-if (flutterRoot == null) {
- throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+plugins {
+ id "com.android.application"
+ id "kotlin-android"
+ id "dev.flutter.flutter-gradle-plugin"
+ id "com.google.gms.google-services"
}
-def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
-if (flutterVersionCode == null) {
- flutterVersionCode = '1'
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
}
-def flutterVersionName = localProperties.getProperty('flutter.versionName')
-if (flutterVersionName == null) {
- flutterVersionName = '1.0'
+// Carregar variáveis do arquivo .env
+def dotenvPropertiesFile = rootProject.file('.env')
+if (dotenvPropertiesFile.exists()) {
+ dotenvPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
}
-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`')
+// 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.")
}
-
-apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode', '1')
+def flutterVersionName = localProperties.getProperty('flutter.versionName', '1.0')
android {
- compileSdkVersion 31
+ namespace 'com.zenithaerospace.monitor'
- sourceSets {
- main.java.srcDirs += 'src/main/kotlin'
- }
+ compileSdkVersion 34
+ ndkVersion "25.2.9519653"
- defaultConfig {
- // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+ defaultConfig {
+ applicationId "com.zenithaerospace.monitor"
+ minSdkVersion 23
+ targetSdkVersion 31
+ multiDexEnabled true
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ manifestPlaceholders = [MAPS_API_KEY: mapsApiKey]
+ }
- applicationId "com.zenithaerospace.monitor"
- minSdkVersion 21
- targetSdkVersion 31
- multiDexEnabled true
- versionCode flutterVersionCode.toInteger()
- versionName flutterVersionName
- manifestPlaceholders = [MAPS_API_KEY: mapsApiKey]
- }
+ buildTypes {
+ release {
+ signingConfig signingConfigs.debug
+ }
+ }
- 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/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/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 0000000..ef466fd
Binary files /dev/null and b/assets/images/devicon_google.png differ
diff --git a/firebase.json b/firebase.json
new file mode 100644
index 0000000..b565715
--- /dev/null
+++ b/firebase.json
@@ -0,0 +1 @@
+{"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/ios/RunnerTests/RunnerTests.swift b/ios/RunnerTests/RunnerTests.swift
new file mode 100644
index 0000000..86a7c3b
--- /dev/null
+++ b/ios/RunnerTests/RunnerTests.swift
@@ -0,0 +1,12 @@
+import Flutter
+import UIKit
+import XCTest
+
+class RunnerTests: XCTestCase {
+
+ func testExample() {
+ // If you add code to the Runner application, consider adding tests here.
+ // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
+ }
+
+}
diff --git a/lib/firebase_options.dart b/lib/firebase_options.dart
index 522e2e9..cf05519 100644
--- a/lib/firebase_options.dart
+++ b/lib/firebase_options.dart
@@ -1,5 +1,5 @@
// File generated by FlutterFire CLI.
-// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members
+// ignore_for_file: type=lint
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart'
show defaultTargetPlatform, kIsWeb, TargetPlatform;
@@ -26,12 +26,25 @@ class DefaultFirebaseOptions {
case TargetPlatform.android:
return android;
case TargetPlatform.iOS:
- return 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.',
@@ -40,23 +53,10 @@ class DefaultFirebaseOptions {
}
static const FirebaseOptions android = FirebaseOptions(
- apiKey: 'AIzaSyAdFLNjRj6EtKgvuyeyZZb6lwmSneDCltw',
- appId: '1:647188572266:android:32f5b010a7468bbf5c0213',
- messagingSenderId: '647188572266',
- projectId: 'zenith-monitor-a0d89',
- databaseURL: 'https://zenith-monitor-a0d89.firebaseio.com',
- storageBucket: 'zenith-monitor-a0d89.appspot.com',
- );
-
- static const FirebaseOptions ios = FirebaseOptions(
- apiKey: 'AIzaSyDb0CYZjgUE0WzIxc_vfR26VWh08FpRW3g',
- appId: '1:647188572266:ios:3b7c8e4eb2980b995c0213',
- messagingSenderId: '647188572266',
- projectId: 'zenith-monitor-a0d89',
- databaseURL: 'https://zenith-monitor-a0d89.firebaseio.com',
- storageBucket: 'zenith-monitor-a0d89.appspot.com',
- androidClientId: '647188572266-j3201qobbgvq19oqso8978l6t62tl13q.apps.googleusercontent.com',
- iosClientId: '647188572266-c0r04kh4gg2f4jq60mg1dari157af9s9.apps.googleusercontent.com',
- iosBundleId: 'com.example.zenithMonitor',
+ 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/lib/main.dart b/lib/main.dart
index 488dd0a..13bf39d 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,6 +1,9 @@
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';
import 'package:zenith_monitor/widgets/not_found_screen.dart';
import 'firebase_options.dart';
@@ -11,12 +14,13 @@ 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';
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();
@@ -24,9 +28,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);
@@ -45,7 +62,7 @@ class Application extends StatelessWidget {
FirestoreServices fireServices = FirestoreServices();
return MultiBlocProvider(
providers: [
- BlocProvider(create: (context) => LoginBloc()),
+ BlocProvider(create: (context) => LoginBloc(auth: GoogleAuth())),
BlocProvider(
create: (context) =>
DataBloc(usbManager: usbManager, fireServices: fireServices)),
@@ -55,6 +72,10 @@ class Application extends StatelessWidget {
BlocProvider(
create: (context) =>
TerminalBloc(dataBloc: BlocProvider.of(context))),
+ BlocProvider(create: (context) => LoginBloc(auth: GoogleAuth())),
+ BlocProvider(
+ create: (context) =>
+ TerminalBloc(dataBloc: BlocProvider.of(context))),
],
child: FutureBuilder(
future: _initialization,
@@ -64,20 +85,25 @@ class Application extends StatelessWidget {
}
if (snapshot.connectionState == ConnectionState.done) {
+ final User? currentUser = FirebaseAuth.instance.currentUser;
+
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)),
primaryColor: Colors.black,
),
- initialRoute: '/login',
+ initialRoute: currentUser != null ? '/home' : '/login',
+ //initialRoute: '/login',
routes: {
'/login': (context) => const LoginScreen(),
'/signup': (context) => const SignUpScreen(),
- '/forgotPwd': (context) => const ForgotMyPassword(),
+ // '/forgotPwd': (context) => const ForgotMyPassword(),
+ '/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/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/BluetoothMainPage.dart b/lib/modules/bluetooth/BluetoothMainPage.dart
new file mode 100644
index 0000000..d8f04f9
--- /dev/null
+++ b/lib/modules/bluetooth/BluetoothMainPage.dart
@@ -0,0 +1,372 @@
+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 'package:url_launcher/url_launcher.dart';
+
+// import './helpers/LineChart.dart';
+
+class BluetoothMainPage 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('Ativar 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("Teste google maps"),
+ onTap: () async {
+ await openGoogleMaps(48.8584, 2.2945);
+ },
+ ),
+ 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 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,
+ ) 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/ChatPage.dart b/lib/modules/bluetooth/ChatPage.dart
new file mode 100644
index 0000000..0aa0448
--- /dev/null
+++ b/lib/modules/bluetooth/ChatPage.dart
@@ -0,0 +1,270 @@
+import 'dart:async';
+import 'dart:convert';
+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;
+
+ const ChatPage({required this.server});
+
+ @override
+ ChatPageState createState() => ChatPageState();
+}
+
+class Message {
+ int whom;
+ String text;
+ double? latitude;
+ double? longitude;
+
+ Message(this.whom, this.text, {this.latitude, this.longitude});
+}
+
+class ChatPageState extends State {
+ static const clientID = 0;
+ BluetoothConnection? connection;
+
+ List messages = List.empty(growable: true);
+ String _messageBuffer = '';
+
+ final TextEditingController textEditingController = TextEditingController();
+ final ScrollController listScrollController = 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(
+ mainAxisAlignment: _message.whom == clientID
+ ? MainAxisAlignment.end
+ : MainAxisAlignment.start,
+ children: [
+ Container(
+ 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: 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'),
+ ),
+ ],
+ ],
+ ),
+ ),
+ ],
+ );
+ }).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),
+ ),
+ ],
+ )
+ ],
+ ),
+ ),
+ );
+ }
+
+ 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) {
+ // 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;
+ }
+
+ // 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;
+ }
+
+ // Dividir pacote
+ // 85;6;-23.550501;-46.633301;760;"12:30:45"
+ // rssi;id;latitude;longitude;altitude
+ List parts = dataString.split(';');
+
+ // Extrair dados
+ double? rssi;
+ double? id;
+ double? latitude;
+ double? longitude;
+ double? altitude;
+ String? datahora;
+
+ 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");
+
+ setState(() {
+ messages.add(
+ Message(
+ 1,
+ "$id ($datahora, $rssi dB)",
+ longitude: longitude,
+ latitude: latitude,
+ ),
+ );
+ });
+ }
+
+ void _sendMessage(String text) async {
+ text = text.trim();
+ textEditingController.clear();
+
+ if (text.isNotEmpty) {
+ try {
+ connection!.output.add(Uint8List.fromList(utf8.encode("$text\r\n")));
+ await connection!.output.allSent;
+
+ setState(() {
+ messages.add(Message(clientID, text));
+ });
+
+ Future.delayed(const Duration(milliseconds: 333)).then((_) {
+ listScrollController.animateTo(
+ listScrollController.position.maxScrollExtent,
+ duration: const 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/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..557c047
--- /dev/null
+++ b/lib/modules/bluetooth/bluetooth_screen.dart
@@ -0,0 +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 'BluetoothMainPage.dart';
+
+class BluetoothScreen extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ body: BluetoothMainPage(),
+ );
+ }
+}
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/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/home_page/screen/home_screen.dart b/lib/modules/home_page/screen/home_screen.dart
new file mode 100644
index 0000000..d96f724
--- /dev/null
+++ b/lib/modules/home_page/screen/home_screen.dart
@@ -0,0 +1,116 @@
+import 'package:firebase_auth/firebase_auth.dart';
+import 'package:flutter/material.dart';
+
+class HomeScreen extends StatelessWidget {
+ final User? currentUser;
+
+ const HomeScreen({this.currentUser});
+
+ @override
+ Widget build(BuildContext context) {
+ 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'),
+ ],
+ ),
+ ),
+ );
+ }
+}
+
+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);
+ },
+ ),
+ );
+}
diff --git a/lib/modules/login/bloc/login_bloc.dart b/lib/modules/login/bloc/login_bloc.dart
index f14e08a..6d6c18b 100644
--- a/lib/modules/login/bloc/login_bloc.dart
+++ b/lib/modules/login/bloc/login_bloc.dart
@@ -2,73 +2,44 @@ 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();
+ 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_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..7fd28c8 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,9 @@ class LoginError extends LoginState {
class LoadingState extends LoginState {}
class LoginSuccess extends LoginState {
- LoginSuccess(LocalUser newUser) {
- user = newUser;
- }
+ LoginSuccess(LocalUser newUser) : super(user: newUser);
}
+
+class SignOutSuccess extends LoginState {}
+
+class CheckSessionEvent extends LoginEvent {}
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..39b631d 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
@@ -52,7 +41,10 @@ class _LoginWidgetState extends State {
body: BlocConsumer(
listener: (context, state) {
if (state is LoginSuccess) {
- Navigator.popAndPushNamed(context, '/map');
+ Navigator.popAndPushNamed(context, '/home');
+ }
+ if (state is SignOutSuccess) {
+ Navigator.popAndPushNamed(context, '/login');
}
},
builder: (context, state) {
@@ -80,10 +72,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 +93,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 +102,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..c077b49 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: "9371d13b8ee442e3bfc08a24e3a1b3742c839abbfaf5eef11b79c4b862c89bf7"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.41"
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: "77ac40d66a0fd585ca1e80ef3cbfd9df34462103bd4c1cf5950cb7d1f4c2e188"
+ url: "https://pub.dev"
source: hosted
- version: "2.5.4"
+ version: "5.3.0"
cloud_firestore_platform_interface:
- dependency: transitive
+ dependency: "direct main"
description:
name: cloud_firestore_platform_interface
- url: "https://pub.dartlang.org"
+ sha256: da0fa07284bc845b4a517449365eac0346a084676b3ed399f75a0ed25c3ec4c8
+ url: "https://pub.dev"
source: hosted
- version: "5.5.7"
+ version: "6.3.2"
cloud_firestore_web:
dependency: transitive
description:
name: cloud_firestore_web
- url: "https://pub.dartlang.org"
+ sha256: "98dca6f864141cd3f79cf42a036f45233b8e2a3940b0011e6a370dd2283c45db"
+ url: "https://pub.dev"
source: hosted
- version: "2.6.16"
+ version: "4.1.2"
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"
+ sha256: "2056db5241f96cdc0126bd94459fc4cdc13876753768fc7a31c425e50a7177d0"
+ url: "https://pub.dev"
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"
- source: hosted
- version: "1.2.4"
+ version: "6.0.5"
connectivity_plus_platform_interface:
dependency: transitive
description:
name: connectivity_plus_platform_interface
- url: "https://pub.dartlang.org"
+ sha256: "42657c1715d48b167930d5f34d00222ac100475f73d10162ddf43e714932f204"
+ url: "https://pub.dev"
source: hosted
- version: "1.2.1"
- connectivity_plus_web:
+ version: "2.0.1"
+ cross_file:
dependency: transitive
description:
- name: connectivity_plus_web
- url: "https://pub.dartlang.org"
+ name: cross_file
+ sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
+ url: "https://pub.dev"
source: hosted
- version: "1.2.2"
- connectivity_plus_windows:
+ version: "0.3.4+2"
+ csslib:
dependency: transitive
description:
- name: connectivity_plus_windows
- url: "https://pub.dartlang.org"
+ name: csslib
+ sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb"
+ url: "https://pub.dev"
source: hosted
- version: "1.2.2"
- cross_file:
- dependency: transitive
- description:
- name: cross_file
- url: "https://pub.dartlang.org"
- 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: "2.0.5"
+ facebook_auth_desktop:
+ dependency: transitive
+ description:
+ name: facebook_auth_desktop
+ sha256: "223e04414e078271682dadd2a694da1f4d329634137414b30217b2f478457fdb"
+ url: "https://pub.dev"
source: hosted
- version: "0.7.3"
+ version: "2.1.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: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
+ url: "https://pub.dev"
source: hosted
- version: "1.2.1"
- file:
+ version: "2.1.3"
+ 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: "2ad726953f6e8affbc4df8dc78b77c3b4a060967a291e528ef72ae846c60fb69"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.9.3+2"
firebase_auth:
dependency: "direct main"
description:
name: firebase_auth
- url: "https://pub.dartlang.org"
+ sha256: "6f5792bdc208416bfdfbfe3363b78ce01667b6ebc4c5cb47cfa891f2fca45ab7"
+ url: "https://pub.dev"
source: hosted
- version: "3.3.19"
+ version: "5.2.0"
firebase_auth_platform_interface:
dependency: transitive
description:
name: firebase_auth_platform_interface
- url: "https://pub.dartlang.org"
+ sha256: "80237bb8a92bb0a5e3b40de1c8dbc80254e49ac9e3907b4b47b8e95ac3dd3fad"
+ url: "https://pub.dev"
source: hosted
- version: "6.2.7"
+ version: "7.4.4"
firebase_auth_web:
dependency: transitive
description:
name: firebase_auth_web
- url: "https://pub.dartlang.org"
+ sha256: "9d315491a6be65ea83511cb0e078544a309c39dd54c0ee355c51dbd6d8c03cc8"
+ url: "https://pub.dev"
source: hosted
- version: "3.3.16"
+ version: "5.12.6"
firebase_core:
dependency: "direct main"
description:
name: firebase_core
- url: "https://pub.dartlang.org"
+ sha256: "06537da27db981947fa535bb91ca120b4e9cb59cb87278dbdde718558cafc9ff"
+ url: "https://pub.dev"
source: hosted
- version: "1.17.1"
+ version: "3.4.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: "362e52457ed2b7b180964769c1e04d1e0ea0259fdf7025fdfedd019d4ae2bd88"
+ url: "https://pub.dev"
source: hosted
- version: "1.6.4"
+ version: "2.17.5"
firebase_storage:
dependency: "direct main"
description:
name: firebase_storage
- url: "https://pub.dartlang.org"
+ sha256: dfc06d783dbc0b6200a4b936d8cdbd826bd1571c959854d14a70259188d96e85
+ url: "https://pub.dev"
source: hosted
- version: "10.2.17"
+ version: "12.2.0"
firebase_storage_platform_interface:
dependency: transitive
description:
name: firebase_storage_platform_interface
- url: "https://pub.dartlang.org"
+ sha256: "3da511301b77514dee5370281923fbbc6d5725c2a0b96004c5c45415e067f234"
+ url: "https://pub.dev"
source: hosted
- version: "4.1.7"
+ version: "5.1.28"
firebase_storage_web:
dependency: transitive
description:
name: firebase_storage_web
- url: "https://pub.dartlang.org"
+ sha256: "7ad67b1c1c46c995a6bd4f225d240fc9a5fb277fade583631ae38750ffd9be17"
+ url: "https://pub.dev"
source: hosted
- version: "3.2.16"
+ version: "3.9.13"
flutter:
dependency: "direct main"
description: flutter
@@ -234,58 +282,130 @@ 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_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:
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: faa92fb7626c230837f30d02de7d92b6af334b77ba744867c63545d4ad7e171f
+ url: "https://pub.dev"
source: hosted
- version: "3.5.7"
+ version: "7.1.0"
flutter_facebook_auth_platform_interface:
dependency: transitive
description:
name: flutter_facebook_auth_platform_interface
- url: "https://pub.dartlang.org"
+ sha256: a416f89de881ba98188942060aecf17b434b06677d4557731f19e859343df54c
+ url: "https://pub.dev"
source: hosted
- version: "2.7.1"
+ version: "6.1.0"
flutter_facebook_auth_web:
dependency: transitive
description:
name: flutter_facebook_auth_web
- url: "https://pub.dartlang.org"
+ sha256: "8a6890a98522604169ca9f958d7189c9f579dbf05ba4d3d7adf26eca4c4c4c93"
+ url: "https://pub.dev"
source: hosted
- version: "2.6.0+2"
+ version: "6.1.1"
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: "9ee02950848f61c4129af3d6ec84a1cfc0e47931abc746b03e7a3bc3e8ff6eda"
+ url: "https://pub.dev"
source: hosted
- version: "2.0.6"
+ version: "2.0.22"
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 +416,502 @@ 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: "5be191523702ba8d7a01ca97c17fca096822ccf246b0a9f11923a6ded06199b6"
+ url: "https://pub.dev"
source: hosted
- version: "4.6.5"
+ version: "0.3.1+4"
+ google_maps:
+ dependency: transitive
+ description:
+ name: google_maps
+ sha256: "463b38e5a92a05cde41220a11fd5eef3847031fef3e8cf295ac76ec453246907"
+ url: "https://pub.dev"
+ source: hosted
+ version: "8.0.0"
google_maps_flutter:
dependency: "direct main"
description:
name: google_maps_flutter
- url: "https://pub.dartlang.org"
+ sha256: "2e302fa3aaf4e2a297f0342d83ebc5e8e9f826e9a716aef473fe7f404ec630a7"
+ url: "https://pub.dev"
source: hosted
- version: "2.1.6"
+ version: "2.9.0"
+ google_maps_flutter_android:
+ dependency: transitive
+ description:
+ name: google_maps_flutter_android
+ sha256: "60a005bf1ba8d178144e442f6e2d734b0ffc2cc800a05415388472f934ad6d6a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.14.4"
+ google_maps_flutter_ios:
+ dependency: transitive
+ description:
+ name: google_maps_flutter_ios
+ sha256: "3a484846fc56f15e47e3de1f5ea80a7ff2b31721d2faa88f390f3b3cf580c953"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.13.0"
google_maps_flutter_platform_interface:
dependency: transitive
description:
name: google_maps_flutter_platform_interface
- url: "https://pub.dartlang.org"
+ sha256: "4f6930fd668bf5d40feb2695d5695dbc0c35e5542b557a34ad35be491686d2ba"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.9.0"
+ google_maps_flutter_web:
+ dependency: transitive
+ description:
+ name: google_maps_flutter_web
+ sha256: ff39211bd25d7fad125d19f757eba85bd154460907cd4d135e07e3d0f98a4130
+ url: "https://pub.dev"
source: hosted
- version: "2.1.7"
+ version: "0.5.10"
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: "5a47ebec9af97daf0822e800e4f101c3340b5ebc3f6898cf860c1a71b53cf077"
+ url: "https://pub.dev"
source: hosted
- version: "5.2.8"
+ version: "6.1.28"
google_sign_in_ios:
dependency: transitive
description:
name: google_sign_in_ios
- url: "https://pub.dartlang.org"
+ sha256: "4898410f55440049e1ba8f15411612d9f89299d89c61cd9baf7e02d56ff81ac7"
+ url: "https://pub.dev"
source: hosted
- version: "5.3.1"
+ version: "5.7.7"
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: "042805a21127a85b0dc46bba98a37926f17d2439720e8a459d27045d8ef68055"
+ url: "https://pub.dev"
source: hosted
- version: "0.10.1+2"
+ version: "0.12.4+2"
graphs:
dependency: transitive
description:
name: graphs
- url: "https://pub.dartlang.org"
+ sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
+ url: "https://pub.dev"
source: hosted
- version: "2.1.0"
+ version: "2.3.2"
+ 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: "1.5.1"
+ 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: "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: "8c5abf0dcc24fe6e8e0b4a5c0b51a5cf30cefdf6407a3213dae61edc75a70f56"
+ url: "https://pub.dev"
source: hosted
- version: "0.8.4+13"
+ version: "0.8.12+12"
image_picker_for_web:
dependency: transitive
description:
name: image_picker_for_web
- url: "https://pub.dartlang.org"
+ sha256: "65d94623e15372c5c51bebbcb820848d7bcb323836e12dfdba60b5d3a8b39e50"
+ url: "https://pub.dev"
source: hosted
- version: "2.1.8"
+ version: "3.0.5"
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.3"
+ version: "0.6.7"
+ 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: "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.7.0"
+ version: "1.12.0"
+ mime:
+ dependency: transitive
+ description:
+ name: mime
+ sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.6"
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: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378
+ url: "https://pub.dev"
source: hosted
- version: "2.0.10"
+ version: "2.1.4"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
- url: "https://pub.dartlang.org"
+ sha256: "6f01f8e37ec30b07bc424b4deabac37cacb1bc7e2e515ad74486039918a37eb7"
+ url: "https://pub.dev"
source: hosted
- version: "2.0.14"
- path_provider_ios:
+ version: "2.2.10"
+ 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"
- source: hosted
- version: "2.1.7"
- path_provider_macos:
- dependency: transitive
- description:
- name: path_provider_macos
- url: "https://pub.dartlang.org"
+ sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
+ url: "https://pub.dev"
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.3.0"
+ permission_handler:
+ dependency: "direct main"
+ description:
+ name: permission_handler
+ sha256: bc56bfe9d3f44c3c612d8d393bd9b174eb796d706759f9b495ac254e4294baa5
+ url: "https://pub.dev"
source: hosted
- version: "2.0.7"
+ 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:
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: daa5394a7d064b4997b39e9afa02f6882c479c38b19fa0dd60f052b99c105400
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.13.13"
+ rive_common:
+ dependency: transitive
+ description:
+ name: rive_common
+ sha256: c7bf0781b1621629361579c300ac2f8aa1a238227a242202a596e82becc244d7
+ url: "https://pub.dev"
source: hosted
- version: "0.7.33"
- rxdart:
+ version: "0.4.11"
+ sanitize_html:
dependency: transitive
description:
- name: rxdart
- url: "https://pub.dartlang.org"
+ name: sanitize_html
+ sha256: "12669c4a913688a26555323fb9cec373d8f9fbe091f2d01c40c723b33caa8989"
+ url: "https://pub.dev"
source: hosted
- version: "0.27.4"
+ 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
@@ -620,93 +921,186 @@ 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"
+ 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:
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: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a"
+ url: "https://pub.dev"
source: hosted
- version: "2.5.2"
+ version: "5.5.4"
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..3e91486 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -21,38 +21,46 @@ 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.
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
- flutter_dotenv: ^5.0.2
+ http_parser: ^4.0.2
+ google_sign_in: ^6.2.1
+ firebase_auth: ^5.1.4
+ 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.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
+ url_launcher: ^6.1.10
+ permission_handler: ^10.2.0
+
+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
@@ -66,7 +74,6 @@ flutter:
# the material Icons class.
uses-material-design: true
-
assets:
- assets/images/
- assets/maps/
@@ -105,4 +112,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
-