Compare commits

...

23 Commits

Author SHA1 Message Date
Tran Anh Tuan
2afc71e24c chore(notifications): fix conflig when show notification in android 2025-10-23 16:57:10 +07:00
Tran Anh Tuan
9bff11a0b1 chore(group): fix delay show group when switch and convert to relative import 2025-10-23 11:08:03 +07:00
Tran Anh Tuan
cfebf74515 update to relative import 2025-10-22 17:52:28 +07:00
anhtunz
fe622d8af9 fix(ui): fix error text in warning card and update information for device's inter-family owner 2025-07-21 10:09:19 +07:00
anhtunz
4ff2ad4396 Update alarm notification when app in foreground, background and terminate 2025-07-11 12:04:20 +07:00
anhtunz
cdbd5b7484 Add SimDataScreen 2025-07-08 16:56:30 +07:00
anhtunz
e6c536f380 update(MainScreen): Fix Timer cannot cancel when switching tabs 2025-06-23 16:10:36 +07:00
anhtunz
bd73ba1b45 Update Gradle Plugin 2025-06-18 13:15:27 +07:00
anhtunz
d7d565a36a Update intl package 2025-06-18 10:02:40 +07:00
anhtunz
2d53f2cdd3 Update
Logging data
Try-catch function
2025-06-17 16:43:45 +07:00
anhtunz
22fef0e0a8 fix map 2025-06-09 15:43:50 +07:00
anhtunz
3a8fa3633c update
feat(api_service): Update try-catch funtion and handle exception
update(loading_animation): Update loading animation using Lottie
2025-06-09 14:29:43 +07:00
477646ab9d v1.0.8 2025-05-26 15:21:58 +07:00
anhtunz
677a7c4d4a chore(HomeScreen): fix context unmounted 2025-05-26 14:08:30 +07:00
anhtunz
01ae020374 Fix(bugs):
Can't logout
ui display when user doesn't has device
2025-05-26 11:58:19 +07:00
anhtunz
f80e234b1d fix(bugs):
fix(DeviceManagerScreen): show all devices when visibility=DELETED
fix(MapScreen): cannot delete polylines when closing SnackBar on iOS
2025-04-28 09:44:26 +07:00
anhtunz
b75635a801 fix(ui): fix layout in DetailGroupScreen 2025-04-15 13:34:42 +07:00
anhtunz
c19cdec3cf fix(bugs): Fix dispose error in group_detail_screen 2025-04-15 12:02:47 +07:00
anhtunz
07af3c344f chore(ui): Set width device_name's column to 1/3 size_width in device_manager_screen
update text color on button show_direction_widget
2025-04-15 11:59:18 +07:00
anhtunz
8310451f6b fix(ui): Change TextStyle to ResponsiveText 2025-04-15 11:31:29 +07:00
anhtunz
da8eb3ae2d fix(bugs): Fix FindAWay Function with new package 2025-04-15 10:13:17 +07:00
anhtunz
6af1baed32 Resolve merge conflict between main and vypq 2025-04-14 11:41:00 +07:00
d5b323e158 new branch 2025-04-14 11:26:43 +07:00
154 changed files with 8370 additions and 2926 deletions

2
.gitignore vendored
View File

@@ -5,9 +5,11 @@
*.swp *.swp
.DS_Store .DS_Store
.atom/ .atom/
.build/
.buildlog/ .buildlog/
.history .history
.svn/ .svn/
.swiftpm/
migrate_working_dir/ migrate_working_dir/
# IntelliJ related # IntelliJ related

View File

@@ -7,6 +7,10 @@
# The following line activates a set of recommended lints for Flutter apps, # The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices. # packages, and plugins designed to encourage good coding practices.
analyzer:
errors:
use_build_context_synchronously: ignore
avoid_print: ignore
include: package:flutter_lints/flutter.yaml include: package:flutter_lints/flutter.yaml
linter: linter:
@@ -22,8 +26,8 @@ linter:
# `// ignore_for_file: name_of_lint` syntax on the line or in the file # `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint. # producing the lint.
rules: rules:
- prefer_relative_imports: true
# avoid_print: false # Uncomment to disable the `avoid_print` rule # avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at # Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options # https://dart.dev/guides/language/analysis-options

View File

@@ -1,3 +1,10 @@
plugins {
id "com.android.application"
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
id 'com.google.gms.google-services'
}
def localProperties = new Properties() def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties') def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) { if (localPropertiesFile.exists()) {
@@ -6,11 +13,6 @@ if (localPropertiesFile.exists()) {
} }
} }
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
// throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode') def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) { if (flutterVersionCode == null) {
flutterVersionCode = '1' flutterVersionCode = '1'
@@ -21,21 +23,12 @@ if (flutterVersionName == null) {
flutterVersionName = '1.0' flutterVersionName = '1.0'
} }
apply plugin: 'com.android.application'
// START: FlutterFire Configuration
apply plugin: 'com.google.gms.google-services'
// END: FlutterFire Configuration
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
def keystoreProperties = new Properties() def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties') def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) { if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
} }
android { android {
namespace "vn.smatec.sfm" namespace "vn.smatec.sfm"
compileSdkVersion 35 compileSdkVersion 35
@@ -44,12 +37,12 @@ android {
compileOptions { compileOptions {
// Flag to enable support for the new language APIs // Flag to enable support for the new language APIs
coreLibraryDesugaringEnabled true coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_17
} }
kotlinOptions { kotlinOptions {
jvmTarget = '1.8' jvmTarget = '17'
} }
sourceSets { sourceSets {
@@ -102,9 +95,9 @@ flutter {
} }
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" // implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.google.firebase:firebase-messaging-directboot:20.2.0' implementation platform('com.google.firebase:firebase-bom:33.15.0')
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.2.2' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'
implementation 'androidx.window:window:1.0.0' implementation 'androidx.window:window:1.0.0'
implementation 'androidx.window:window-java:1.0.0' implementation 'androidx.window:window-java:1.0.0'
} }

View File

@@ -1,30 +1,36 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature
android:name="android.hardware.camera"
android:required="false"
tools:targetApi="5" />
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.VIBRATE" />
<!-- <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" /> -->
<!-- NOTE: the example app requests USE_EXACT_ALARM to make it easier to run the app.
Developers will need to check if their own app needs to use SCHEDULE_EXACT_ALARM instead -->
<!-- <uses-permission android:name="android.permission.USE_EXACT_ALARM" /> -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- NOTE: Special use was selected as it's the closest match for this example app.
apps should specify the appropriate permission for their use cases. -->
<!-- <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" /> -->
<!-- START Permissions Package -->
<!-- Permissions options for the `camera` group --> <!-- Permissions options for the `camera` group -->
<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.CAMERA"/>
<!-- Permissions options for the `location` group --> <!-- Permissions options for the `location` group -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- Permissions options for the `alarm` group -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission android:name="android.permission.USE_EXACT_ALARM"
tools:ignore="ExactAlarm" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
<!-- END Permissions Package --> <!-- END Permissions Package -->
<application <application
android:label="SFM" android:label="SFM"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/launcher_icon"> android:icon="@mipmap/launcher_icon">
<meta-data android:name="com.google.android.geo.API_KEY" <meta-data android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyA9C7Pmxw6Gw3H2mM4WA_XGngRIIr2VS7k"/> android:value="AIzaSyDI8b-PUgKUgj5rHdtgEHCwWjUXYJrqYhE"/>
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:launchMode="singleTop" android:launchMode="singleTop"
@@ -34,6 +40,7 @@
android:windowSoftInputMode="adjustResize" android:windowSoftInputMode="adjustResize"
android:showWhenLocked="true" android:showWhenLocked="true"
android:turnScreenOn="true" android:turnScreenOn="true"
android:enableOnBackInvokedCallback="true"
android:exported="true"> android:exported="true">
<!-- Specifies an Android theme to apply to this Activity as soon as <!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user the Android process has started. This theme is visible to the user
@@ -60,7 +67,25 @@
<service <service
android:name="com.dexterous.flutterlocalnotifications.ForegroundService" android:name="com.dexterous.flutterlocalnotifications.ForegroundService"
android:exported="false" android:exported="false"
android:stopWithTask="false"/> android:stopWithTask="false"
android:foregroundServiceType="specialUse" />
<service android:name="com.gdelataillade.alarm.services.NotificationOnKillService" />
<service
android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false"/>
<receiver
android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmBroadcastReceiver"
android:exported="false"/>
<receiver
android:name="dev.fluttercommunity.plus.androidalarmmanager.RebootBroadcastReceiver"
android:enabled="false"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ActionBroadcastReceiver" /> <receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ActionBroadcastReceiver" />
<receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver" /> <receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver" />
<receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver"> <receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver">

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,21 +1,9 @@
buildscript { plugins {
ext.kotlin_version = '1.8.22' id 'com.google.gms.google-services' version '4.3.15' apply false
repositories {
google()
mavenCentral()
} }
dependencies {
// START: FlutterFire Configuration
classpath 'com.google.gms:google-services:4.3.15'
// END: FlutterFire Configuration
classpath 'com.android.tools.build:gradle:8.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects { allprojects {
repositories { repositories {
gradlePluginPortal()
google() google()
mavenCentral() mavenCentral()
} }
@@ -43,3 +31,7 @@ subprojects {
tasks.register("clean", Delete) { tasks.register("clean", Delete) {
delete rootProject.buildDir delete rootProject.buildDir
} }

View File

@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-all.zip

View File

@@ -18,8 +18,11 @@ pluginManagement {
plugins { plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.2.1" apply false id "com.android.application" version "8.3.2" apply false
id "org.jetbrains.kotlin.android" version "1.8.22" apply false // START: FlutterFire Configuration
id "com.google.gms.google-services" version "4.3.15" apply false
// END: FlutterFire Configuration
id "org.jetbrains.kotlin.android" version "2.1.0" apply false
} }
include ":app" include ":app"

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

3
devtools_options.yaml Normal file
View File

@@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:

View File

@@ -21,6 +21,6 @@
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.0</string> <string>1.0</string>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>11.0</string> <string>12.0</string>
</dict> </dict>
</plist> </plist>

View File

@@ -1,3 +1,39 @@
# Uncomment this line to define a global platform for your project
platform :ios, '15.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer| post_install do |installer|
installer.pods_project.targets.each do |target| installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target) flutter_additional_ios_build_settings(target)
@@ -30,5 +66,8 @@ post_install do |installer|
] ]
end end
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '14.0'
end
end end
end end

197
ios/Podfile.lock Normal file
View File

@@ -0,0 +1,197 @@
PODS:
- alarm (0.0.1):
- Flutter
- app_settings (5.1.1):
- Flutter
- connectivity_plus (0.0.1):
- Flutter
- Firebase/CoreOnly (11.10.0):
- FirebaseCore (~> 11.10.0)
- Firebase/Messaging (11.10.0):
- Firebase/CoreOnly
- FirebaseMessaging (~> 11.10.0)
- firebase_core (3.13.0):
- Firebase/CoreOnly (= 11.10.0)
- Flutter
- firebase_messaging (15.2.5):
- Firebase/Messaging (= 11.10.0)
- firebase_core
- Flutter
- FirebaseCore (11.10.0):
- FirebaseCoreInternal (~> 11.10.0)
- GoogleUtilities/Environment (~> 8.0)
- GoogleUtilities/Logger (~> 8.0)
- FirebaseCoreInternal (11.10.0):
- "GoogleUtilities/NSData+zlib (~> 8.0)"
- FirebaseInstallations (11.10.0):
- FirebaseCore (~> 11.10.0)
- GoogleUtilities/Environment (~> 8.0)
- GoogleUtilities/UserDefaults (~> 8.0)
- PromisesObjC (~> 2.4)
- FirebaseMessaging (11.10.0):
- FirebaseCore (~> 11.10.0)
- FirebaseInstallations (~> 11.0)
- GoogleDataTransport (~> 10.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.0)
- GoogleUtilities/Environment (~> 8.0)
- GoogleUtilities/Reachability (~> 8.0)
- GoogleUtilities/UserDefaults (~> 8.0)
- nanopb (~> 3.30910.0)
- Flutter (1.0.0)
- flutter_barcode_scanner_plus (3.0.7):
- Flutter
- flutter_fgbg (0.0.1):
- Flutter
- flutter_local_notifications (0.0.1):
- Flutter
- geolocator_apple (1.2.0):
- Flutter
- FlutterMacOS
- Google-Maps-iOS-Utils (5.0.0):
- GoogleMaps (~> 8.0)
- google_maps_flutter_ios (0.0.1):
- Flutter
- Google-Maps-iOS-Utils (< 7.0, >= 5.0)
- GoogleMaps (< 10.0, >= 8.4)
- GoogleDataTransport (10.1.0):
- nanopb (~> 3.30910.0)
- PromisesObjC (~> 2.4)
- GoogleMaps (8.4.0):
- GoogleMaps/Maps (= 8.4.0)
- GoogleMaps/Base (8.4.0)
- GoogleMaps/Maps (8.4.0):
- GoogleMaps/Base
- GoogleUtilities/AppDelegateSwizzler (8.1.0):
- GoogleUtilities/Environment
- GoogleUtilities/Logger
- GoogleUtilities/Network
- GoogleUtilities/Privacy
- GoogleUtilities/Environment (8.1.0):
- GoogleUtilities/Privacy
- GoogleUtilities/Logger (8.1.0):
- GoogleUtilities/Environment
- GoogleUtilities/Privacy
- GoogleUtilities/Network (8.1.0):
- GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib"
- GoogleUtilities/Privacy
- GoogleUtilities/Reachability
- "GoogleUtilities/NSData+zlib (8.1.0)":
- GoogleUtilities/Privacy
- GoogleUtilities/Privacy (8.1.0)
- GoogleUtilities/Reachability (8.1.0):
- GoogleUtilities/Logger
- GoogleUtilities/Privacy
- GoogleUtilities/UserDefaults (8.1.0):
- GoogleUtilities/Logger
- GoogleUtilities/Privacy
- maps_launcher (0.0.1):
- Flutter
- nanopb (3.30910.0):
- nanopb/decode (= 3.30910.0)
- nanopb/encode (= 3.30910.0)
- nanopb/decode (3.30910.0)
- nanopb/encode (3.30910.0)
- permission_handler_apple (9.3.0):
- Flutter
- PromisesObjC (2.4.0)
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- url_launcher_ios (0.0.1):
- Flutter
DEPENDENCIES:
- alarm (from `.symlinks/plugins/alarm/ios`)
- app_settings (from `.symlinks/plugins/app_settings/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
- Flutter (from `Flutter`)
- flutter_barcode_scanner_plus (from `.symlinks/plugins/flutter_barcode_scanner_plus/ios`)
- flutter_fgbg (from `.symlinks/plugins/flutter_fgbg/ios`)
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/darwin`)
- google_maps_flutter_ios (from `.symlinks/plugins/google_maps_flutter_ios/ios`)
- maps_launcher (from `.symlinks/plugins/maps_launcher/ios`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
SPEC REPOS:
trunk:
- Firebase
- FirebaseCore
- FirebaseCoreInternal
- FirebaseInstallations
- FirebaseMessaging
- Google-Maps-iOS-Utils
- GoogleDataTransport
- GoogleMaps
- GoogleUtilities
- nanopb
- PromisesObjC
EXTERNAL SOURCES:
alarm:
:path: ".symlinks/plugins/alarm/ios"
app_settings:
:path: ".symlinks/plugins/app_settings/ios"
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
firebase_core:
:path: ".symlinks/plugins/firebase_core/ios"
firebase_messaging:
:path: ".symlinks/plugins/firebase_messaging/ios"
Flutter:
:path: Flutter
flutter_barcode_scanner_plus:
:path: ".symlinks/plugins/flutter_barcode_scanner_plus/ios"
flutter_fgbg:
:path: ".symlinks/plugins/flutter_fgbg/ios"
flutter_local_notifications:
:path: ".symlinks/plugins/flutter_local_notifications/ios"
geolocator_apple:
:path: ".symlinks/plugins/geolocator_apple/darwin"
google_maps_flutter_ios:
:path: ".symlinks/plugins/google_maps_flutter_ios/ios"
maps_launcher:
:path: ".symlinks/plugins/maps_launcher/ios"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
SPEC CHECKSUMS:
alarm: 9ff6d2dae9bd69c4022622f7e0a1e9c1dd70064e
app_settings: 5127ae0678de1dcc19f2293271c51d37c89428b2
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
Firebase: 1fe1c0a7d9aaea32efe01fbea5f0ebd8d70e53a2
firebase_core: 2d4534e7b489907dcede540c835b48981d890943
firebase_messaging: 75bc93a4df25faccad67f6662ae872ac9ae69b64
FirebaseCore: 8344daef5e2661eb004b177488d6f9f0f24251b7
FirebaseCoreInternal: ef4505d2afb1d0ebbc33162cb3795382904b5679
FirebaseInstallations: 9980995bdd06ec8081dfb6ab364162bdd64245c3
FirebaseMessaging: 2b9f56aa4ed286e1f0ce2ee1d413aabb8f9f5cb9
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_barcode_scanner_plus: e5ef7f41cdbf3086e1b9348dce986dde3aa08696
flutter_fgbg: d3da78df78454b1808f0829a5da9cd17dfe16444
flutter_local_notifications: ad39620c743ea4c15127860f4b5641649a988100
geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e
Google-Maps-iOS-Utils: 66d6de12be1ce6d3742a54661e7a79cb317a9321
google_maps_flutter_ios: 0291eb2aa252298a769b04d075e4a9d747ff7264
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleMaps: 8939898920281c649150e0af74aa291c60f2e77d
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
maps_launcher: edf829809ba9e894d70e569bab11c16352dedb45
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
PODFILE CHECKSUM: 72b62bbc58143b910914489c5ddbf2bfa99b3dd4
COCOAPODS: 1.16.2

View File

@@ -10,10 +10,14 @@
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
5EFD34A62DA7D89800351DB2 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5EFD34A52DA7D89800351DB2 /* GoogleService-Info.plist */; };
634E6B9E5E8AF15560F03990 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 501AA52756E2B08A7E1FBE73 /* Pods_Runner.framework */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
A241E2BF2EAA2F1C00664284 /* warning_alarm.caf in Resources */ = {isa = PBXBuildFile; fileRef = A241E2BE2EAA2F1C00664284 /* warning_alarm.caf */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@@ -42,10 +46,15 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
22127402AC507377520FA076 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
25C6B6667708ECABC11CB63A /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
30C8386A287397C265D21D0C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
501AA52756E2B08A7E1FBE73 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
5EE624442DA6030500B7B650 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; }; 5EE624442DA6030500B7B650 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
5EFD34A52DA7D89800351DB2 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
@@ -56,6 +65,7 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
A241E2BE2EAA2F1C00664284 /* warning_alarm.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = warning_alarm.caf; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@@ -63,6 +73,8 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */,
634E6B9E5E8AF15560F03990 /* Pods_Runner.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -77,6 +89,14 @@
path = RunnerTests; path = RunnerTests;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
7E17DC341A9804E98C0A4D9D /* Frameworks */ = {
isa = PBXGroup;
children = (
501AA52756E2B08A7E1FBE73 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = { 9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -91,10 +111,13 @@
97C146E51CF9000F007C117D = { 97C146E51CF9000F007C117D = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
A241E2BE2EAA2F1C00664284 /* warning_alarm.caf */,
9740EEB11CF90186004384FC /* Flutter */, 9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */, 97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */, 97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */, 331C8082294A63A400263BE5 /* RunnerTests */,
E4B9BF767E8449E8CDAC1C45 /* Pods */,
7E17DC341A9804E98C0A4D9D /* Frameworks */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@@ -114,6 +137,7 @@
97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
5EFD34A52DA7D89800351DB2 /* GoogleService-Info.plist */,
97C147021CF9000F007C117D /* Info.plist */, 97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
@@ -123,6 +147,16 @@
path = Runner; path = Runner;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
E4B9BF767E8449E8CDAC1C45 /* Pods */ = {
isa = PBXGroup;
children = (
30C8386A287397C265D21D0C /* Pods-Runner.debug.xcconfig */,
25C6B6667708ECABC11CB63A /* Pods-Runner.release.xcconfig */,
22127402AC507377520FA076 /* Pods-Runner.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
@@ -147,18 +181,24 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = ( buildPhases = (
63291DA6552A39BD8B950BE6 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */, 9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */, 97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */, 97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */, 97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */, 9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
6E40E7EA960942FA803CE7C6 /* [CP] Embed Pods Frameworks */,
1DD8F2A74CC23CF717FB4664 /* [CP] Copy Pods Resources */,
); );
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
); );
name = Runner; name = Runner;
packageProductDependencies = (
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */,
);
productName = Runner; productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
@@ -169,7 +209,7 @@
97C146E61CF9000F007C117D /* Project object */ = { 97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastUpgradeCheck = 1300; LastUpgradeCheck = 1510;
ORGANIZATIONNAME = ""; ORGANIZATIONNAME = "";
TargetAttributes = { TargetAttributes = {
331C8080294A63A400263BE5 = { 331C8080294A63A400263BE5 = {
@@ -191,6 +231,9 @@
Base, Base,
); );
mainGroup = 97C146E51CF9000F007C117D; mainGroup = 97C146E51CF9000F007C117D;
packageReferences = (
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */,
);
productRefGroup = 97C146EF1CF9000F007C117D /* Products */; productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = ""; projectDirPath = "";
projectRoot = ""; projectRoot = "";
@@ -215,6 +258,8 @@
files = ( files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
5EFD34A62DA7D89800351DB2 /* GoogleService-Info.plist in Resources */,
A241E2BF2EAA2F1C00664284 /* warning_alarm.caf in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
); );
@@ -223,6 +268,23 @@
/* End PBXResourcesBuildPhase section */ /* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */
1DD8F2A74CC23CF717FB4664 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1; alwaysOutOfDate = 1;
@@ -239,6 +301,45 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
}; };
63291DA6552A39BD8B950BE6 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
6E40E7EA960942FA803CE7C6 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = { 9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1; alwaysOutOfDate = 1;
@@ -345,7 +446,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
@@ -361,15 +462,22 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = WM843WW2NF;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SmartFM;
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.5;
PRODUCT_BUNDLE_IDENTIFIER = vn.smatec.sfm; PRODUCT_BUNDLE_IDENTIFIER = vn.smatec.sfm;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
@@ -470,7 +578,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@@ -519,7 +627,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
@@ -537,15 +645,22 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = WM843WW2NF;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SmartFM;
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.5;
PRODUCT_BUNDLE_IDENTIFIER = vn.smatec.sfm; PRODUCT_BUNDLE_IDENTIFIER = vn.smatec.sfm;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@@ -560,15 +675,22 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = WM843WW2NF;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SmartFM;
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.5;
PRODUCT_BUNDLE_IDENTIFIER = vn.smatec.sfm; PRODUCT_BUNDLE_IDENTIFIER = vn.smatec.sfm;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
@@ -609,6 +731,20 @@
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
/* End XCConfigurationList section */ /* End XCConfigurationList section */
/* Begin XCLocalSwiftPackageReference section */
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = {
isa = XCLocalSwiftPackageReference;
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
};
/* End XCLocalSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = {
isa = XCSwiftPackageProductDependency;
productName = FlutterGeneratedPluginSwiftPackage;
};
/* End XCSwiftPackageProductDependency section */
}; };
rootObject = 97C146E61CF9000F007C117D /* Project object */; rootObject = 97C146E61CF9000F007C117D /* Project object */;
} }

View File

@@ -1,10 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1300" LastUpgradeVersion = "1510"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"> buildImplicitDependencies = "YES">
<PreActions>
<ExecutionAction
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
<ActionContent
title = "Run Prepare Flutter Framework Script"
scriptText = "/bin/sh &quot;$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh&quot; prepare&#10;">
<EnvironmentBuildable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</EnvironmentBuildable>
</ActionContent>
</ExecutionAction>
</PreActions>
<BuildActionEntries> <BuildActionEntries>
<BuildActionEntry <BuildActionEntry
buildForTesting = "YES" buildForTesting = "YES"
@@ -26,6 +44,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion> <MacroExpansion>
<BuildableReference <BuildableReference
@@ -54,11 +73,13 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES" debugDocumentVersioning = "YES"
debugServiceExtension = "internal" debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES"> allowLocationSimulation = "YES">
<BuildableProductRunnable <BuildableProductRunnable
runnableDebuggingMode = "0"> runnableDebuggingMode = "0">

View File

@@ -4,4 +4,7 @@
<FileRef <FileRef
location = "group:Runner.xcodeproj"> location = "group:Runner.xcodeproj">
</FileRef> </FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace> </Workspace>

View File

@@ -1,25 +1,26 @@
import UIKit import UIKit
import Flutter import Flutter
import flutter_local_notifications import flutter_local_notifications
import GoogleMaps
import FirebaseCore
import FirebaseMessaging
import UserNotifications
import alarm
@UIApplicationMain @main
@objc class AppDelegate: FlutterAppDelegate { @objc class AppDelegate: FlutterAppDelegate {
override func application( override func application(
_ application: UIApplication, _ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool { ) -> Bool {
// This is required to make any communication available in the action isolate.
FlutterLocalNotificationsPlugin.setPluginRegistrantCallback { (registry) in
GeneratedPluginRegistrant.register(with: registry)
}
GMSServices.provideAPIKey("AIzaSyDI8b-PUgKUgj5rHdtgEHCwWjUXYJrqYhE")
GeneratedPluginRegistrant.register(with: self)
if #available(iOS 10.0, *) { if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate
} }
SwiftAlarmPlugin.registerBackgroundTasks()
FirebaseApp.configure()
GMSServices.provideAPIKey("AIzaSyA9C7Pmxw6Gw3H2mM4WA_XGngRIIr2VS7k")
GeneratedPluginRegistrant.register(with: self) GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions) return super.application(application, didFinishLaunchingWithOptions: launchOptions)
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 755 B

After

Width:  |  Height:  |  Size: 729 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<scenes> <scenes>
<!--Flutter View Controller--> <!--Flutter View Controller-->
@@ -14,13 +16,14 @@
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/> <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides> </layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/> <rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view> </view>
</viewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects> </objects>
<point key="canvasLocation" x="121" y="-34"/>
</scene> </scene>
</scenes> </scenes>
</document> </document>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>API_KEY</key>
<string>AIzaSyDkCAxj9W1HNpi-bBdDH6lmid6WIlMsiO8</string>
<key>GCM_SENDER_ID</key>
<string>926568447136</string>
<key>PLIST_VERSION</key>
<string>1</string>
<key>BUNDLE_ID</key>
<string>vn.smatec.sfm</string>
<key>PROJECT_ID</key>
<string>smart-fire-monitoring</string>
<key>STORAGE_BUCKET</key>
<string>smart-fire-monitoring.firebasestorage.app</string>
<key>IS_ADS_ENABLED</key>
<false></false>
<key>IS_ANALYTICS_ENABLED</key>
<false></false>
<key>IS_APPINVITE_ENABLED</key>
<true></true>
<key>IS_GCM_ENABLED</key>
<true></true>
<key>IS_SIGNIN_ENABLED</key>
<true></true>
<key>GOOGLE_APP_ID</key>
<string>1:926568447136:ios:e14244264188362114ddc5</string>
</dict>
</plist>

View File

@@ -7,7 +7,7 @@
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>Sfm App</string> <string>SmartFM</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
@@ -39,6 +39,7 @@
<key>UIBackgroundModes</key> <key>UIBackgroundModes</key>
<array> <array>
<string>fetch</string> <string>fetch</string>
<string>audio</string>
<string>remote-notification</string> <string>remote-notification</string>
</array> </array>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
@@ -60,5 +61,9 @@
</array> </array>
<key>UIViewControllerBasedStatusBarAppearance</key> <key>UIViewControllerBasedStatusBarAppearance</key>
<false/> <false/>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.gdelataillade.fetch</string>
</array>
</dict> </dict>
</plist> </plist>

Binary file not shown.

BIN
ios/warning_alarm.caf Normal file

Binary file not shown.

View File

@@ -4,7 +4,6 @@ import '../product/base/bloc/base_bloc.dart';
import '../feature/bell/bell_model.dart'; import '../feature/bell/bell_model.dart';
class BellBloc extends BlocBase { class BellBloc extends BlocBase {
final bellItems = StreamController<List<BellItems>>.broadcast(); final bellItems = StreamController<List<BellItems>>.broadcast();
StreamSink<List<BellItems>> get sinkBellItems => bellItems.sink; StreamSink<List<BellItems>> get sinkBellItems => bellItems.sink;
Stream<List<BellItems>> get streamBellItems => bellItems.stream; Stream<List<BellItems>> get streamBellItems => bellItems.stream;

View File

@@ -1,19 +1,15 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:sfm_app/product/services/api_services.dart';
import 'package:sfm_app/product/utils/device_utils.dart';
import '../product/services/api_services.dart';
import '../product/utils/date_time_utils.dart'; import '../product/utils/date_time_utils.dart';
import '../feature/device_log/device_logs_model.dart'; import '../feature/device_log/device_logs_model.dart';
import '../feature/devices/device_model.dart'; import '../feature/devices/device_model.dart';
import '../product/base/bloc/base_bloc.dart'; import '../product/base/bloc/base_bloc.dart';
import '../product/utils/device_utils.dart';
class DetailDeviceBloc extends BlocBase { class DetailDeviceBloc extends BlocBase {
APIServices apiServices = APIServices(); APIServices apiServices = APIServices();
@@ -42,14 +38,12 @@ class DetailDeviceBloc extends BlocBase {
String thingID, String thingID,
Completer<GoogleMapController> controller, Completer<GoogleMapController> controller,
) async { ) async {
String body = await apiServices.getDeviceInfomation(thingID); await apiServices.execute(context, () async {
if (body != "") { Device device = await apiServices.getDeviceInformation(thingID);
final data = jsonDecode(body);
Device device = Device.fromJson(data);
sinkDeviceInfo.add(device); sinkDeviceInfo.add(device);
if (device.areaPath != null) { if (device.areaPath != null) {
String fullLocation = await DeviceUtils.instance String fullLocation = await DeviceUtils.instance
.getFullDeviceLocation(context, device.areaPath!); .getFullDeviceLocation(context, device.areaPath!, "");
log("Location: $fullLocation"); log("Location: $fullLocation");
sinkDeviceLocation.add(fullLocation); sinkDeviceLocation.add(fullLocation);
} }
@@ -74,20 +68,56 @@ class DetailDeviceBloc extends BlocBase {
mapController mapController
.animateCamera(CameraUpdate.newCameraPosition(cameraPosition)); .animateCamera(CameraUpdate.newCameraPosition(cameraPosition));
} }
} });
// try {
// Device device = await apiServices.getDeviceInformation(thingID);
// sinkDeviceInfo.add(device);
// if (device.areaPath != null) {
// String fullLocation = await DeviceUtils.instance
// .getFullDeviceLocation(context, device.areaPath!, "");
// log("Location: $fullLocation");
// sinkDeviceLocation.add(fullLocation);
// }
// Map<String, dynamic> sensorMap = {};
// if (device.status!.sensors != null) {
// sensorMap = DeviceUtils.instance
// .getDeviceSensors(context, device.status!.sensors!);
// } else {
// sensorMap = DeviceUtils.instance.getDeviceSensors(context, []);
// }
// sinkDeviceSensor.add(sensorMap);
// if (device.settings!.latitude! != "" &&
// device.settings!.longitude! != "") {
// final CameraPosition cameraPosition = CameraPosition(
// target: LatLng(
// double.parse(device.settings!.latitude!),
// double.parse(device.settings!.longitude!),
// ),
// zoom: 13,
// );
// final GoogleMapController mapController = await controller.future;
// mapController
// .animateCamera(CameraUpdate.newCameraPosition(cameraPosition));
// }
// } catch (e) {
// if (!context.mounted) return;
// showErrorTopSnackBarCustom(context, e.toString());
// }
} }
void findLocation(BuildContext context, String areaPath) async { void findLocation(BuildContext context, String areaPath) async {
String fullLocation = String fullLocation =
await DeviceUtils.instance.getFullDeviceLocation(context, areaPath); await DeviceUtils.instance.getFullDeviceLocation(context, areaPath, "");
sinkDeviceLocation.add(fullLocation); sinkDeviceLocation.add(fullLocation);
} }
void getNearerSensorValue(String thingID) async { void getNearerSensorValue(BuildContext context, String thingID) async {
apiServices.execute(context, () async {
List<SensorLogs> sensorTemps = []; List<SensorLogs> sensorTemps = [];
DateTime twoDaysAgo = DateTime.now().subtract(const Duration(days: 2)); DateTime twoDaysAgo = DateTime.now().subtract(const Duration(days: 2));
String from = DateTimeUtils.instance.formatDateTimeToString(twoDaysAgo); String from = DateTimeUtils.instance.formatDateTimeToString(twoDaysAgo);
String now = DateTimeUtils.instance.formatDateTimeToString(DateTime.now()); String now =
DateTimeUtils.instance.formatDateTimeToString(DateTime.now());
Map<String, dynamic> params = { Map<String, dynamic> params = {
'thing_id': thingID, 'thing_id': thingID,
'from': from, 'from': from,
@@ -95,10 +125,8 @@ class DetailDeviceBloc extends BlocBase {
'limit': '100', 'limit': '100',
'n': '7', 'n': '7',
}; };
final body = await apiServices.getLogsOfDevice(thingID, params); DeviceLog devicesListLog =
if (body != "") { await apiServices.getLogsOfDevice(thingID, params);
final data = jsonDecode(body);
DeviceLog devicesListLog = DeviceLog.fromJson(data);
if (devicesListLog.sensors!.isNotEmpty) { if (devicesListLog.sensors!.isNotEmpty) {
for (var sensor in devicesListLog.sensors!) { for (var sensor in devicesListLog.sensors!) {
sensorTemps.add(sensor); sensorTemps.add(sensor);
@@ -108,6 +136,35 @@ class DetailDeviceBloc extends BlocBase {
} else { } else {
sinkSensorTemps.add([]); sinkSensorTemps.add([]);
} }
} });
// try {
// List<SensorLogs> sensorTemps = [];
// DateTime twoDaysAgo = DateTime.now().subtract(const Duration(days: 2));
// String from = DateTimeUtils.instance.formatDateTimeToString(twoDaysAgo);
// String now =
// DateTimeUtils.instance.formatDateTimeToString(DateTime.now());
// Map<String, dynamic> params = {
// 'thing_id': thingID,
// 'from': from,
// 'to': now,
// 'limit': '100',
// 'n': '7',
// };
// DeviceLog devicesListLog =
// await apiServices.getLogsOfDevice(thingID, params);
// if (devicesListLog.sensors!.isNotEmpty) {
// for (var sensor in devicesListLog.sensors!) {
// sensorTemps.add(sensor);
// }
// sensorTemps = sensorTemps.reversed.toList();
// sinkSensorTemps.add(sensorTemps);
// } else {
// sinkSensorTemps.add([]);
// }
// } catch (e) {
// if (!context.mounted) return;
// showErrorTopSnackBarCustom(context, e.toString());
// sinkSensorTemps.add([]);
// }
} }
} }

View File

@@ -1,12 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'package:flutter/material.dart';
import '../feature/devices/device_model.dart'; import '../feature/devices/device_model.dart';
import '../product/base/bloc/base_bloc.dart'; import '../product/base/bloc/base_bloc.dart';
import '../product/constant/app/app_constants.dart'; import '../product/constant/app/app_constants.dart';
import '../product/services/api_services.dart'; import '../product/services/api_services.dart';
import '../product/utils/date_time_utils.dart'; import '../product/utils/date_time_utils.dart';
import '../product/utils/device_utils.dart'; import '../product/utils/device_utils.dart';
import '../feature/device_log/device_logs_model.dart'; import '../feature/device_log/device_logs_model.dart';
@@ -35,28 +34,28 @@ class DeviceLogsBloc extends BlocBase {
@override @override
void dispose() {} void dispose() {}
void getAllDevices() async { void getAllDevices(BuildContext context) async {
String body = await apiServices.getOwnerDevices(); await apiServices.execute(context, () async {
if (body != "") { List<Device> originalDevices = await apiServices.getOwnerDevices();
final data = jsonDecode(body);
List<dynamic> items = data['items'];
List<Device> originalDevices = Device.fromJsonDynamicList(items);
List<Device> devices = List<Device> devices =
DeviceUtils.instance.sortDeviceByState(originalDevices); DeviceUtils.instance.sortDeviceByState(originalDevices);
sinkAllDevices.add(devices); sinkAllDevices.add(devices);
} });
} }
void getDeviceLogByThingID( void getDeviceLogByThingID(
BuildContext context,
int offset, int offset,
String thingID, String thingID,
DateTime fromDate, DateTime fromDate,
List<SensorLogs> sensors, List<SensorLogs> sensors,
) async { ) async {
await apiServices.execute(context, () async {
sinkmessage.add(ApplicationConstants.LOADING); sinkmessage.add(ApplicationConstants.LOADING);
String fromDateString = String fromDateString =
DateTimeUtils.instance.formatDateTimeToString(fromDate); DateTimeUtils.instance.formatDateTimeToString(fromDate);
String now = DateTimeUtils.instance.formatDateTimeToString(DateTime.now()); String now =
DateTimeUtils.instance.formatDateTimeToString(DateTime.now());
Map<String, dynamic> params = { Map<String, dynamic> params = {
'thing_id': thingID, 'thing_id': thingID,
'from': fromDateString, 'from': fromDateString,
@@ -65,10 +64,8 @@ class DeviceLogsBloc extends BlocBase {
"offset": offset.toString(), "offset": offset.toString(),
"asc": "true" "asc": "true"
}; };
final body = await apiServices.getLogsOfDevice(thingID, params); DeviceLog devicesListLog =
if (body != "") { await apiServices.getLogsOfDevice(thingID, params);
final data = jsonDecode(body);
DeviceLog devicesListLog = DeviceLog.fromJson(data);
if (devicesListLog.sensors!.isEmpty) { if (devicesListLog.sensors!.isEmpty) {
bool hasMore = false; bool hasMore = false;
sinkHasMore.add(hasMore); sinkHasMore.add(hasMore);
@@ -81,6 +78,38 @@ class DeviceLogsBloc extends BlocBase {
sinkmessage.add(ApplicationConstants.NO_DATA); sinkmessage.add(ApplicationConstants.NO_DATA);
} }
sinkSensors.add(sensors); sinkSensors.add(sensors);
} });
// try {
// sinkmessage.add(ApplicationConstants.LOADING);
// String fromDateString =
// DateTimeUtils.instance.formatDateTimeToString(fromDate);
// String now =
// DateTimeUtils.instance.formatDateTimeToString(DateTime.now());
// Map<String, dynamic> params = {
// 'thing_id': thingID,
// 'from': fromDateString,
// 'to': now,
// 'limit': '30',
// "offset": offset.toString(),
// "asc": "true"
// };
// DeviceLog devicesListLog =
// await apiServices.getLogsOfDevice(thingID, params);
// if (devicesListLog.sensors!.isEmpty) {
// bool hasMore = false;
// sinkHasMore.add(hasMore);
// }
// if (devicesListLog.sensors!.isNotEmpty) {
// for (var sensor in devicesListLog.sensors!) {
// sensors.add(sensor);
// }
// } else {
// sinkmessage.add(ApplicationConstants.NO_DATA);
// }
// sinkSensors.add(sensors);
// } catch (e) {
// if (!context.mounted) return;
// showErrorTopSnackBarCustom(context, e.toString());
// }
} }
} }

View File

@@ -1,5 +1,4 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../feature/settings/device_notification_settings/device_notification_settings_model.dart'; import '../feature/settings/device_notification_settings/device_notification_settings_model.dart';
@@ -36,7 +35,6 @@ class DeviceNotificationSettingsBloc extends BlocBase {
StreamSink<String> get sinkMessageChange => messageChange.sink; StreamSink<String> get sinkMessageChange => messageChange.sink;
Stream<String> get streaMmessageChange => messageChange.stream; Stream<String> get streaMmessageChange => messageChange.stream;
@override @override
void dispose() {} void dispose() {}
} }

View File

@@ -1,16 +1,16 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:developer';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import '../product/services/api_services.dart'; import '../product/services/api_services.dart';
import '../product/services/language_services.dart'; import '../product/services/language_services.dart';
import '../product/shared/model/ward_model.dart'; import '../product/shared/model/ward_model.dart';
import '../product/shared/shared_snack_bar.dart';
import '../product/utils/response_status_utils.dart'; import '../product/utils/response_status_utils.dart';
import '../product/shared/model/district_model.dart'; import '../product/shared/model/district_model.dart';
import '../product/shared/model/province_model.dart'; import '../product/shared/model/province_model.dart';
import '../feature/devices/device_model.dart'; import '../feature/devices/device_model.dart';
@@ -75,151 +75,281 @@ class DeviceUpdateBloc extends BlocBase {
// deviceInfo.done; // deviceInfo.done;
} }
Future<void> getAllProvinces() async { Future<void> getAllProvinces(BuildContext context) async {
List<DropdownMenuItem<Province>> provincesData = []; List<DropdownMenuItem<Province>> provincesData = [];
provincesData.clear(); provincesData.clear();
sinkListProvinces.add(provincesData); sinkListProvinces.add(provincesData);
final body = await apiServices.getAllProvinces(); await apiServices.execute(context, () async {
final data = jsonDecode(body); List<Province> provinces = await apiServices.getAllProvinces();
List<dynamic> items = data["items"];
final provinces = Province.fromJsonDynamicList(items);
for (var province in provinces) { for (var province in provinces) {
provincesData.add( provincesData.add(
DropdownMenuItem(value: province, child: Text(province.fullName!))); DropdownMenuItem(value: province, child: Text(province.fullName!)));
} }
sinkListProvinces.add(provincesData); sinkListProvinces.add(provincesData);
});
} }
Future<void> getAllDistricts(String provinceID) async { Future<void> getAllDistricts(BuildContext context, String provinceID) async {
List<DropdownMenuItem<District>> districtsData = []; List<DropdownMenuItem<District>> districtsData = [];
districtsData.clear(); districtsData.clear();
sinkListDistricts.add(districtsData); sinkListDistricts.add(districtsData);
final body = await apiServices.getAllDistricts(provinceID); await apiServices.execute(context, () async {
final data = jsonDecode(body); final districts = await apiServices.getAllDistricts(provinceID);
List<dynamic> items = data["items"];
final districts = District.fromJsonDynamicList(items);
for (var district in districts) { for (var district in districts) {
districtsData.add( districtsData.add(
DropdownMenuItem(value: district, child: Text(district.fullName!))); DropdownMenuItem(value: district, child: Text(district.fullName!)));
} }
sinkListDistricts.add(districtsData); sinkListDistricts.add(districtsData);
});
} }
Future<void> getAllWards(String districtID) async { Future<void> getAllWards(BuildContext context, String districtID) async {
List<DropdownMenuItem<Ward>> wardsData = []; List<DropdownMenuItem<Ward>> wardsData = [];
wardsData.clear(); wardsData.clear();
sinkListWards.add(wardsData); sinkListWards.add(wardsData);
final body = await apiServices.getAllWards(districtID); await apiServices.execute(context, () async {
final data = jsonDecode(body); final wards = await apiServices.getAllWards(districtID);
List<dynamic> items = data["items"];
final wards = Ward.fromJsonDynamicList(items);
for (var ward in wards) { for (var ward in wards) {
wardsData.add(DropdownMenuItem(value: ward, child: Text(ward.fullName!))); wardsData
.add(DropdownMenuItem(value: ward, child: Text(ward.fullName!)));
} }
sinkListWards.add(wardsData); sinkListWards.add(wardsData);
});
} }
Future<void> getDeviceInfomation( Future<void> getDeviceInformation(
BuildContext context,
String thingID, String thingID,
List<DropdownMenuItem<District>> districtsData,
List<DropdownMenuItem<Ward>> wardsData,
TextEditingController deviceNameController, TextEditingController deviceNameController,
TextEditingController latitudeController, TextEditingController latitudeController,
TextEditingController longitudeController) async { TextEditingController longitudeController) async {
String body = await apiServices.getDeviceInfomation(thingID); await apiServices.execute(context, () async {
final data = jsonDecode(body); Device device = await apiServices.getDeviceInformation(thingID);
Device device = Device.fromJson(data);
sinkDeviceInfo.add(device); sinkDeviceInfo.add(device);
deviceNameController.text = device.name ?? ""; deviceNameController.text = device.name ?? "";
latitudeController.text = device.settings!.latitude ?? ""; latitudeController.text = device.settings!.latitude ?? "";
longitudeController.text = device.settings!.longitude ?? ""; longitudeController.text = device.settings!.longitude ?? "";
if (device.areaPath != "") {
if (device.areaPath != null && device.areaPath!.isNotEmpty) {
List<String> areaPath = device.areaPath!.split('_'); List<String> areaPath = device.areaPath!.split('_');
// Kiểm tra độ dài của areaPath
if (areaPath.length >= 3) {
String provinceCode = areaPath[0]; String provinceCode = areaPath[0];
String districtCode = areaPath[1]; String districtCode = areaPath[1];
String wardCode = areaPath[2]; String wardCode = areaPath[2];
getAllDistricts(provinceCode);
getAllWards(districtCode); // Kiểm tra các mã có hợp lệ không (không rỗng)
final provinceResponse = await apiServices.getProvinceByID(provinceCode); if (provinceCode.isNotEmpty &&
final provincesData = jsonDecode(provinceResponse); districtCode.isNotEmpty &&
Province province = Province.fromJson(provincesData['data']); wardCode.isNotEmpty) {
final districtResponse = await apiServices.getDistrictByID(districtCode); try {
final districtData = jsonDecode(districtResponse); // Lấy danh sách districts và wards
District district = District.fromJson(districtData['data']); await getAllDistricts(context, provinceCode);
final wardResponse = await apiServices.getWardByID(wardCode); await getAllWards(context, districtCode);
final wardData = jsonDecode(wardResponse);
Ward ward = Ward.fromJson(wardData['data']); // Xử lý Province
try {
Province province =
await apiServices.getProvinceByID(provinceCode);
Map<String, String> provinceData = { Map<String, String> provinceData = {
"name": province.fullName!, "name": province.fullName ?? "Unknown Province",
"code": province.code! "code": province.code ?? provinceCode
}; };
sinkProvinceData.add(provinceData); sinkProvinceData.add(provinceData);
} catch (e) {
// Thêm dữ liệu mặc định khi lỗi
Map<String, String> provinceData = {
"name": "Error Loading Province",
"code": provinceCode
};
sinkProvinceData.add(provinceData);
if (!context.mounted) return;
showErrorTopSnackBarCustom(context, e.toString());
}
// Xử lý District
try {
District district =
await apiServices.getDistrictByID(districtCode);
Map<String, String> districData = { Map<String, String> districData = {
"name": district.fullName!, "name": district.fullName ?? "Unknown District",
"code": district.code!, "code": district.code ?? districtCode,
}; };
sinkDistrictData.add(districData); sinkDistrictData.add(districData);
} catch (e) {
log("Lỗi khi lấy thông tin district ($districtCode): $e");
Map<String, String> districData = {
"name": "Error Loading District",
"code": districtCode,
};
sinkDistrictData.add(districData);
}
// Xử lý Ward
try {
Ward ward = await apiServices.getWardByID(wardCode);
Map<String, String> wardMap = { Map<String, String> wardMap = {
"name": ward.fullName!, "name": ward.fullName ?? "Unknown Ward",
"code": ward.code!, "code": ward.code ?? wardCode,
};
sinkWardData.add(wardMap);
} catch (e) {
log("Lỗi khi lấy thông tin ward ($wardCode): $e");
Map<String, String> wardMap = {
"name": "Error Loading Ward",
"code": wardCode,
}; };
sinkWardData.add(wardMap); sinkWardData.add(wardMap);
} }
} catch (e) {
log("Lỗi khi gọi getAllDistricts hoặc getAllWards: $e");
}
} else {
await getAllProvinces(context);
log("Một hoặc nhiều mã địa phương trống: Province: $provinceCode, District: $districtCode, Ward: $wardCode");
}
} else {
showNoIconTopSnackBar(
context,
"AreaPath không đủ thông tin: ${device.areaPath}",
Colors.orangeAccent,
Colors.white);
}
}
});
// try {
// Device device = await apiServices.getDeviceInformation(thingID);
// sinkDeviceInfo.add(device);
// deviceNameController.text = device.name ?? "";
// latitudeController.text = device.settings!.latitude ?? "";
// longitudeController.text = device.settings!.longitude ?? "";
// if (device.areaPath != null && device.areaPath!.isNotEmpty) {
// List<String> areaPath = device.areaPath!.split('_');
// // Kiểm tra độ dài của areaPath
// if (areaPath.length >= 3) {
// String provinceCode = areaPath[0];
// String districtCode = areaPath[1];
// String wardCode = areaPath[2];
// // Kiểm tra các mã có hợp lệ không (không rỗng)
// if (provinceCode.isNotEmpty &&
// districtCode.isNotEmpty &&
// wardCode.isNotEmpty) {
// try {
// // Lấy danh sách districts và wards
// await getAllDistricts(context, provinceCode);
// await getAllWards(context, districtCode);
// // Xử lý Province
// try {
// Province province =
// await apiServices.getProvinceByID(provinceCode);
// Map<String, String> provinceData = {
// "name": province.fullName ?? "Unknown Province",
// "code": province.code ?? provinceCode
// };
// sinkProvinceData.add(provinceData);
// } catch (e) {
// // Thêm dữ liệu mặc định khi lỗi
// Map<String, String> provinceData = {
// "name": "Error Loading Province",
// "code": provinceCode
// };
// sinkProvinceData.add(provinceData);
// if (!context.mounted) return;
// showErrorTopSnackBarCustom(context, e.toString());
// }
// // Xử lý District
// try {
// District district =
// await apiServices.getDistrictByID(districtCode);
// Map<String, String> districData = {
// "name": district.fullName ?? "Unknown District",
// "code": district.code ?? districtCode,
// };
// sinkDistrictData.add(districData);
// } catch (e) {
// log("Lỗi khi lấy thông tin district ($districtCode): $e");
// Map<String, String> districData = {
// "name": "Error Loading District",
// "code": districtCode,
// };
// sinkDistrictData.add(districData);
// }
// // Xử lý Ward
// try {
// Ward ward = await apiServices.getWardByID(wardCode);
// Map<String, String> wardMap = {
// "name": ward.fullName ?? "Unknown Ward",
// "code": ward.code ?? wardCode,
// };
// sinkWardData.add(wardMap);
// } catch (e) {
// print("Lỗi khi lấy thông tin ward ($wardCode): $e");
// Map<String, String> wardMap = {
// "name": "Error Loading Ward",
// "code": wardCode,
// };
// sinkWardData.add(wardMap);
// }
// } catch (e) {
// print("Lỗi khi gọi getAllDistricts hoặc getAllWards: $e");
// }
// } else {
// await getAllProvinces(context);
// print(
// "Một hoặc nhiều mã địa phương trống: Province: $provinceCode, District: $districtCode, Ward: $wardCode");
// }
// } else {
// showNoIconTopSnackBar(
// context,
// "AreaPath không đủ thông tin: ${device.areaPath}",
// Colors.orangeAccent,
// Colors.white);
// }
// }
// } catch (e) {
// showNoIconTopSnackBar(context, "Lỗi trong getDeviceInfomation: $e",
// Colors.orangeAccent, Colors.white);
// }
} }
Future<Province> getProvinceByName(String name) async { Future<Province> getProvinceByName(BuildContext context, String name) async {
final response = await apiServices.getProvincesByName(name); return await apiServices.execute(context, () async {
final data = jsonDecode(response); List<Province> provinces = await apiServices.getProvincesByName(name);
if (data != null &&
data.containsKey('items') &&
data['items'] != null &&
data['items'].isNotEmpty) {
List<dynamic> items = data['items'];
List<Province> provinces = Province.fromJsonDynamicList(items);
if (provinces.isNotEmpty) { if (provinces.isNotEmpty) {
return provinces[0]; return provinces[0];
} } else {
}
return Province(name: "null"); return Province(name: "null");
} }
});
Future<District> getDistrictByName(String name, String provinceCode) async {
final response = await apiServices.getDistrictsByName(name);
if (response != "") {
final data = jsonDecode(response);
List<dynamic> items = data['items'];
if (items.isNotEmpty) {
List<District> districts = District.fromJsonDynamicList(items);
if (districts.isNotEmpty) {
for (var district in districts) {
if (district.provinceCode == provinceCode) {
return district;
}
}
}
}
}
return District(name: "null");
} }
Future<Ward> getWardByName(String name, String districtCode) async { Future<District> getDistrictByName(
final response = await apiServices.getWarsdByName(name); BuildContext context, String name, String provinceCode) async {
final data = jsonDecode(response); return apiServices.execute(context, () async {
if (data != null && data['items'] != null) { final districts = await apiServices.getDistrictsByName(name);
List<dynamic> items = data['items']; return districts.firstWhere(
if (items.isNotEmpty) { (district) => district.provinceCode == provinceCode,
List<Ward> wards = Ward.fromJsonDynamicList(items); orElse: () => District(name: "null"),
if (wards.isNotEmpty) { );
for (var ward in wards) { });
if (ward.districtCode == districtCode) {
return ward;
} }
}
} Future<Ward> getWardByName(
} BuildContext context, String name, String districtCode) async {
} return apiServices.execute(context, () async {
return Ward(name: "null"); final wards = await apiServices.getWardsByName(name);
return wards.firstWhere(
(ward) => ward.districtCode == districtCode,
orElse: () => Ward(name: "null"),
);
});
} }
Future<void> updateDevice( Future<void> updateDevice(
@@ -232,6 +362,7 @@ class DeviceUpdateBloc extends BlocBase {
String districtCode, String districtCode,
String wardCode, String wardCode,
) async { ) async {
await apiServices.execute(context, () async {
DateTime dateTime = DateTime.now(); DateTime dateTime = DateTime.now();
String formattedDateTime = String formattedDateTime =
DateFormat('yyyy-MM-dd HH:mm:ss').format(dateTime); DateFormat('yyyy-MM-dd HH:mm:ss').format(dateTime);
@@ -251,5 +382,30 @@ class DeviceUpdateBloc extends BlocBase {
appLocalization(context).notification_update_device_success, appLocalization(context).notification_update_device_success,
appLocalization(context).notification_update_device_failed, appLocalization(context).notification_update_device_failed,
); );
});
// try {
// DateTime dateTime = DateTime.now();
// String formattedDateTime =
// DateFormat('yyyy-MM-dd HH:mm:ss').format(dateTime);
// Map<String, dynamic> body = {
// "name": name,
// "area_province": provinceCode,
// "area_district": districtCode,
// "area_ward": wardCode,
// "latitude": latitude,
// "longitude": longitude,
// "note": "User updated device infomation at $formattedDateTime",
// };
// int statusCode = await apiServices.updateOwnerDevice(thingID, body);
// showSnackBarResponseByStatusCodeNoIcon(
// context,
// statusCode,
// appLocalization(context).notification_update_device_success,
// appLocalization(context).notification_update_device_failed,
// );
// } catch (e) {
// if (!context.mounted) return;
// showErrorTopSnackBarCustom(context, e.toString());
// }
} }
} }

View File

@@ -1,11 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'package:flutter/cupertino.dart';
import '../feature/devices/device_model.dart'; import '../feature/devices/device_model.dart';
import '../product/base/bloc/base_bloc.dart'; import '../product/base/bloc/base_bloc.dart';
import '../product/constant/app/app_constants.dart'; import '../product/constant/app/app_constants.dart';
import '../product/services/api_services.dart'; import '../product/services/api_services.dart';
import '../product/shared/shared_snack_bar.dart';
import '../product/utils/device_utils.dart'; import '../product/utils/device_utils.dart';
class DevicesManagerBloc extends BlocBase { class DevicesManagerBloc extends BlocBase {
@@ -73,7 +73,8 @@ class DevicesManagerBloc extends BlocBase {
// } // }
// } // }
void getDeviceByState(int state) async { void getDeviceByState(BuildContext context, int state) async {
try {
sinkTagStates.add([state]); sinkTagStates.add([state]);
Map<String, List<Device>> deviceByState = { Map<String, List<Device>> deviceByState = {
@@ -85,34 +86,38 @@ class DevicesManagerBloc extends BlocBase {
}; };
List<Device> devices = []; List<Device> devices = [];
String body; List<Device> originalDevices = [];
if (state != -2) { if (state != -2) {
body = originalDevices = await apiServices
await apiServices.getOwnerDeviceByState({"state": state.toString()}); .getOwnerDeviceByState({"state": state.toString()});
} else { } else {
body = await apiServices.getOwnerDevices(); originalDevices = await apiServices.getOwnerDevices();
} }
if (body.isNotEmpty) { List<Device> publicDevices = [];
final data = jsonDecode(body);
List<dynamic> items = data['items'];
List<Device> originalDevices = Device.fromJsonDynamicList(items);
for (var device in originalDevices) {
if (device.visibility == "PUBLIC") {
publicDevices.add(device);
}
}
devices = (state != -2) devices = (state != -2)
? DeviceUtils.instance.sortDeviceAZByName(originalDevices) ? DeviceUtils.instance.sortDeviceAZByName(publicDevices)
: DeviceUtils.instance.sortDeviceByState(originalDevices); : DeviceUtils.instance.sortDeviceByState(publicDevices);
if (state == -2) { if (state == -2) {
for (var device in originalDevices) { for (var device in publicDevices) {
String stateKey = _getStateKey(device.state!); String stateKey = _getStateKey(device.state!);
deviceByState[stateKey]!.add(device); deviceByState[stateKey]!.add(device);
} }
sinkDeviceByState.add(deviceByState); sinkDeviceByState.add(deviceByState);
} }
}
sinkAllDevices.add(devices); sinkAllDevices.add(devices);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(context, e.toString());
}
} }
String _getStateKey(int state) { String _getStateKey(int state) {

View File

@@ -1,15 +1,13 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import '../feature/devices/device_model.dart'; import '../feature/devices/device_model.dart';
import '../product/base/bloc/base_bloc.dart'; import '../product/base/bloc/base_bloc.dart';
import '../product/services/api_services.dart'; import '../product/services/api_services.dart';
import '../product/services/language_services.dart'; import '../product/services/language_services.dart';
import '../product/utils/response_status_utils.dart'; import '../product/utils/response_status_utils.dart';
import '../feature/inter_family/group_detail/group_detail_model.dart'; import '../feature/inter_family/group_detail/group_detail_model.dart';
class DetailGroupBloc extends BlocBase { class DetailGroupBloc extends BlocBase {
@@ -29,12 +27,10 @@ class DetailGroupBloc extends BlocBase {
@override @override
void dispose() {} void dispose() {}
Future<void> getGroupDetail(String groupID) async { Future<void> getGroupDetail(BuildContext context, String groupID) async {
final body = await apiServices.getGroupDetail(groupID); await apiServices.execute(context, () async {
final data = jsonDecode(body);
List<DeviceOfGroup> warningDevices = []; List<DeviceOfGroup> warningDevices = [];
if (data != null) { GroupDetail group = await apiServices.getGroupDetail(groupID);
GroupDetail group = GroupDetail.fromJson(data);
sinkDetailGroup.add(group); sinkDetailGroup.add(group);
if (group.devices != null) { if (group.devices != null) {
for (var device in group.devices!) { for (var device in group.devices!) {
@@ -44,43 +40,104 @@ class DetailGroupBloc extends BlocBase {
} }
sinkWarningDevice.add(warningDevices); sinkWarningDevice.add(warningDevices);
} }
} });
// try {
// List<DeviceOfGroup> warningDevices = [];
// GroupDetail group = await apiServices.getGroupDetail(groupID);
// sinkDetailGroup.add(group);
// if (group.devices != null) {
// for (var device in group.devices!) {
// if (device.state == 1) {
// warningDevices.add(device);
// }
// }
// sinkWarningDevice.add(warningDevices);
// }
// } catch (e) {
// if (!context.mounted) return;
// showErrorTopSnackBarCustom(context, e.toString());
// }
} }
Future<void> approveUserToGroup(BuildContext context, String groupID, Future<void> approveUserToGroup(BuildContext context, String groupID,
String userID, String userName) async { String userID, String userName) async {
await apiServices.execute(context, () async {
Map<String, dynamic> body = {"group_id": groupID, "user_id": userID}; Map<String, dynamic> body = {"group_id": groupID, "user_id": userID};
int statusCode = await apiServices.approveGroup(body); int statusCode = await apiServices.approveGroup(body);
showSnackBarResponseByStatusCode(context, statusCode, showSnackBarResponseByStatusCode(context, statusCode,
"Đã duyệt $userName vào nhóm!", "Duyệt $userName thất bại!"); "Đã duyệt $userName vào nhóm!", "Duyệt $userName thất bại!");
});
// try {
// Map<String, dynamic> body = {"group_id": groupID, "user_id": userID};
// int statusCode = await apiServices.approveGroup(body);
// showSnackBarResponseByStatusCode(context, statusCode,
// "Đã duyệt $userName vào nhóm!", "Duyệt $userName thất bại!");
// } catch (e) {
// if (!context.mounted) return;
// showErrorTopSnackBarCustom(context, e.toString());
// }
} }
Future<void> deleteOrUnapproveUser(BuildContext context, String groupID, Future<void> deleteOrUnapproveUser(BuildContext context, String groupID,
String userID, String userName) async { String userID, String userName) async {
await apiServices.execute(context, () async {
int statusCode = await apiServices.deleteUserInGroup(groupID, userID); int statusCode = await apiServices.deleteUserInGroup(groupID, userID);
showSnackBarResponseByStatusCode(context, statusCode, showSnackBarResponseByStatusCode(context, statusCode,
"Đã xóa người dùng $userName", "Xóa người dùng $userName thất bại"); "Đã xóa người dùng $userName", "Xóa người dùng $userName thất bại");
});
// try {
// int statusCode = await apiServices.deleteUserInGroup(groupID, userID);
// showSnackBarResponseByStatusCode(context, statusCode,
// "Đã xóa người dùng $userName", "Xóa người dùng $userName thất bại");
// } catch (e) {
// if (!context.mounted) return;
// showErrorTopSnackBarCustom(context, e.toString());
// }
} }
Future<void> deleteDevice(BuildContext context, String groupID, Future<void> deleteDevice(BuildContext context, String groupID,
String thingID, String deviceName) async { String thingID, String deviceName) async {
await apiServices.execute(context, () async {
int statusCode = await apiServices.deleteDeviceInGroup(groupID, thingID); int statusCode = await apiServices.deleteDeviceInGroup(groupID, thingID);
showSnackBarResponseByStatusCode(context, statusCode, showSnackBarResponseByStatusCode(context, statusCode,
"Đã xóa thiết bị $deviceName", "Xóa thiết bị $deviceName thất bại"); "Đã xóa thiết bị $deviceName", "Xóa thiết bị $deviceName thất bại");
});
// try {
// int statusCode = await apiServices.deleteDeviceInGroup(groupID, thingID);
// showSnackBarResponseByStatusCode(context, statusCode,
// "Đã xóa thiết bị $deviceName", "Xóa thiết bị $deviceName thất bại");
// } catch (e) {
// if (!context.mounted) return;
// showErrorTopSnackBarCustom(context, e.toString());
// }
} }
Future<void> leaveGroup( Future<void> leaveGroup(
BuildContext context, String groupID, String userID) async { BuildContext context, String groupID, String userID) async {
await apiServices.execute(context, () async {
int statusCode = await apiServices.deleteUserInGroup(groupID, userID); int statusCode = await apiServices.deleteUserInGroup(groupID, userID);
showSnackBarResponseByStatusCode( showSnackBarResponseByStatusCode(
context, context,
statusCode, statusCode,
appLocalization(context).notification_leave_group_success, appLocalization(context).notification_leave_group_success,
appLocalization(context).notification_leave_group_failed); appLocalization(context).notification_leave_group_failed);
});
// try {
// int statusCode = await apiServices.deleteUserInGroup(groupID, userID);
// showSnackBarResponseByStatusCode(
// context,
// statusCode,
// appLocalization(context).notification_leave_group_success,
// appLocalization(context).notification_leave_group_failed);
// } catch (e) {
// if (!context.mounted) return;
// showErrorTopSnackBarCustom(context, e.toString());
// }
} }
Future<void> updateDeviceNameInGroup( Future<void> updateDeviceNameInGroup(
BuildContext context, String thingID, String newAlias) async { BuildContext context, String thingID, String newAlias) async {
await apiServices.execute(context, () async {
Map<String, dynamic> body = {"thing_id": thingID, "alias": newAlias}; Map<String, dynamic> body = {"thing_id": thingID, "alias": newAlias};
int statusCode = await apiServices.updateDeviceAlias(body); int statusCode = await apiServices.updateDeviceAlias(body);
showSnackBarResponseByStatusCode( showSnackBarResponseByStatusCode(
@@ -89,17 +146,29 @@ class DetailGroupBloc extends BlocBase {
appLocalization(context).notification_update_device_success, appLocalization(context).notification_update_device_success,
appLocalization(context).notification_update_device_failed, appLocalization(context).notification_update_device_failed,
); );
});
// try {
// Map<String, dynamic> body = {"thing_id": thingID, "alias": newAlias};
// int statusCode = await apiServices.updateDeviceAlias(body);
// showSnackBarResponseByStatusCode(
// context,
// statusCode,
// appLocalization(context).notification_update_device_success,
// appLocalization(context).notification_update_device_failed,
// );
// } catch (e) {
// if (!context.mounted) return;
// showErrorTopSnackBarCustom(context, e.toString());
// }
} }
Future<List<Device>> getOwnerDevices() async { Future<List<Device>> getOwnerDevices(BuildContext context) {
List<Device> allDevices = []; return apiServices.execute(context, () async {
String body = await apiServices.getOwnerDevices(); final originalDevices = await apiServices.getOwnerDevices();
if (body != "") { return originalDevices
final data = jsonDecode(body); .where((device) => device.visibility == "PUBLIC")
List<dynamic> items = data['items']; .toList();
allDevices = Device.fromJsonDynamicList(items); });
}
return allDevices;
} }
Future<void> addDeviceToGroup( Future<void> addDeviceToGroup(
@@ -107,6 +176,7 @@ class DetailGroupBloc extends BlocBase {
Map<String, dynamic> body = { Map<String, dynamic> body = {
"thing_id": thingID, "thing_id": thingID,
}; };
await apiServices.execute(context, () async {
int statusCode = await apiServices.addDeviceToGroup(groupID, body); int statusCode = await apiServices.addDeviceToGroup(groupID, body);
showSnackBarResponseByStatusCode( showSnackBarResponseByStatusCode(
context, context,
@@ -114,5 +184,18 @@ class DetailGroupBloc extends BlocBase {
appLocalization(context).notification_add_device_success, appLocalization(context).notification_add_device_success,
appLocalization(context).notification_add_device_failed, appLocalization(context).notification_add_device_failed,
); );
});
// try {
// int statusCode = await apiServices.addDeviceToGroup(groupID, body);
// showSnackBarResponseByStatusCode(
// context,
// statusCode,
// appLocalization(context).notification_add_device_success,
// appLocalization(context).notification_add_device_failed,
// );
// } catch (e) {
// if (!context.mounted) return;
// showErrorTopSnackBarCustom(context, e.toString());
// }
} }
} }

View File

@@ -1,34 +1,143 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart';
import '../product/extension/context_extension.dart';
import '../product/services/api_services.dart';
import '../feature/home/device_alias_model.dart'; import '../feature/home/device_alias_model.dart';
import '../product/base/bloc/base_bloc.dart'; import '../product/base/bloc/base_bloc.dart';
import '../product/services/language_services.dart';
import '../product/utils/device_utils.dart';
class HomeBloc extends BlocBase { class HomeBloc extends BlocBase {
APIServices apiServices = APIServices();
final allDevicesAliasMap = StreamController<Map<String,List<DeviceWithAlias>>>.broadcast(); final allDevicesAliasMap =
StreamSink<Map<String,List<DeviceWithAlias>>> get sinkAllDevicesAliasMap => StreamController<Map<String, List<DeviceWithAlias>>?>.broadcast();
StreamSink<Map<String, List<DeviceWithAlias>>?> get sinkAllDevicesAliasMap =>
allDevicesAliasMap.sink; allDevicesAliasMap.sink;
Stream<Map<String,List<DeviceWithAlias>>> get streamAllDevicesAliasMap => Stream<Map<String, List<DeviceWithAlias>>?> get streamAllDevicesAliasMap =>
allDevicesAliasMap.stream; allDevicesAliasMap.stream;
final allDevicesAliasJoinedMap = StreamController<Map<String,List<DeviceWithAlias>>>.broadcast(); // final allDevicesAliasJoinedMap =
StreamSink<Map<String,List<DeviceWithAlias>>> get sinkAllDevicesAliasJoinedMap => // StreamController<Map<String, List<DeviceWithAlias>>>.broadcast();
allDevicesAliasJoinedMap.sink; // StreamSink<Map<String, List<DeviceWithAlias>>>
Stream<Map<String,List<DeviceWithAlias>>> get streamAllDevicesAliasJoinedMap => // get sinkAllDevicesAliasJoinedMap => allDevicesAliasJoinedMap.sink;
allDevicesAliasJoinedMap.stream; // Stream<Map<String, List<DeviceWithAlias>>>
// get streamAllDevicesAliasJoinedMap => allDevicesAliasJoinedMap.stream;
final countNotification = StreamController<int>.broadcast(); final countNotification = StreamController<int>.broadcast();
StreamSink<int> get sinkCountNotification => countNotification.sink; StreamSink<int> get sinkCountNotification => countNotification.sink;
Stream<int> get streamCountNotification => countNotification.stream; Stream<int> get streamCountNotification => countNotification.stream;
final hasJoinedDevice = StreamController<bool?>.broadcast();
StreamSink<bool?> get sinkHasJoinedDevice => hasJoinedDevice.sink;
Stream<bool?> get streamHasJoinedDevice => hasJoinedDevice.stream;
final ownerDevicesStatus = final ownerDevicesStatus =
StreamController<Map<String, List<DeviceWithAlias>>>.broadcast(); StreamController<Map<String, List<DeviceWithAlias>>>.broadcast();
StreamSink<Map<String, List<DeviceWithAlias>>> StreamSink<Map<String, List<DeviceWithAlias>>> get sinkOwnerDevicesStatus =>
get sinkOwnerDevicesStatus => ownerDevicesStatus.sink; ownerDevicesStatus.sink;
Stream<Map<String, List<DeviceWithAlias>>> get streamOwnerDevicesStatus => Stream<Map<String, List<DeviceWithAlias>>> get streamOwnerDevicesStatus =>
ownerDevicesStatus.stream; ownerDevicesStatus.stream;
@override
void dispose() {} final aliasDevices = StreamController<List<DeviceWithAlias>?>.broadcast();
StreamSink<List<DeviceWithAlias>?> get sinkAliasDevices => aliasDevices.sink;
Stream<List<DeviceWithAlias>?> get streamAliasDevices => aliasDevices.stream;
void getOwnerAndJoinedDevices(BuildContext context) async {
await apiServices.execute(context, () async {
List<DeviceWithAlias> devices = await apiServices.getDashBoardDevices().handleApiError();
List<DeviceWithAlias> publicDevices = [];
for (var device in devices) {
if (device.visibility == "PUBLIC") {
publicDevices.add(device);
}
}
// getDeviceStatusAliasMap(publicDevices);
sinkAllDevicesAliasMap.add(null);
sinkAliasDevices.add(publicDevices);
if (!context.mounted) return;
getOwnerDeviceState(context, publicDevices);
});
}
void getOwnerDeviceState(BuildContext context,List<DeviceWithAlias> allDevices) async {
// int notificationCount = 0;
Map<String, List<DeviceWithAlias>> ownerDevicesStatus = {};
if (!context.mounted) return;
sinkOwnerDevicesStatus.add(ownerDevicesStatus);
int count = 0;
for (var device in allDevices) {
// if (device.isOwner != true) continue;
if (!context.mounted) return;
Map<String, dynamic> sensorMap = DeviceUtils.instance
.getDeviceSensors(context, device.status?.sensors ?? []);
if (device.state == 1 || device.state == 3) {
ownerDevicesStatus["state"] ??= [];
ownerDevicesStatus["state"]!.add(device);
if (!context.mounted) return;
sinkOwnerDevicesStatus.add(ownerDevicesStatus);
count++;
}
final noDataMessage = appLocalization(context).no_data_message;
if (sensorMap['sensorBattery'] != noDataMessage) {
if (double.parse(sensorMap['sensorBattery']) <= 20) {
ownerDevicesStatus['battery'] ??= [];
ownerDevicesStatus['battery']!.add(device);
if (!context.mounted) return;
sinkOwnerDevicesStatus.add(ownerDevicesStatus);
count++;
}
}
}
if (!context.mounted) return;
sinkCountNotification.add(count);
}
void getDeviceStatusAliasMap(List<DeviceWithAlias> devices) {
Map<String, List<DeviceWithAlias>> allDevicesAliasMap = {};
for (var key in ['all', 'online', 'offline', 'warning', 'not-use']) {
allDevicesAliasMap[key] = [];
}
for (DeviceWithAlias device in devices) {
allDevicesAliasMap['all']!.add(device);
if (device.state == 0 || device.state == 1) {
allDevicesAliasMap['online']!.add(device);
}
if (device.state == -1) {
allDevicesAliasMap['offline']!.add(device);
}
if (device.state == 1) {
allDevicesAliasMap['warning']!.add(device);
}
if (device.state == -2) {
allDevicesAliasMap['not-use']!.add(device);
}
}
sinkAllDevicesAliasMap.add(allDevicesAliasMap);
}
@override
void dispose() {
allDevicesAliasMap.close();
// allDevicesAliasJoinedMap.close();
countNotification.close();
hasJoinedDevice.close();
ownerDevicesStatus.close();
aliasDevices.close();
}
} }

View File

@@ -1,12 +1,9 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../product/constant/app/app_constants.dart';
import '../product/constant/app/app_constants.dart';
import '../product/services/api_services.dart'; import '../product/services/api_services.dart';
import '../product/base/bloc/base_bloc.dart'; import '../product/base/bloc/base_bloc.dart';
import '../product/services/language_services.dart'; import '../product/services/language_services.dart';
@@ -35,43 +32,32 @@ class InterFamilyBloc extends BlocBase {
@override @override
void dispose() {} void dispose() {}
void getAllGroup(String role) async { void getAllGroup(BuildContext context, String role) async {
List<Group> groups = []; List<Group> groups = [];
sinkCurrentGroups.add(groups); sinkCurrentGroups.add(groups);
final body = await apiServices.getAllGroups(); await apiServices.execute(context, () async {
groups = await apiServices.getAllGroups();
if (body.isNotEmpty) {
final data = jsonDecode(body);
List<dynamic> items = data["items"];
groups = Group.fromJsonDynamicList(items);
groups = sortGroupByName(groups); groups = sortGroupByName(groups);
List<Group> currentGroups = groups.where( List<Group> currentGroups = groups.where(
(group) { (group) {
bool isPublic = group.visibility == "PUBLIC"; bool isPublic = group.visibility == "PUBLIC";
if (role == ApplicationConstants.OWNER_GROUP) { if (role == ApplicationConstants.OWNER_GROUP) {
return group.isOwner == true && isPublic; return group.isOwner == true && isPublic;
} }
if (role == ApplicationConstants.PARTICIPANT_GROUP) { if (role == ApplicationConstants.PARTICIPANT_GROUP) {
return group.isOwner == null && isPublic; return group.isOwner == null && isPublic;
} }
return false; return false;
}, },
).toList(); ).toList();
sinkCurrentGroups.add(currentGroups); sinkCurrentGroups.add(currentGroups);
} else { });
log("Get groups from API failed");
}
log("Inter Family Role: $role");
} }
Future<void> createGroup( Future<void> createGroup(
BuildContext context, String name, String description) async { BuildContext context, String name, String description) async {
APIServices apiServices = APIServices(); await apiServices.execute(context, () async {
Map<String, dynamic> body = {"name": name, "description": description}; Map<String, dynamic> body = {"name": name, "description": description};
int? statusCode = await apiServices.createGroup(body); int? statusCode = await apiServices.createGroup(body);
showSnackBarResponseByStatusCode( showSnackBarResponseByStatusCode(
@@ -79,10 +65,12 @@ class InterFamilyBloc extends BlocBase {
statusCode, statusCode,
appLocalization(context).notification_add_group_success, appLocalization(context).notification_add_group_success,
appLocalization(context).notification_add_group_failed); appLocalization(context).notification_add_group_failed);
});
} }
Future<void> changeGroupInfomation(BuildContext context, String groupID, Future<void> changeGroupInformation(BuildContext context, String groupID,
String name, String description) async { String name, String description) async {
await apiServices.execute(context, () async {
Map<String, dynamic> body = {"name": name, "description": description}; Map<String, dynamic> body = {"name": name, "description": description};
int statusCode = await apiServices.updateGroup(body, groupID); int statusCode = await apiServices.updateGroup(body, groupID);
showSnackBarResponseByStatusCode( showSnackBarResponseByStatusCode(
@@ -90,27 +78,32 @@ class InterFamilyBloc extends BlocBase {
statusCode, statusCode,
appLocalization(context).notification_update_group_success, appLocalization(context).notification_update_group_success,
appLocalization(context).notification_update_group_failed); appLocalization(context).notification_update_group_failed);
});
} }
Future<void> joinGroup(BuildContext context, String groupID) async { Future<void> joinGroup(BuildContext context, String groupID) async {
Map<String, dynamic> body = { Map<String, dynamic> body = {
"group_id": groupID, "group_id": groupID,
}; };
await apiServices.execute(context, () async {
int statusCode = await apiServices.joinGroup(groupID, body); int statusCode = await apiServices.joinGroup(groupID, body);
showSnackBarResponseByStatusCode( showSnackBarResponseByStatusCode(
context, context,
statusCode, statusCode,
appLocalization(context).notification_join_request_group_success, appLocalization(context).notification_join_request_group_success,
appLocalization(context).notification_join_request_group_failed); appLocalization(context).notification_join_request_group_failed);
});
} }
Future<void> deleteGroup(BuildContext context, String groupID) async { Future<void> deleteGroup(BuildContext context, String groupID) async {
await apiServices.execute(context, () async {
int statusCode = await apiServices.deleteGroup(groupID); int statusCode = await apiServices.deleteGroup(groupID);
showSnackBarResponseByStatusCode( showSnackBarResponseByStatusCode(
context, context,
statusCode, statusCode,
appLocalization(context).notification_delete_group_success, appLocalization(context).notification_delete_group_success,
appLocalization(context).notification_delete_group_failed); appLocalization(context).notification_delete_group_failed);
});
} }
List<Group> sortGroupByName(List<Group> groups) { List<Group> sortGroupByName(List<Group> groups) {

View File

@@ -2,7 +2,6 @@ import 'dart:async';
import '../product/base/bloc/base_bloc.dart'; import '../product/base/bloc/base_bloc.dart';
class LoginBloc extends BlocBase { class LoginBloc extends BlocBase {
final loginRequest = StreamController<Map<String, dynamic>>.broadcast(); final loginRequest = StreamController<Map<String, dynamic>>.broadcast();
StreamSink<Map<String, dynamic>> get sinkLoginRequest => loginRequest.sink; StreamSink<Map<String, dynamic>> get sinkLoginRequest => loginRequest.sink;
Stream<Map<String, dynamic>> get streamLoginRequest => loginRequest.stream; Stream<Map<String, dynamic>> get streamLoginRequest => loginRequest.stream;
@@ -12,7 +11,5 @@ class LoginBloc extends BlocBase{
Stream<bool> get streamIsShowPassword => isShowPassword.stream; Stream<bool> get streamIsShowPassword => isShowPassword.stream;
@override @override
void dispose() { void dispose() {}
}
} }

View File

@@ -1,7 +1,12 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:developer';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../product/cache/local_manager.dart';
import '../product/constant/app/api_path_constant.dart';
import '../product/constant/enums/local_keys_enums.dart';
import '../product/network/network_manager.dart';
import '../product/base/bloc/base_bloc.dart'; import '../product/base/bloc/base_bloc.dart';
import '../product/services/api_services.dart'; import '../product/services/api_services.dart';
import '../feature/bell/bell_model.dart'; import '../feature/bell/bell_model.dart';
@@ -36,9 +41,34 @@ class MainBloc extends BlocBase {
@override @override
void dispose() {} void dispose() {}
void getUserProfile() async { void getUserProfile(BuildContext context) async {
String data = await apiServices.getUserDetail(); await apiServices.execute(context, () async {
User user = User.fromJson(jsonDecode(data)); User user = await apiServices.getUserDetail();
sinkUserProfile.add(user); sinkUserProfile.add(user);
});
}
getFCMTokenAndPresentations() async {
String? firebaseAppToken = await FirebaseMessaging.instance.getToken();
if (firebaseAppToken != null) {
log("FCM TOKEN: $firebaseAppToken");
sendNotificationToken(firebaseAppToken);
} else {
log("FCM TOKEN: null");
}
}
Future<int> sendNotificationToken(String token) async {
String uid = await getUID();
Map<String, dynamic> body = {"user_id": uid, "app_token": token};
int statusCode = await NetworkManager.instance!
.updateDataInServer(APIPathConstants.NOTIFICATION_TOKEN_PATH, body);
return statusCode;
}
Future<String> getUID() async {
String uid = LocaleManager.instance.getStringValue(PreferencesKeys.UID);
return uid;
} }
} }

View File

@@ -1,9 +1,9 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../product/services/map_services.dart'; import '../product/services/map_services.dart';
import '../product/shared/shared_snack_bar.dart'; import '../product/shared/shared_snack_bar.dart';
import '../feature/devices/device_model.dart'; import '../feature/devices/device_model.dart';
@@ -75,6 +75,4 @@ class MapBloc extends BlocBase {
context, "Không tìm thấy đường", Colors.orange, Colors.white); context, "Không tìm thấy đường", Colors.orange, Colors.white);
} }
} }
} }

View File

@@ -1,15 +1,17 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/cupertino.dart';
import '../product/services/api_services.dart';
import '../feature/settings/profile/profile_model.dart'; import '../feature/settings/profile/profile_model.dart';
import '../product/base/bloc/base_bloc.dart'; import '../product/base/bloc/base_bloc.dart';
class SettingsBloc extends BlocBase { class SettingsBloc extends BlocBase {
// Settings Screen // Settings Screen
APIServices apiServices = APIServices();
final userProfile = StreamController<User>.broadcast(); final userProfile = StreamController<User>.broadcast();
StreamSink<User> get sinkUserProfile => userProfile.sink; StreamSink<User> get sinkUserProfile => userProfile.sink;
Stream<User> get streamUserProfile => userProfile.stream; Stream<User> get streamUserProfile => userProfile.stream;
// Profile Screen // Profile Screen
final isChangeProfileInfomation = StreamController<bool>.broadcast(); final isChangeProfileInfomation = StreamController<bool>.broadcast();
StreamSink<bool> get sinkIsChangeProfileInfomation => StreamSink<bool> get sinkIsChangeProfileInfomation =>
@@ -17,9 +19,13 @@ class SettingsBloc extends BlocBase {
Stream<bool> get streamIsChangeProfileInfomation => Stream<bool> get streamIsChangeProfileInfomation =>
isChangeProfileInfomation.stream; isChangeProfileInfomation.stream;
void getUserProfile(BuildContext context) async {
await apiServices.execute(context, () async {
User user = await apiServices.getUserDetail();
sinkUserProfile.add(user);
});
}
@override @override
void dispose() { void dispose() {}
}
} }

View File

@@ -0,0 +1,34 @@
import 'dart:async';
import 'package:flutter/material.dart';
import '../product/services/api_services.dart';
import '../feature/devices/device_model.dart';
import '../product/base/bloc/base_bloc.dart';
class SimDataBloc extends BlocBase{
APIServices apiServices = APIServices();
final devices = StreamController<List<Device>>.broadcast();
StreamSink<List<Device>> get sinkDevices => devices.sink;
Stream<List<Device>> get streamDevices => devices.stream;
@override
void dispose() {}
void getOwnerDevices(BuildContext context) async {
await apiServices.execute(context, () async {
List<Device> devices = [];
devices = await apiServices.getOwnerDevices();
List<Device> publicDevices = [];
for (var device in devices) {
if (device.visibility == "PUBLIC") {
publicDevices.add(device);
}
}
sinkDevices.add(publicDevices);
});
}
}

View File

@@ -1,11 +1,12 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../product/extension/context_extension.dart';
import '../../product/services/language_services.dart'; import '../../product/services/language_services.dart';
import '../../bloc/bell_bloc.dart'; import '../../bloc/bell_bloc.dart';
import '../../product/base/bloc/base_bloc.dart'; import '../../product/base/bloc/base_bloc.dart';
import '../../product/services/api_services.dart'; import '../../product/services/api_services.dart';
import '../../product/shared/shared_component_loading_animation.dart';
import '../../product/shared/shared_loading_animation.dart';
import 'bell_model.dart'; import 'bell_model.dart';
class BellScreen extends StatefulWidget { class BellScreen extends StatefulWidget {
@@ -56,11 +57,7 @@ class _BellScreenState extends State<BellScreen> {
initialData: items, initialData: items,
builder: (context, bellSnapshot) { builder: (context, bellSnapshot) {
return check return check
? Center( ? const SharedLoadingAnimation()
child: CircularProgressIndicator(
value: context.highValue,
),
)
: bellSnapshot.data?.isEmpty ?? true : bellSnapshot.data?.isEmpty ?? true
? Center( ? Center(
child: Text( child: Text(
@@ -78,16 +75,8 @@ class _BellScreenState extends State<BellScreen> {
if (index < bellSnapshot.data!.length) { if (index < bellSnapshot.data!.length) {
return GestureDetector( return GestureDetector(
onTap: () async { onTap: () async {
List<String> read = []; readNotification(
read.add(bellSnapshot.data![index].id!); bellSnapshot.data![index].id!);
int code = await apiServices
.updateStatusOfNotification(read);
if (code == 200) {
read.clear();
} else {
read.clear();
}
refresh();
}, },
child: Column( child: Column(
children: [ children: [
@@ -143,7 +132,7 @@ class _BellScreenState extends State<BellScreen> {
builder: (context, hasMoreSnapshot) { builder: (context, hasMoreSnapshot) {
return Center( return Center(
child: hasMoreSnapshot.data ?? hasMore child: hasMoreSnapshot.data ?? hasMore
? const CircularProgressIndicator() ? const SharedComponentLoadingAnimation()
: Text( : Text(
appLocalization(context) appLocalization(context)
.bell_read_all, .bell_read_all,
@@ -173,10 +162,20 @@ class _BellScreenState extends State<BellScreen> {
getBellNotification(offset); getBellNotification(offset);
} }
void readNotification(String id) async {
await apiServices.execute(context, () async {
List<String> read = [];
read.add(id);
await apiServices.updateStatusOfNotification(read);
read.clear();
});
refresh();
}
Future<void> getBellNotification(int offset) async { Future<void> getBellNotification(int offset) async {
apiServices.execute(context, () async {
bell = await apiServices.getBellNotifications( bell = await apiServices.getBellNotifications(
offset.toString(), (offset + 20).toString()); offset.toString(), (offset + 20).toString());
if (bell.items!.isEmpty) { if (bell.items!.isEmpty) {
hasMore = false; hasMore = false;
bellBloc.sinkHasMore.add(hasMore); bellBloc.sinkHasMore.add(hasMore);
@@ -187,6 +186,7 @@ class _BellScreenState extends State<BellScreen> {
} }
bellBloc.bellItems.add(items); bellBloc.bellItems.add(items);
check = false; check = false;
});
} }
String timeAgo(BuildContext context, DateTime dateTime) { String timeAgo(BuildContext context, DateTime dateTime) {

View File

@@ -1,7 +1,10 @@
import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import '../../product/shared/shared_component_loading_animation.dart';
import '../../product/shared/shared_loading_animation.dart';
import 'widgets/tag_widget.dart'; import 'widgets/tag_widget.dart';
import '../devices/device_model.dart'; import '../devices/device_model.dart';
import '../../bloc/device_logs_bloc.dart'; import '../../bloc/device_logs_bloc.dart';
@@ -12,7 +15,6 @@ import '../../product/services/language_services.dart';
import '../../product/shared/shared_snack_bar.dart'; import '../../product/shared/shared_snack_bar.dart';
import '../../product/utils/date_time_utils.dart'; import '../../product/utils/date_time_utils.dart';
import '../../product/utils/device_utils.dart'; import '../../product/utils/device_utils.dart';
import '../../product/base/bloc/base_bloc.dart'; import '../../product/base/bloc/base_bloc.dart';
import 'device_logs_model.dart'; import 'device_logs_model.dart';
@@ -42,7 +44,7 @@ class _DeviceLogsScreenState extends State<DeviceLogsScreen> {
() { () {
if (controller.position.maxScrollExtent == controller.offset) { if (controller.position.maxScrollExtent == controller.offset) {
offset += 30; offset += 30;
deviceLogsBloc.getDeviceLogByThingID( deviceLogsBloc.getDeviceLogByThingID(context,
offset, thingID, dateTime!, sensors); offset, thingID, dateTime!, sensors);
} }
}, },
@@ -63,11 +65,9 @@ class _DeviceLogsScreenState extends State<DeviceLogsScreen> {
body: StreamBuilder<List<Device>>( body: StreamBuilder<List<Device>>(
stream: deviceLogsBloc.streamAllDevices, stream: deviceLogsBloc.streamAllDevices,
builder: (context, allDevicesSnapshot) { builder: (context, allDevicesSnapshot) {
if (allDevicesSnapshot.data?[0].thingId == null) { if (allDevicesSnapshot.data == null) {
deviceLogsBloc.getAllDevices(); deviceLogsBloc.getAllDevices(context);
return const Center( return const SharedLoadingAnimation();
child: CircularProgressIndicator(),
);
} else { } else {
return StreamBuilder<List<SensorLogs>>( return StreamBuilder<List<SensorLogs>>(
stream: deviceLogsBloc.streamSensors, stream: deviceLogsBloc.streamSensors,
@@ -90,9 +90,7 @@ class _DeviceLogsScreenState extends State<DeviceLogsScreen> {
hint: Text( hint: Text(
appLocalization(context) appLocalization(context)
.choose_device_dropdownButton, .choose_device_dropdownButton,
style: const TextStyle( style: context.responsiveBodySmall
fontSize: 14,
),
), ),
items: allDevicesSnapshot.data?.isNotEmpty ?? false items: allDevicesSnapshot.data?.isNotEmpty ?? false
? allDevicesSnapshot.data! ? allDevicesSnapshot.data!
@@ -101,9 +99,7 @@ class _DeviceLogsScreenState extends State<DeviceLogsScreen> {
value: device.thingId, value: device.thingId,
child: Text( child: Text(
device.name!, device.name!,
style: const TextStyle( style: context.responsiveBodySmall
fontSize: 14,
),
), ),
), ),
) )
@@ -192,6 +188,7 @@ class _DeviceLogsScreenState extends State<DeviceLogsScreen> {
); );
} else { } else {
deviceLogsBloc.getDeviceLogByThingID( deviceLogsBloc.getDeviceLogByThingID(
context,
offset, offset,
thingID, thingID,
dateTime!, dateTime!,
@@ -250,7 +247,7 @@ class _DeviceLogsScreenState extends State<DeviceLogsScreen> {
return Center( return Center(
child: hasMoreSnapshot.data ?? child: hasMoreSnapshot.data ??
hasMore hasMore
? const CircularProgressIndicator() ? const SharedComponentLoadingAnimation()
: Text( : Text(
appLocalization(context) appLocalization(context)
.main_no_data), .main_no_data),
@@ -314,7 +311,7 @@ class _DeviceLogsScreenState extends State<DeviceLogsScreen> {
deviceLogsBloc.sensors.add(sensors); deviceLogsBloc.sensors.add(sensors);
hasMore = true; hasMore = true;
deviceLogsBloc.sinkHasMore.add(hasMore); deviceLogsBloc.sinkHasMore.add(hasMore);
deviceLogsBloc.getDeviceLogByThingID(offset, thingID, dateTime!, sensors); deviceLogsBloc.getDeviceLogByThingID(context,offset, thingID, dateTime!, sensors);
} }
Widget leadingList(SensorLogs sensor) { Widget leadingList(SensorLogs sensor) {

View File

@@ -1,7 +1,9 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sfm_app/product/shared/shared_snack_bar.dart';
import '../../bloc/devices_manager_bloc.dart';
import '../../product/shared/shared_snack_bar.dart';
import '../../product/utils/response_status_utils.dart'; import '../../product/utils/response_status_utils.dart';
import '../../product/constant/enums/role_enums.dart'; import '../../product/constant/enums/role_enums.dart';
import '../../product/services/api_services.dart'; import '../../product/services/api_services.dart';
@@ -10,7 +12,8 @@ import '../../product/constant/icon/icon_constants.dart';
import '../../product/extension/context_extension.dart'; import '../../product/extension/context_extension.dart';
import '../../product/services/language_services.dart'; import '../../product/services/language_services.dart';
addNewDevice(BuildContext context, String role) async { addNewDevice(BuildContext context, String role,
DevicesManagerBloc deviceManagerBloc) async {
TextEditingController extIDController = TextEditingController(text: ""); TextEditingController extIDController = TextEditingController(text: "");
TextEditingController deviceNameController = TextEditingController(text: ""); TextEditingController deviceNameController = TextEditingController(text: "");
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
@@ -25,8 +28,7 @@ addNewDevice(BuildContext context, String role) async {
children: [ children: [
Text( Text(
'${appLocalization(context).add_device_title}: ', '${appLocalization(context).add_device_title}: ',
style: style: context.responsiveBodyLargeWithBold,
const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
), ),
Container( Container(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
@@ -77,7 +79,8 @@ addNewDevice(BuildContext context, String role) async {
Colors.white); Colors.white);
ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).hideCurrentSnackBar();
} else { } else {
addDevices(context, role, extID, deviceName); addDevices(
context, role, extID, deviceName, deviceManagerBloc);
ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).hideCurrentSnackBar();
} }
}, },
@@ -90,11 +93,12 @@ addNewDevice(BuildContext context, String role) async {
); );
} }
void addDevices( void addDevices(BuildContext context, String role, String extID,
BuildContext context, String role, String extID, String deviceName) async { String deviceName, DevicesManagerBloc deviceManagerBloc) async {
APIServices apiServices = APIServices(); APIServices apiServices = APIServices();
Map<String, dynamic> body = {}; Map<String, dynamic> body = {};
if (role == RoleEnums.ADMIN.name) { if (role == RoleEnums.ADMIN.name) {
await apiServices.execute(context,() async {
body = {"ext_id": extID, "name": deviceName}; body = {"ext_id": extID, "name": deviceName};
int statusCode = await apiServices.createDeviceByAdmin(body); int statusCode = await apiServices.createDeviceByAdmin(body);
showSnackBarResponseByStatusCode( showSnackBarResponseByStatusCode(
@@ -102,7 +106,10 @@ void addDevices(
statusCode, statusCode,
appLocalization(context).notification_create_device_success, appLocalization(context).notification_create_device_success,
appLocalization(context).notification_create_device_failed); appLocalization(context).notification_create_device_failed);
deviceManagerBloc.getDeviceByState(context, -2);
});
} else { } else {
await apiServices.execute(context,() async {
body = {"ext_id": extID}; body = {"ext_id": extID};
int statusCode = await apiServices.registerDevice(body); int statusCode = await apiServices.registerDevice(body);
showSnackBarResponseByStatusCode( showSnackBarResponseByStatusCode(
@@ -110,5 +117,7 @@ void addDevices(
statusCode, statusCode,
appLocalization(context).notification_add_device_success, appLocalization(context).notification_add_device_success,
appLocalization(context).notification_device_not_exist); appLocalization(context).notification_device_not_exist);
deviceManagerBloc.getDeviceByState(context, -2);
});
} }
} }

View File

@@ -1,6 +1,7 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../bloc/devices_manager_bloc.dart'; import '../../bloc/devices_manager_bloc.dart';
import '../../product/constant/enums/role_enums.dart'; import '../../product/constant/enums/role_enums.dart';
import '../../product/services/api_services.dart'; import '../../product/services/api_services.dart';
@@ -47,20 +48,24 @@ deleteOrUnregisterDevice(BuildContext context, DevicesManagerBloc devicesBloc,
Map<String, dynamic> body = { Map<String, dynamic> body = {
"ext_id": extID, "ext_id": extID,
}; };
await apiServices.execute(context, () async {
int statusCode = await apiServices.unregisterDevice(body); int statusCode = await apiServices.unregisterDevice(body);
showSnackBarResponseByStatusCode( showSnackBarResponseByStatusCode(
context, context,
statusCode, statusCode,
appLocalization(context).notification_delete_device_success, appLocalization(context).notification_delete_device_success,
appLocalization(context).notification_delete_device_failed); appLocalization(context).notification_delete_device_failed);
devicesBloc.getDeviceByState(-2); devicesBloc.getDeviceByState(context, -2);
});
} else { } else {
await apiServices.execute(context, () async {
int statusCode = await apiServices.deleteDeviceByAdmin(extID); int statusCode = await apiServices.deleteDeviceByAdmin(extID);
showSnackBarResponseByStatusCode( showSnackBarResponseByStatusCode(
context, context,
statusCode, statusCode,
appLocalization(context).notification_delete_device_success, appLocalization(context).notification_delete_device_success,
appLocalization(context).notification_delete_device_failed); appLocalization(context).notification_delete_device_failed);
devicesBloc.getDeviceByState(-2); devicesBloc.getDeviceByState(context, -2);
});
} }
} }

View File

@@ -1,18 +1,19 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:simple_ripple_animation/simple_ripple_animation.dart'; import 'package:simple_ripple_animation/simple_ripple_animation.dart';
import 'package:sfm_app/feature/device_log/device_logs_model.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../../../product/shared/shared_component_loading_animation.dart';
import '../../../product/constant/image/image_constants.dart'; import '../../../product/constant/image/image_constants.dart';
import '../../../product/shared/shared_line_chart.dart'; import '../../../product/shared/shared_line_chart.dart';
import '../../../product/shared/shared_curve.dart'; import '../../../product/shared/shared_curve.dart';
import '../../../product/shared/shared_loading_animation.dart';
import '../../device_log/device_logs_model.dart';
import '../device_model.dart'; import '../device_model.dart';
import '../../../product/base/bloc/base_bloc.dart'; import '../../../product/base/bloc/base_bloc.dart';
import '../../../product/extension/context_extension.dart'; import '../../../product/extension/context_extension.dart';
import '../../../product/services/language_services.dart'; import '../../../product/services/language_services.dart';
import '../../../product/utils/device_utils.dart'; import '../../../product/utils/device_utils.dart';
import '../../../product/constant/icon/icon_constants.dart'; import '../../../product/constant/icon/icon_constants.dart';
import '../../../bloc/device_detail_bloc.dart'; import '../../../bloc/device_detail_bloc.dart';
@@ -46,8 +47,8 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
late DetailDeviceBloc detailDeviceBloc; late DetailDeviceBloc detailDeviceBloc;
Completer<GoogleMapController> controller = Completer(); Completer<GoogleMapController> controller = Completer();
CameraPosition initialCamera = CameraPosition initialCamera = const CameraPosition(
const CameraPosition(target: LatLng(20.966048511844402, 105.74977710843086), zoom: 15); target: LatLng(20.966048511844402, 105.74977710843086), zoom: 15);
Timer? getDeviceDetailTimer; Timer? getDeviceDetailTimer;
@override @override
@@ -68,6 +69,7 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
@override @override
void dispose() { void dispose() {
getDeviceDetailTimer?.cancel(); getDeviceDetailTimer?.cancel();
super.dispose();
} }
BoxDecoration boxDecoration = BoxDecoration( BoxDecoration boxDecoration = BoxDecoration(
@@ -84,16 +86,6 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
return StreamBuilder<Device>( return StreamBuilder<Device>(
stream: detailDeviceBloc.streamDeviceInfo, stream: detailDeviceBloc.streamDeviceInfo,
builder: (context, deviceSnapshot) { builder: (context, deviceSnapshot) {
if (deviceSnapshot.data?.extId == null) {
detailDeviceBloc.getDeviceDetail(
context,
widget.thingID,
controller,
);
return const Center(
child: CircularProgressIndicator(),
);
} else {
return StreamBuilder<Map<String, dynamic>>( return StreamBuilder<Map<String, dynamic>>(
stream: detailDeviceBloc.streamDeviceSensor, stream: detailDeviceBloc.streamDeviceSensor,
builder: (context, sensorSnapshot) { builder: (context, sensorSnapshot) {
@@ -118,32 +110,11 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
children: [ children: [
Positioned.fill( Positioned.fill(
child: Image.asset( child: Image.asset(
ImageConstants.instance.getImage('smoke-detector'), ImageConstants.instance
.getImage('smoke-detector'),
fit: BoxFit.fill, fit: BoxFit.fill,
), ),
), ),
// Center(
// child: Container(
// height: 50,
// width: 400,
// // color: Colors.blueAccent,
// alignment: Alignment.centerRight,
// margin: const EdgeInsets.fromLTRB(0, 0, 0, 50),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// const SizedBox(),
// Text(
// deviceSnapshot.data?.name ?? "",
// style: const TextStyle(
// fontSize: 25,
// fontWeight: FontWeight.w600,
// ),
// ),
// ],
// ),
// ),
// ),
], ],
), ),
), ),
@@ -156,13 +127,14 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
height: context.dynamicHeight(0.08), height: context.dynamicHeight(0.08),
width: context.dynamicWidth(0.5), width: context.dynamicWidth(0.5),
decoration: BoxDecoration( decoration: BoxDecoration(
color: DeviceUtils.instance color: DeviceUtils.instance.getTableRowColor(
.getTableRowColor(context,deviceSnapshot.data?.state ?? 3), context, deviceSnapshot.data?.state ?? 3),
borderRadius: BorderRadius.circular(50), borderRadius: BorderRadius.circular(50),
), ),
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [ children: [
SizedBox( SizedBox(
height: context.mediumValue, height: context.mediumValue,
@@ -170,17 +142,20 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
child: deviceSnapshot.data?.state == 1 child: deviceSnapshot.data?.state == 1
? RippleAnimation( ? RippleAnimation(
color: Colors.red, color: Colors.red,
delay: context.dynamicMilliSecondDuration( delay: context
.dynamicMilliSecondDuration(
800, 800,
), ),
repeat: true, repeat: true,
minRadius: 10, minRadius: 10,
ripplesCount: 5, ripplesCount: 5,
duration: context.dynamicMilliSecondDuration( duration: context
.dynamicMilliSecondDuration(
1800, 1800,
), ),
child: CircleAvatar( child: CircleAvatar(
backgroundColor: Colors.transparent, backgroundColor:
Colors.transparent,
minRadius: context.mediumValue, minRadius: context.mediumValue,
maxRadius: context.mediumValue, maxRadius: context.mediumValue,
backgroundImage: AssetImage( backgroundImage: AssetImage(
@@ -191,8 +166,10 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
), ),
) )
: CircleAvatar( : CircleAvatar(
backgroundColor: backgroundColor: DeviceUtils
DeviceUtils.instance.getTableRowColor(context, .instance
.getTableRowColor(
context,
deviceSnapshot.data?.state ?? 3, deviceSnapshot.data?.state ?? 3,
), ),
minRadius: context.mediumValue, minRadius: context.mediumValue,
@@ -237,19 +214,18 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
child: Padding( child: Padding(
padding: context.paddingLow, padding: context.paddingLow,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
appLocalization(context) appLocalization(context)
.paginated_data_table_column_deviceSignal, .paginated_data_table_column_deviceSignal,
style: const TextStyle( style: context
fontSize: 18, .responsiveBodyLargeWithBold),
fontWeight: FontWeight.bold,
),
),
SizedBox( SizedBox(
height: context.dynamicWidth(0.12), height: context.dynamicWidth(0.12),
width: context.dynamicWidth(0.12), width: context.dynamicWidth(0.12),
@@ -259,7 +235,8 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
sensorSnapshot.data!['sensorCsq'], sensorSnapshot.data!['sensorCsq'],
), ),
size: 30, size: 30,
color: DeviceUtils.instance.getSignalIconColor( color: DeviceUtils.instance
.getSignalIconColor(
context, context,
sensorSnapshot.data!['sensorCsq'], sensorSnapshot.data!['sensorCsq'],
), ),
@@ -268,8 +245,10 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
], ],
), ),
Row( Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment:
crossAxisAlignment: CrossAxisAlignment.center, MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Container( Container(
height: context.dynamicHeight(0.09), height: context.dynamicHeight(0.09),
@@ -277,9 +256,11 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
child: Text( child: Text(
sensorSnapshot.data!['sensorCsq'], sensorSnapshot.data!['sensorCsq'],
style: TextStyle( style: TextStyle(
color: DeviceUtils.instance.getSignalIconColor( color: DeviceUtils.instance
.getSignalIconColor(
context, context,
sensorSnapshot.data!['sensorCsq'], sensorSnapshot
.data!['sensorCsq'],
), ),
fontSize: 40, fontSize: 40,
fontWeight: FontWeight.w900, fontWeight: FontWeight.w900,
@@ -299,10 +280,12 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
child: Padding( child: Padding(
padding: context.paddingLow, padding: context.paddingLow,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
appLocalization(context) appLocalization(context)
@@ -316,14 +299,18 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
height: context.dynamicWidth(0.12), height: context.dynamicWidth(0.12),
width: context.dynamicWidth(0.12), width: context.dynamicWidth(0.12),
child: Image.asset( child: Image.asset(
DeviceUtils.instance.getDeviceBatteryImg( DeviceUtils.instance
.getDeviceBatteryImg(
int.parse( int.parse(
sensorSnapshot.data!['sensorBattery'], sensorSnapshot
.data!['sensorBattery'],
), ),
), ),
color: DeviceUtils.instance.getDeviceBatteryColor( color: DeviceUtils.instance
.getDeviceBatteryColor(
int.parse( int.parse(
sensorSnapshot.data!['sensorBattery'], sensorSnapshot
.data!['sensorBattery'],
), ),
), ),
), ),
@@ -331,18 +318,23 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
], ],
), ),
Row( Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment:
crossAxisAlignment: CrossAxisAlignment.center, MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Container( Container(
height: context.dynamicHeight(0.09), height: context.dynamicHeight(0.09),
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
sensorSnapshot.data!['sensorBattery'], sensorSnapshot
.data!['sensorBattery'],
style: TextStyle( style: TextStyle(
color: DeviceUtils.instance.getDeviceBatteryColor( color: DeviceUtils.instance
.getDeviceBatteryColor(
int.parse( int.parse(
sensorSnapshot.data!['sensorBattery'], sensorSnapshot
.data!['sensorBattery'],
), ),
), ),
fontSize: 50, fontSize: 50,
@@ -360,9 +352,11 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
child: Text( child: Text(
'%', '%',
style: TextStyle( style: TextStyle(
color: DeviceUtils.instance.getDeviceBatteryColor( color: DeviceUtils.instance
.getDeviceBatteryColor(
int.parse( int.parse(
sensorSnapshot.data!['sensorBattery'], sensorSnapshot
.data!['sensorBattery'],
), ),
), ),
fontSize: 30, fontSize: 30,
@@ -390,7 +384,8 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
child: Column( child: Column(
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
appLocalization(context) appLocalization(context)
@@ -405,9 +400,11 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
width: context.dynamicWidth(0.12), width: context.dynamicWidth(0.12),
child: Image.asset( child: Image.asset(
'assets/icons/temperature.png', 'assets/icons/temperature.png',
color: DeviceUtils.instance.getDeviceTempColor( color: DeviceUtils.instance
.getDeviceTempColor(
int.parse( int.parse(
sensorSnapshot.data!['sensorTemp'], sensorSnapshot
.data!['sensorTemp'],
), ),
), ),
), ),
@@ -423,20 +420,28 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
width: double.infinity, width: double.infinity,
height: 20, height: 20,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.grey.withValues(alpha: 0.3), color: Colors.grey
borderRadius: BorderRadius.circular(10), .withValues(alpha: 0.3),
borderRadius:
BorderRadius.circular(10),
), ),
), ),
LayoutBuilder( LayoutBuilder(
builder: (context, constraints) => Container( builder: (context, constraints) =>
Container(
width: constraints.maxWidth * width: constraints.maxWidth *
(int.parse(sensorSnapshot.data!['sensorTemp']) / 75), (int.parse(sensorSnapshot
.data!['sensorTemp']) /
75),
height: 20, height: 20,
decoration: BoxDecoration( decoration: BoxDecoration(
color: DeviceUtils.instance.getDeviceTempColor( color: DeviceUtils.instance
int.parse(sensorSnapshot.data!['sensorTemp']), .getDeviceTempColor(
int.parse(sensorSnapshot
.data!['sensorTemp']),
), ),
borderRadius: BorderRadius.circular(10), borderRadius:
BorderRadius.circular(10),
), ),
), ),
) )
@@ -446,25 +451,26 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
height: 5, height: 5,
), ),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
"${sensorSnapshot.data!['sensorTemp']} °C", "${sensorSnapshot.data!['sensorTemp']} °C",
style: TextStyle( style: TextStyle(
color: DeviceUtils.instance.getDeviceTempColor( color: DeviceUtils.instance
.getDeviceTempColor(
int.parse( int.parse(
sensorSnapshot.data!['sensorTemp'], sensorSnapshot
.data!['sensorTemp'],
), ),
), ),
fontSize: 30, fontSize: 30,
fontWeight: FontWeight.w900, fontWeight: FontWeight.w900,
), ),
), ),
const Text( Text(
"75 °C", "75 °C",
style: TextStyle( style: context.responsiveBodyLarge
fontSize: 20,
),
), ),
], ],
) )
@@ -480,11 +486,9 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
appLocalization(context).paginated_data_table_column_devicePower, appLocalization(context)
style: const TextStyle( .paginated_data_table_column_devicePower,
fontSize: 18, style: context.responsiveBodyLargeWithBold
fontWeight: FontWeight.bold,
),
), ),
SizedBox( SizedBox(
height: context.dynamicWidth(0.12), height: context.dynamicWidth(0.12),
@@ -504,12 +508,11 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
stream: detailDeviceBloc.streamSensorTemps, stream: detailDeviceBloc.streamSensorTemps,
builder: (context, sensorTempsSnapshot) { builder: (context, sensorTempsSnapshot) {
if (sensorTempsSnapshot.data == null) { if (sensorTempsSnapshot.data == null) {
detailDeviceBloc.getNearerSensorValue(widget.thingID); detailDeviceBloc
.getNearerSensorValue(context,widget.thingID);
return const AspectRatio( return const AspectRatio(
aspectRatio: 3, aspectRatio: 3,
child: Center( child: SharedComponentLoadingAnimation(),
child: CircularProgressIndicator(),
),
); );
} else if (sensorTempsSnapshot.data!.isEmpty) { } else if (sensorTempsSnapshot.data!.isEmpty) {
return Center( return Center(
@@ -523,7 +526,8 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
child: Container( child: Container(
margin: context.paddingLow, margin: context.paddingLow,
child: sharedLineChart( child: sharedLineChart(
appLocalization(context).detail_device_volt_message, appLocalization(context)
.detail_device_volt_message,
sensorTempsSnapshot.data ?? [], sensorTempsSnapshot.data ?? [],
), ),
), ),
@@ -550,33 +554,48 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
Radius.circular(15), Radius.circular(15),
), ),
), ),
child: deviceSnapshot.data!.settings!.latitude != "" child: deviceSnapshot
.data!.settings!.latitude !=
""
? StreamBuilder<String>( ? StreamBuilder<String>(
stream: detailDeviceBloc.streamDeviceLocation, stream: detailDeviceBloc
.streamDeviceLocation,
builder: (context, locationSnapshot) { builder: (context, locationSnapshot) {
if (locationSnapshot.data == null) { if (locationSnapshot.data == null) {
detailDeviceBloc.findLocation( detailDeviceBloc.findLocation(
context, deviceSnapshot.data!.areaPath!); context,
deviceSnapshot
.data!.areaPath!);
} }
return GoogleMap( return GoogleMap(
initialCameraPosition: initialCamera, initialCameraPosition:
initialCamera,
mapType: MapType.normal, mapType: MapType.normal,
markers: { markers: {
Marker( Marker(
infoWindow: InfoWindow( infoWindow: InfoWindow(
title: locationSnapshot.data ?? "", title:
locationSnapshot.data ??
"",
), ),
markerId: MarkerId(deviceSnapshot.data!.thingId!), markerId: MarkerId(
deviceSnapshot
.data!.thingId!),
position: LatLng( position: LatLng(
double.parse( double.parse(deviceSnapshot
deviceSnapshot.data!.settings!.latitude!), .data!
double.parse( .settings!
deviceSnapshot.data!.settings!.longitude!), .latitude!),
double.parse(deviceSnapshot
.data!
.settings!
.longitude!),
), ),
), ),
}, },
onMapCreated: (mapcontroller) { onMapCreated: (mapcontroller) {
controller.complete(mapcontroller); controller
.complete(mapcontroller);
}, },
mapToolbarEnabled: false, mapToolbarEnabled: false,
zoomControlsEnabled: false, zoomControlsEnabled: false,
@@ -591,11 +610,9 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
), ),
), ),
Text( Text(
appLocalization(context).device_update_location, appLocalization(context)
style: const TextStyle( .device_update_location,
fontSize: 18, style: context.responsiveBodyLargeWithBold
fontWeight: FontWeight.bold,
),
) )
], ],
), ),
@@ -605,18 +622,26 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
), ),
), ),
); );
} else { }
else {
detailDeviceBloc.getDeviceDetail(
context,
widget.thingID,
controller,
);
return Scaffold( return Scaffold(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
appBar: AppBar(), appBar: AppBar(),
body: const Center( body: const Center(
child: CircularProgressIndicator(), child: SharedLoadingAnimation(),
), ),
); );
} }
}, },
); );
} }
},
); );
} }
} }

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:search_choices/search_choices.dart'; import 'package:search_choices/search_choices.dart';
import '../../../product/shared/shared_loading_animation.dart';
import '../device_model.dart'; import '../device_model.dart';
import '../../../bloc/device_update_bloc.dart'; import '../../../bloc/device_update_bloc.dart';
import 'map_dialog.dart'; import 'map_dialog.dart';
@@ -8,7 +9,6 @@ import '../../../product/base/bloc/base_bloc.dart';
import '../../../product/extension/context_extension.dart'; import '../../../product/extension/context_extension.dart';
import '../../../product/services/api_services.dart'; import '../../../product/services/api_services.dart';
import '../../../product/services/language_services.dart'; import '../../../product/services/language_services.dart';
import '../../../product/shared/model/district_model.dart'; import '../../../product/shared/model/district_model.dart';
import '../../../product/shared/model/province_model.dart'; import '../../../product/shared/model/province_model.dart';
import '../../../product/shared/model/ward_model.dart'; import '../../../product/shared/model/ward_model.dart';
@@ -47,7 +47,7 @@ class _DeviceUpdateScreenState extends State<DeviceUpdateScreen> {
void initState() { void initState() {
super.initState(); super.initState();
deviceUpdateBloc = BlocProvider.of(context); deviceUpdateBloc = BlocProvider.of(context);
deviceUpdateBloc.getAllProvinces(); deviceUpdateBloc.getAllProvinces(context);
} }
@override @override
@@ -72,16 +72,16 @@ class _DeviceUpdateScreenState extends State<DeviceUpdateScreen> {
initialData: device, initialData: device,
builder: (context, deviceInfoSnapshot) { builder: (context, deviceInfoSnapshot) {
if (deviceInfoSnapshot.data!.thingId == null) { if (deviceInfoSnapshot.data!.thingId == null) {
deviceUpdateBloc.getDeviceInfomation( deviceUpdateBloc.getDeviceInformation(
context,
widget.thingID, widget.thingID,
districtsData, // provincesData,
wardsData, // districtsData,
// wardsData,
deviceNameController, deviceNameController,
deviceLatitudeController, deviceLatitudeController,
deviceLongitudeController); deviceLongitudeController);
return const Center( return const SharedLoadingAnimation();
child: CircularProgressIndicator(),
);
} else { } else {
return StreamBuilder<bool>( return StreamBuilder<bool>(
stream: deviceUpdateBloc.streamIsChanged, stream: deviceUpdateBloc.streamIsChanged,
@@ -245,7 +245,7 @@ class _DeviceUpdateScreenState extends State<DeviceUpdateScreen> {
.sinkProvinceData .sinkProvinceData
.add(provinceData); .add(provinceData);
deviceUpdateBloc deviceUpdateBloc
.getAllDistricts( .getAllDistricts(context,
value.code); value.code);
selectedDistrict = ""; selectedDistrict = "";
districtData['name'] = districtData['name'] =
@@ -318,7 +318,7 @@ class _DeviceUpdateScreenState extends State<DeviceUpdateScreen> {
.sinkDistrictData .sinkDistrictData
.add(districtData); .add(districtData);
deviceUpdateBloc deviceUpdateBloc
.getAllWards(value.code); .getAllWards(context,value.code);
selectedWard = ""; selectedWard = "";
wardData['name'] = wardData['name'] =
selectedWard!; selectedWard!;

View File

@@ -2,15 +2,14 @@ import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../../../bloc/device_update_bloc.dart'; import '../../../bloc/device_update_bloc.dart';
import '../../../product/constant/app/app_constants.dart'; import '../../../product/constant/app/app_constants.dart';
import '../../../product/extension/context_extension.dart'; import '../../../product/extension/context_extension.dart';
import '../../../product/services/language_services.dart'; import '../../../product/services/language_services.dart';
import '../../../product/shared/find_location_maps/shared_map_search_location.dart'; import '../../../product/shared/find_location_maps/shared_map_search_location.dart';
import '../../../product/shared/find_location_maps/model/prediction_model.dart'; import '../../../product/shared/find_location_maps/model/prediction_model.dart';
import '../../../product/shared/shared_transition.dart'; import '../../../product/shared/shared_transition.dart';
import 'geocode_model.dart'; import 'geocode_model.dart';
@@ -64,7 +63,8 @@ showMapDialog(
String latitude = mapDialogLatitudeController.text; String latitude = mapDialogLatitudeController.text;
String longitude = mapDialogLongitudeController.text; String longitude = mapDialogLongitudeController.text;
log("Finish -- Latitude: $latitude, longitude: $longitude --"); log("Finish -- Latitude: $latitude, longitude: $longitude --");
getDataFromApi(latitude, longitude, deviceUpdateBloc); getDataFromApi(
context, latitude, longitude, deviceUpdateBloc);
latitudeController.text = latitudeController.text =
mapDialogLatitudeController.text; mapDialogLatitudeController.text;
longitudeController.text = longitudeController.text =
@@ -184,7 +184,7 @@ addMarker(
updateCameraPosition(position, 14, mapController); updateCameraPosition(position, 14, mapController);
} }
void getDataFromApi(String latitude, String longitude, void getDataFromApi(BuildContext context, String latitude, String longitude,
DeviceUpdateBloc deviceUpdateBloc) async { DeviceUpdateBloc deviceUpdateBloc) async {
String path = String path =
"maps/api/geocode/json?latlng=$latitude,$longitude&language=vi&result_type=political&key=${ApplicationConstants.MAP_KEY}"; "maps/api/geocode/json?latlng=$latitude,$longitude&language=vi&result_type=political&key=${ApplicationConstants.MAP_KEY}";
@@ -215,7 +215,7 @@ void getDataFromApi(String latitude, String longitude,
log("$key: $value"); log("$key: $value");
}); });
await _processLocations(locations, deviceUpdateBloc); await _processLocations(context, locations, deviceUpdateBloc);
} }
Map<String, String> _extractLocationComponents( Map<String, String> _extractLocationComponents(
@@ -241,31 +241,31 @@ Map<String, String> _extractLocationComponents(
return locations; return locations;
} }
Future<void> _processLocations( Future<void> _processLocations(BuildContext context,
Map<String, String> locations, DeviceUpdateBloc deviceUpdateBloc) async { Map<String, String> locations, DeviceUpdateBloc deviceUpdateBloc) async {
String provinceNameFromAPI = locations['provincekey'] ?? ""; String provinceNameFromAPI = locations['provincekey'] ?? "";
String districtNameFromAPI = locations['districtkey'] ?? ""; String districtNameFromAPI = locations['districtkey'] ?? "";
String wardNameFromAPI = locations['wardkey'] ?? ""; String wardNameFromAPI = locations['wardkey'] ?? "";
final province = final province =
await deviceUpdateBloc.getProvinceByName(provinceNameFromAPI); await deviceUpdateBloc.getProvinceByName(context, provinceNameFromAPI);
if (province.name != "null") { if (province.name != "null") {
log("Province: ${province.fullName}, ProvinceCode: ${province.code}"); log("Province: ${province.fullName}, ProvinceCode: ${province.code}");
deviceUpdateBloc.sinkProvinceData deviceUpdateBloc.sinkProvinceData
.add({"code": province.code!, "name": province.fullName!}); .add({"code": province.code!, "name": province.fullName!});
deviceUpdateBloc.getAllProvinces(); deviceUpdateBloc.getAllProvinces(context);
final district = await deviceUpdateBloc.getDistrictByName( final district = await deviceUpdateBloc.getDistrictByName(
districtNameFromAPI, province.code!); context, districtNameFromAPI, province.code!);
log("Districtname: ${district.fullName}, districtCode: ${district.code}"); log("Districtname: ${district.fullName}, districtCode: ${district.code}");
deviceUpdateBloc.getAllDistricts(province.code!); deviceUpdateBloc.getAllDistricts(context, province.code!);
if (district.name != "null") { if (district.name != "null") {
deviceUpdateBloc.sinkDistrictData deviceUpdateBloc.sinkDistrictData
.add({"code": district.code!, "name": district.fullName!}); .add({"code": district.code!, "name": district.fullName!});
final ward = final ward = await deviceUpdateBloc.getWardByName(
await deviceUpdateBloc.getWardByName(wardNameFromAPI, district.code!); context, wardNameFromAPI, district.code!);
log("Wardname: ${ward.fullName}, WardCode: ${ward.code}"); log("Wardname: ${ward.fullName}, WardCode: ${ward.code}");
deviceUpdateBloc.getAllWards(district.code!); deviceUpdateBloc.getAllWards(context, district.code!);
if (ward.name != "null") { if (ward.name != "null") {
log("Xac dinh duoc het thong tin tu toa do"); log("Xac dinh duoc het thong tin tu toa do");
deviceUpdateBloc.sinkWardData deviceUpdateBloc.sinkWardData

View File

@@ -1,9 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'package:data_table_2/data_table_2.dart'; import 'package:data_table_2/data_table_2.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import '../../product/shared/shared_component_loading_animation.dart';
import '../../product/shared/shared_loading_animation.dart';
import 'add_new_device_widget.dart'; import 'add_new_device_widget.dart';
import 'delete_device_widget.dart'; import 'delete_device_widget.dart';
import 'device_model.dart'; import 'device_model.dart';
@@ -29,7 +31,7 @@ class _DevicesManagerScreenState extends State<DevicesManagerScreen> {
late DevicesManagerBloc devicesManagerBloc; late DevicesManagerBloc devicesManagerBloc;
String role = "Undefine"; String role = "Undefine";
APIServices apiServices = APIServices(); APIServices apiServices = APIServices();
List<Device> devices = []; // List<Device> devices = [];
Timer? getAllDevicesTimer; Timer? getAllDevicesTimer;
List<Widget> tags = []; List<Widget> tags = [];
int tagIndex = -2; int tagIndex = -2;
@@ -41,7 +43,7 @@ class _DevicesManagerScreenState extends State<DevicesManagerScreen> {
const duration = Duration(seconds: 10); const duration = Duration(seconds: 10);
getAllDevicesTimer = Timer.periodic( getAllDevicesTimer = Timer.periodic(
duration, duration,
(Timer t) => devicesManagerBloc.getDeviceByState(tagIndex), (Timer t) => devicesManagerBloc.getDeviceByState(context,tagIndex),
); );
} }
@@ -58,15 +60,43 @@ class _DevicesManagerScreenState extends State<DevicesManagerScreen> {
body: StreamBuilder<List<int>>( body: StreamBuilder<List<int>>(
stream: devicesManagerBloc.streamTagStates, stream: devicesManagerBloc.streamTagStates,
builder: (context, tagSnapshot) { builder: (context, tagSnapshot) {
return StreamBuilder<String>(
stream: devicesManagerBloc.streamUserRole,
initialData: role,
builder: (context, roleSnapshot) {
return SafeArea( return SafeArea(
child: StreamBuilder<List<Device>>( child: StreamBuilder<List<Device>>(
stream: devicesManagerBloc.streamAllDevices, stream: devicesManagerBloc.streamAllDevices,
initialData: devices,
builder: (context, allDeviceSnapshot) { builder: (context, allDeviceSnapshot) {
if (allDeviceSnapshot.data?.isEmpty ?? devices.isEmpty) { if(allDeviceSnapshot.data == null){
devicesManagerBloc devicesManagerBloc
.getDeviceByState(tagSnapshot.data?[0] ?? -2); .getDeviceByState(context,tagSnapshot.data?[0] ?? -2);
return const Center(child: CircularProgressIndicator()); return const SharedLoadingAnimation();
}
if (allDeviceSnapshot.data!.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width: 100,
height: 100,
decoration: BoxDecoration(
border: Border.all(color: Theme.of(context).colorScheme.primary),
borderRadius: BorderRadius.circular(50)
),
child: IconButton(onPressed: (){
ScaffoldMessenger.of(context)
.clearSnackBars();
addNewDevice(context,
roleSnapshot.data ?? role, devicesManagerBloc);
}, iconSize: 50, icon: const Icon(Icons.add),),),
SizedBox(height: context.mediumValue,),
Text(appLocalization(context).dont_have_device, style: context.responsiveBodyMediumWithBold,)
],
),
);
} else { } else {
if (tagSnapshot.data!.isNotEmpty) { if (tagSnapshot.data!.isNotEmpty) {
tagIndex = tagSnapshot.data![0]; tagIndex = tagSnapshot.data![0];
@@ -83,11 +113,7 @@ class _DevicesManagerScreenState extends State<DevicesManagerScreen> {
devicesManagerBloc: devicesManagerBloc, devicesManagerBloc: devicesManagerBloc,
), ),
SizedBox(height: context.lowValue), SizedBox(height: context.lowValue),
StreamBuilder<String>( SizedBox(
stream: devicesManagerBloc.streamUserRole,
initialData: role,
builder: (context, roleSnapshot) {
return SizedBox(
height: getTableHeight(allDeviceSnapshot.data?.length ?? 1), height: getTableHeight(allDeviceSnapshot.data?.length ?? 1),
child: PaginatedDataTable2( child: PaginatedDataTable2(
wrapInCard: false, wrapInCard: false,
@@ -103,7 +129,7 @@ class _DevicesManagerScreenState extends State<DevicesManagerScreen> {
child: Text( child: Text(
appLocalization(context) appLocalization(context)
.paginated_data_table_title, .paginated_data_table_title,
style: context.headlineMediumTextStyle, style: context.responsiveBodyLargeWithBold,
), ),
), ),
columns: [ columns: [
@@ -111,7 +137,8 @@ class _DevicesManagerScreenState extends State<DevicesManagerScreen> {
RoleEnums.ADMIN.name || RoleEnums.ADMIN.name ||
roleSnapshot.data == roleSnapshot.data ==
RoleEnums.USER.name) RoleEnums.USER.name)
DataColumn( DataColumn2(
fixedWidth: context.dynamicWidth(0.3),
label: Text(appLocalization(context) label: Text(appLocalization(context)
.paginated_data_table_column_deviceName), .paginated_data_table_column_deviceName),
), ),
@@ -172,37 +199,31 @@ class _DevicesManagerScreenState extends State<DevicesManagerScreen> {
ScaffoldMessenger.of(context) ScaffoldMessenger.of(context)
.clearSnackBars(); .clearSnackBars();
addNewDevice(context, addNewDevice(context,
roleSnapshot.data ?? role); roleSnapshot.data ?? role, devicesManagerBloc);
}, },
icon: IconConstants.instance icon: IconConstants.instance
.getMaterialIcon(Icons.add)) .getMaterialIcon(Icons.add))
], ],
source: DeviceSource( source: DeviceSource(
devices: allDeviceSnapshot.data ?? devices, devices: allDeviceSnapshot.data ?? [],
context: context, context: context,
devicesBloc: devicesManagerBloc, devicesBloc: devicesManagerBloc,
role: role, role: role,
), ),
), ),
);
},
), ),
SizedBox(height: context.lowValue), SizedBox(height: context.lowValue),
Text( Text(
appLocalization(context).overview_message, appLocalization(context).overview_message,
style: const TextStyle( style: context.responsiveBodyLargeWithBold
fontSize: 20,
fontWeight: FontWeight.bold,
),
), ),
StreamBuilder<Map<String, List<Device>>>( StreamBuilder<Map<String, List<Device>>>(
stream: devicesManagerBloc.streamDeviceByState, stream: devicesManagerBloc.streamDeviceByState,
builder: (context, devicesByStateSnapshot) { builder: (context, devicesByStateSnapshot) {
if (devicesByStateSnapshot.data == null) { if (devicesByStateSnapshot.data == null) {
devicesManagerBloc.getDeviceByState( devicesManagerBloc.getDeviceByState(context,
tagSnapshot.data?[0] ?? -2); tagSnapshot.data?[0] ?? -2);
return const Center( return const SharedComponentLoadingAnimation();
child: CircularProgressIndicator());
} else { } else {
return SharedPieChart( return SharedPieChart(
deviceByState: deviceByState:
@@ -219,6 +240,8 @@ class _DevicesManagerScreenState extends State<DevicesManagerScreen> {
}, },
), ),
); );
}
);
}), }),
); );
} }
@@ -230,7 +253,7 @@ class _DevicesManagerScreenState extends State<DevicesManagerScreen> {
double getTableHeight(int dataLength){ double getTableHeight(int dataLength){
if(dataLength < 3){ if(dataLength < 3){
return context.dynamicHeight(0.3); return context.dynamicHeight(0.35);
}else { }else {
return context.dynamicHeight(0.4); return context.dynamicHeight(0.4);
} }
@@ -370,7 +393,7 @@ class TagState extends StatelessWidget {
), ),
GestureDetector( GestureDetector(
onTap: () { onTap: () {
devicesManagerBloc.getDeviceByState(-2); devicesManagerBloc.getDeviceByState(context,-2);
}, },
child: const Icon( child: const Icon(
Icons.close, Icons.close,

View File

@@ -1,7 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:sfm_app/product/constant/enums/app_route_enums.dart';
import 'package:sfm_app/product/constant/image/image_constants.dart'; import '../../product/constant/enums/app_route_enums.dart';
import '../../product/constant/image/image_constants.dart';
class NotFoundScreen extends StatelessWidget { class NotFoundScreen extends StatelessWidget {
const NotFoundScreen({super.key}); const NotFoundScreen({super.key});

View File

@@ -1,11 +1,12 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'package:alarm/alarm.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../product/shared/shared_loading_animation.dart';
import '../../product/shared/shared_component_loading_animation.dart';
import 'shared/alert_card.dart'; import 'shared/alert_card.dart';
import 'shared/warning_card.dart'; import 'shared/warning_card.dart';
import '../../product/utils/device_utils.dart';
import 'device_alias_model.dart'; import 'device_alias_model.dart';
import 'shared/overview_card.dart'; import 'shared/overview_card.dart';
import '../settings/device_notification_settings/device_notification_settings_model.dart'; import '../settings/device_notification_settings/device_notification_settings_model.dart';
@@ -17,7 +18,6 @@ import '../../product/base/bloc/base_bloc.dart';
class HomeScreen extends StatefulWidget { class HomeScreen extends StatefulWidget {
const HomeScreen({super.key}); const HomeScreen({super.key});
@override @override
State<HomeScreen> createState() => _HomeScreenState(); State<HomeScreen> createState() => _HomeScreenState();
} }
@@ -25,33 +25,54 @@ class HomeScreen extends StatefulWidget {
class _HomeScreenState extends State<HomeScreen> { class _HomeScreenState extends State<HomeScreen> {
late HomeBloc homeBloc; late HomeBloc homeBloc;
APIServices apiServices = APIServices(); APIServices apiServices = APIServices();
Map<String, List<DeviceWithAlias>> allDevicesAliasMap = {};
Map<String, List<DeviceWithAlias>> allDevicesAliasJoinedMap = {};
List<DeviceWithAlias> devices = [];
bool isFunctionCall = false; bool isFunctionCall = false;
Timer? getAllDevicesTimer; Timer? getAllDevicesTimer;
int notificationCount = 0;
Map<String, List<DeviceWithAlias>> ownerDevicesStatus = {};
List<String> ownerDevicesState = [];
@override @override
void initState() { void initState() {
super.initState(); super.initState();
homeBloc = BlocProvider.of(context); homeBloc = BlocProvider.of(context);
getOwnerAndJoinedDevices();
const duration = Duration(seconds: 10); const duration = Duration(seconds: 10);
getAllDevicesTimer = Timer.periodic(duration, (Timer t) => getOwnerAndJoinedDevices()); WidgetsBinding.instance.addPostFrameCallback((_) {
// Code ở đây chạy sau khi giao diện render xong
getAllDevicesTimer = Timer.periodic(
duration, (Timer t) => homeBloc.getOwnerAndJoinedDevices(context));
// Ví dụ: gọi API, scroll tới vị trí nào đó, v.v.
});
}
Future<void> loadAlarms() async {
final alarms = await Alarm.getAlarms();
log("Alarms: $alarms");
} }
@override @override
void dispose() { void dispose() {
getAllDevicesTimer?.cancel(); getAllDevicesTimer?.cancel();
homeBloc.dispose();
super.dispose(); super.dispose();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return StreamBuilder<List<DeviceWithAlias>?>(
stream: homeBloc.streamAliasDevices,
builder: (context, aliasDevicesSnapshot) {
if (aliasDevicesSnapshot.data == null) {
homeBloc.getOwnerAndJoinedDevices(context);
return const SharedLoadingAnimation();
} else {
homeBloc.getOwnerDeviceState(
context, aliasDevicesSnapshot.data ?? []);
homeBloc.getDeviceStatusAliasMap(aliasDevicesSnapshot.data ?? []);
checkSettingDevice(aliasDevicesSnapshot.data ?? []);
return Scaffold( return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
loadAlarms();
},
child: const Icon(Icons.alarm),
),
body: Padding( body: Padding(
padding: context.paddingLow, padding: context.paddingLow,
child: SingleChildScrollView( child: SingleChildScrollView(
@@ -67,10 +88,16 @@ class _HomeScreenState extends State<HomeScreen> {
StreamBuilder<int>( StreamBuilder<int>(
stream: homeBloc.streamCountNotification, stream: homeBloc.streamCountNotification,
builder: (context, countSnapshot) { builder: (context, countSnapshot) {
if (countSnapshot.data == null) {
homeBloc.getOwnerDeviceState(
context, aliasDevicesSnapshot.data ?? []);
return const Text("0");
} else {
return Text( return Text(
"(${countSnapshot.data ?? 0})", "(${countSnapshot.data ?? 0})",
style: context.titleMediumTextStyle, style: context.titleMediumTextStyle,
); );
}
}, },
) )
], ],
@@ -78,46 +105,109 @@ class _HomeScreenState extends State<HomeScreen> {
SizedBox( SizedBox(
child: SingleChildScrollView( child: SingleChildScrollView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
child: StreamBuilder<Map<String, List<DeviceWithAlias>>>( child:
StreamBuilder<Map<String, List<DeviceWithAlias>>>(
stream: homeBloc.streamOwnerDevicesStatus, stream: homeBloc.streamOwnerDevicesStatus,
builder: (context, snapshot) { builder: (context, ownerDevicesStatusSnapshot) {
if (snapshot.data?['state'] != null || snapshot.data?['battery'] != null) { if (ownerDevicesStatusSnapshot.data == null) {
return ConstrainedBox( homeBloc.getOwnerDeviceState(
constraints: BoxConstraints(minWidth: MediaQuery.of(context).size.width), context, aliasDevicesSnapshot.data ?? []);
return const SharedComponentLoadingAnimation();
} else {
return AnimatedSwitcher(
duration: context.lowDuration,
transitionBuilder: (Widget child,
Animation<double> animation) {
final offsetAnimation = Tween<Offset>(
begin: const Offset(0.0, 0.2),
end: Offset.zero,
).animate(CurvedAnimation(
parent: animation,
curve: Curves.easeInOut,
));
return FadeTransition(
opacity: animation,
child: SlideTransition(
position: offsetAnimation,
child: child,
),
);
},
child: ownerDevicesStatusSnapshot
.data?['state'] !=
null ||
ownerDevicesStatusSnapshot
.data?['battery'] !=
null
? ConstrainedBox(
key: const ValueKey('data'),
constraints: BoxConstraints(
minWidth: MediaQuery.of(context)
.size
.width),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment:
MainAxisAlignment.start,
children: [ children: [
if (snapshot.data?['state'] != null) if (ownerDevicesStatusSnapshot
...snapshot.data!['state']! .data?['state'] !=
null)
...ownerDevicesStatusSnapshot
.data!['state']!
.map( .map(
(item) => SizedBox( (item) => SizedBox(
width: context.dynamicWidth(0.95), width: context
child: FutureBuilder<Widget>( .dynamicWidth(0.95),
future: warningCard(context, apiServices, item), child: FutureBuilder<
builder: (context, warningCardSnapshot) { Widget>(
if (warningCardSnapshot.hasData) { future: warningCard(
return warningCardSnapshot.data!; context,
apiServices,
item),
builder: (context,
warningCardSnapshot) {
if (warningCardSnapshot
.hasData) {
return warningCardSnapshot
.data!;
} else { } else {
return const SizedBox.shrink(); return const SizedBox
.shrink();
} }
}, },
), ),
), ),
) )
.toList(), .toList(),
if (snapshot.data?['battery'] != null) if (ownerDevicesStatusSnapshot
...snapshot.data!['battery']! .data?['battery'] !=
null)
...ownerDevicesStatusSnapshot
.data!['battery']!
.map( .map(
(batteryItem) => SizedBox( (batteryItem) => SizedBox(
width: context.dynamicWidth(0.95), width: context
child: FutureBuilder<Widget>( .dynamicWidth(0.95),
future: notificationCard( child: FutureBuilder<
context, "lowBattery", appLocalization(context).low_battery_message, batteryItem), Widget>(
builder: (context, warningCardSnapshot) { future:
if (warningCardSnapshot.hasData) { notificationCard(
return warningCardSnapshot.data!; context,
"lowBattery",
appLocalization(
context)
.low_battery_message,
batteryItem,
),
builder: (context,
warningCardSnapshot) {
if (warningCardSnapshot
.hasData) {
return warningCardSnapshot
.data!;
} else { } else {
return const SizedBox.shrink(); return const SizedBox
.shrink();
} }
}, },
), ),
@@ -126,48 +216,49 @@ class _HomeScreenState extends State<HomeScreen> {
.toList(), .toList(),
], ],
), ),
); )
} else { : Padding(
return Padding( key: const ValueKey('no_data'),
padding: context.paddingMedium, padding: context.paddingMedium,
child: Center( child: Center(
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
const Icon( const Icon(
Icons.check_circle_outline_rounded, Icons
.check_circle_outline_rounded,
size: 40, size: 40,
color: Colors.green, color: Colors.green,
), ),
SizedBox(width: context.lowValue), SizedBox(
width: context.lowValue),
Text( Text(
appLocalization(context).notification_description, appLocalization(context)
.notification_description,
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow:
TextOverflow.ellipsis,
softWrap: true, softWrap: true,
textAlign: TextAlign.start, textAlign: TextAlign.start,
), ),
], ],
), ),
), ),
),
); );
} }
}, },
), ),
), ),
), ),
StreamBuilder<Map<String, List<DeviceWithAlias>>?>(
StreamBuilder<Map<String, List<DeviceWithAlias>>>(
stream: homeBloc.streamAllDevicesAliasMap, stream: homeBloc.streamAllDevicesAliasMap,
builder: (context, allDevicesAliasMapSnapshot) { builder: (context, allDevicesAliasMapSnapshot) {
if (!allDevicesAliasMapSnapshot.hasData || if (allDevicesAliasMapSnapshot.data == null) {
allDevicesAliasMapSnapshot.data == null) { homeBloc.getDeviceStatusAliasMap(
return const Center(child: CircularProgressIndicator()); aliasDevicesSnapshot.data ?? []);
} return const SharedComponentLoadingAnimation();
} else {
final data = allDevicesAliasMapSnapshot.data!; final data = allDevicesAliasMapSnapshot.data!;
return OverviewCard( return OverviewCard(
isOwner: true, isOwner: true,
@@ -175,34 +266,10 @@ class _HomeScreenState extends State<HomeScreen> {
active: data['online']?.length ?? 0, active: data['online']?.length ?? 0,
inactive: data['offline']?.length ?? 0, inactive: data['offline']?.length ?? 0,
warning: data['warn']?.length ?? 0, warning: data['warn']?.length ?? 0,
unused: data['not-use']?.length ?? 0); unused: data['not-use']?.length ?? 0,
}), showUnused: false,
SizedBox(height: context.lowValue),
StreamBuilder<Map<String, List<DeviceWithAlias>>>(
stream: homeBloc.streamAllDevicesAliasJoinedMap,
builder: (context, allDevicesAliasJoinedMapSnapshot) {
if (!allDevicesAliasJoinedMapSnapshot.hasData ||
allDevicesAliasJoinedMapSnapshot.data == null) {
return const Center(child: CircularProgressIndicator());
}
final data = allDevicesAliasJoinedMapSnapshot.data!;
final total = data['all']?.length ?? 0;
final active = data['online']?.length ?? 0;
final inactive = data['offline']?.length ?? 0;
final warning = data['warn']?.length ?? 0;
final unused = data['not-use']?.length ?? 0;
if (total == 0 && active == 0 && inactive == 0 && warning == 0 && unused == 0) {
return const SizedBox.shrink();
}
return OverviewCard(
isOwner: false,
total: total,
active: active,
inactive: inactive,
warning: warning,
unused: unused,
); );
}
}, },
), ),
], ],
@@ -211,121 +278,31 @@ class _HomeScreenState extends State<HomeScreen> {
), ),
); );
} }
});
void getOwnerAndJoinedDevices() async {
String response = await apiServices.getDashBoardDevices();
final data = jsonDecode(response);
List<dynamic> result = data["items"];
devices = DeviceWithAlias.fromJsonDynamicList(result);
getOwnerDeviceState(devices);
checkSettingDevice(devices);
getDeviceStatusAliasMap(devices);
}
void getOwnerDeviceState(List<DeviceWithAlias> allDevices) async {
List<DeviceWithAlias> ownerDevices = [];
for (var device in allDevices) {
if (device.isOwner!) {
ownerDevices.add(device);
}
}
if (ownerDevicesState.isEmpty || ownerDevicesState.length < devices.length) {
ownerDevicesState.clear();
ownerDevicesStatus.clear();
homeBloc.sinkOwnerDevicesStatus.add(ownerDevicesStatus);
int count = 0;
for (var device in ownerDevices) {
Map<String, dynamic> sensorMap =
DeviceUtils.instance.getDeviceSensors(context, device.status?.sensors ?? []);
if (device.state == 1 || device.state == 3) {
ownerDevicesStatus["state"] ??= [];
ownerDevicesStatus["state"]!.add(device);
homeBloc.sinkOwnerDevicesStatus.add(ownerDevicesStatus);
count++;
}
if (sensorMap['sensorBattery'] != appLocalization(context).no_data_message) {
if (double.parse(sensorMap['sensorBattery']) <= 20) {
ownerDevicesStatus['battery'] ??= [];
ownerDevicesStatus['battery']!.add(device);
homeBloc.sinkOwnerDevicesStatus.add(ownerDevicesStatus);
count++;
}
}
notificationCount = count;
homeBloc.sinkCountNotification.add(notificationCount);
}
}
}
void getDeviceStatusAliasMap(List<DeviceWithAlias> devices) {
allDevicesAliasMap.clear();
allDevicesAliasJoinedMap.clear();
for (var key in ['all', 'online', 'offline', 'warning', 'not-use']) {
allDevicesAliasMap[key] = [];
allDevicesAliasJoinedMap[key] = [];
}
for (DeviceWithAlias device in devices) {
if (device.isOwner == true) {
allDevicesAliasMap['all']!.add(device);
if (device.state == 0 || device.state == 1) {
allDevicesAliasMap['online']!.add(device);
}
if (device.state == -1) {
allDevicesAliasMap['offline']!.add(device);
}
if (device.state == 1) {
allDevicesAliasMap['warning']!.add(device);
}
if (device.state == -2) {
allDevicesAliasMap['not-use']!.add(device);
}
} else {
allDevicesAliasJoinedMap['all']!.add(device);
if (device.state == 0 || device.state == 1) {
allDevicesAliasJoinedMap['online']!.add(device);
}
if (device.state == -1) {
allDevicesAliasJoinedMap['offline']!.add(device);
}
if (device.state == 1) {
allDevicesAliasJoinedMap['warning']!.add(device);
}
if (device.state == -2) {
allDevicesAliasJoinedMap['not-use']!.add(device);
}
}
}
homeBloc.sinkAllDevicesAliasMap.add(allDevicesAliasMap);
homeBloc.sinkAllDevicesAliasJoinedMap.add(allDevicesAliasJoinedMap);
} }
void checkSettingDevice(List<DeviceWithAlias> devices) async { void checkSettingDevice(List<DeviceWithAlias> devices) async {
if (isFunctionCall) { if (isFunctionCall) {
log("Ham check setting da duoc goi"); log("Ham check setting da duoc goi");
} else { } else {
String? response = await apiServices.getAllSettingsNotificationOfDevices(); await apiServices.execute(context, () async {
if (response != "") {
final data = jsonDecode(response);
final result = data['data'];
// log("Data ${DeviceNotificationSettings.mapFromJson(jsonDecode(data)).values.toList()}");
List<DeviceNotificationSettings> list = List<DeviceNotificationSettings> list =
DeviceNotificationSettings.mapFromJson(result).values.toList(); await apiServices.getAllSettingsNotificationOfDevices();
// log("List: $list"); // log("List: $list");
Set<String> thingIdsInList = list.map((device) => device.thingId!).toSet(); Set<String> thingIdsInList =
list.map((device) => device.thingId!).toSet();
for (var device in devices) { for (var device in devices) {
if (!thingIdsInList.contains(device.thingId)) { if (!thingIdsInList.contains(device.thingId)) {
log("Device with Thing ID ${device.thingId} is not in the notification settings list."); log("Device with Thing ID ${device.thingId} is not in the notification settings list.");
await apiServices.setupDeviceNotification(device.thingId!, device.name!); await apiServices.execute(context, () async {
await apiServices.setupDeviceNotification(
device.thingId!, device.name!);
});
} else { } else {
log("All devices are in the notification settings list."); log("All devices are in the notification settings list.");
} }
} }
} else { });
log("apiServices: getAllSettingsNotificationofDevices error!");
}
} }
isFunctionCall = true; isFunctionCall = true;
} }

View File

@@ -3,24 +3,24 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:sfm_app/feature/home/device_alias_model.dart';
import '../../../product/shared/shared_rocket_container.dart';
import '../../../product/constant/enums/app_route_enums.dart'; import '../../../product/constant/enums/app_route_enums.dart';
import '../../../product/constant/image/image_constants.dart'; import '../../../product/constant/image/image_constants.dart';
import '../../../product/extension/context_extension.dart'; import '../../../product/extension/context_extension.dart';
import '../../../product/services/language_services.dart'; import '../../../product/services/language_services.dart';
import '../../../product/utils/device_utils.dart'; import '../../../product/utils/device_utils.dart';
import '../../../product/constant/icon/icon_constants.dart'; import '../../../product/constant/icon/icon_constants.dart';
import '../device_alias_model.dart';
Future<Widget> notificationCard(BuildContext context, String notiticationType, Future<Widget> notificationCard(BuildContext context, String notificationType,
String notificationTitle, DeviceWithAlias device) async { String notificationTitle, DeviceWithAlias device) async {
String location = ""; String location = "";
if (device.areaPath != "") { if (device.areaPath != "") {
location = await DeviceUtils.instance location = await DeviceUtils.instance
.getFullDeviceLocation(context, device.areaPath!); .getFullDeviceLocation(context, device.areaPath!, "");
} }
String path = ""; String path = "";
// DateTime time = DateTime.now();
String time = ""; String time = "";
for (var sensor in device.status!.sensors!) { for (var sensor in device.status!.sensors!) {
if (sensor.name! == "7") { if (sensor.name! == "7") {
@@ -29,7 +29,7 @@ Future<Widget> notificationCard(BuildContext context, String notiticationType,
time = DateFormat('yyyy-MM-dd HH:mm:ss').format(dateTime); time = DateFormat('yyyy-MM-dd HH:mm:ss').format(dateTime);
} }
} }
if (notiticationType == "lowBattery") { if (notificationType == "lowBattery") {
path = ImageConstants.instance.getImage("low_battery"); path = ImageConstants.instance.getImage("low_battery");
} }
return Card( return Card(
@@ -62,10 +62,7 @@ Future<Widget> notificationCard(BuildContext context, String notiticationType,
SizedBox( SizedBox(
child: Text( child: Text(
"${appLocalization(context).device_title} ${device.isOwner! ? device.name : device.alias}", "${appLocalization(context).device_title} ${device.isOwner! ? device.name : device.alias}",
style: const TextStyle( style: context.responsiveBodyLargeWithBold,
fontWeight: FontWeight.bold,
fontSize: 18,
),
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
softWrap: true, softWrap: true,
@@ -92,7 +89,7 @@ Future<Widget> notificationCard(BuildContext context, String notiticationType,
Expanded( Expanded(
child: Text( child: Text(
location, location,
style: const TextStyle(fontSize: 15), style: context.responsiveBodySmall,
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
softWrap: true, softWrap: true,
@@ -111,7 +108,7 @@ Future<Widget> notificationCard(BuildContext context, String notiticationType,
Expanded( Expanded(
child: Text( child: Text(
time.toString(), time.toString(),
style: const TextStyle(fontSize: 15), style: context.responsiveBodySmall,
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
softWrap: true, softWrap: true,
@@ -120,11 +117,16 @@ Future<Widget> notificationCard(BuildContext context, String notiticationType,
), ),
], ],
), ),
Align( SizedBox(
height: context.lowValue,
),
device.isOwner!
? Align(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: OutlinedButton( child: OutlinedButton(
style: const ButtonStyle( style: const ButtonStyle(
backgroundColor: WidgetStatePropertyAll(Colors.blueAccent), backgroundColor:
WidgetStatePropertyAll(Colors.blueAccent),
), ),
onPressed: () { onPressed: () {
context.pushNamed(AppRoutes.DEVICE_DETAIL.name, context.pushNamed(AppRoutes.DEVICE_DETAIL.name,
@@ -132,11 +134,28 @@ Future<Widget> notificationCard(BuildContext context, String notiticationType,
}, },
child: Text( child: Text(
appLocalization(context).detail_message, appLocalization(context).detail_message,
style: const TextStyle( style: const TextStyle(color: Colors.white),
color: Colors.white, ),
),
)
: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ClipPath(
clipper: SharedRocketContainer(),
child: Container(
padding: EdgeInsets.all(context.lowValue),
height: context.mediumValue,
width: context.dynamicWidth(0.22),
decoration: BoxDecoration(
color: Colors.green[300],
),
child: Text(
appLocalization(context).interfamily_page_name,
), ),
), ),
), ),
],
), ),
], ],
), ),

View File

@@ -1,69 +1,93 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'status_card.dart'; import 'status_card.dart';
import '../../../product/extension/context_extension.dart'; import '../../../product/extension/context_extension.dart';
import '../../../product/services/language_services.dart'; import '../../../product/services/language_services.dart';
class OverviewCard extends StatelessWidget { class OverviewCard extends StatefulWidget {
final bool isOwner; final bool isOwner;
final int total; final int total;
final int active; final int active;
final int inactive; final int inactive;
final int warning; final int warning;
final int unused; final int unused;
final bool showTotal;
final bool showActive;
final bool showInactive;
final bool showWarning;
final bool showUnused;
const OverviewCard( const OverviewCard({
{super.key, super.key,
required this.isOwner, required this.isOwner,
required this.total, required this.total,
required this.active, required this.active,
required this.inactive, required this.inactive,
required this.warning, required this.warning,
required this.unused}); required this.unused,
this.showTotal = true,
this.showActive = true,
this.showInactive = true,
this.showWarning = true,
this.showUnused = true,
});
@override
State<OverviewCard> createState() => _OverviewCardState();
}
class _OverviewCardState extends State<OverviewCard> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Card( return FittedBox(
elevation: 8, alignment: Alignment.topCenter,
child: SizedBox(
width: context.width,
child: Card(
// elevation: 8,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
child: Padding( child: Padding(
padding: context.paddingNormal, padding: context.paddingNormal,
child: Column( child: Column(
mainAxisSize: MainAxisSize.min,
children: [ children: [
Text( Text(
isOwner widget.isOwner
? appLocalization(context).overview_message ? appLocalization(context).overview_message
: appLocalization(context).interfamily_page_name, : appLocalization(context).interfamily_page_name,
style: const TextStyle( style: context.h2,
fontSize: 20,
fontWeight: FontWeight.bold,
),
), ),
SizedBox(height: context.normalValue), SizedBox(height: context.normalValue),
Column( Column(
children: [ children: [
if (widget.showTotal)
StatusCard( StatusCard(
label: appLocalization(context).total_nof_devices_message, label: appLocalization(context).total_nof_devices_message,
count: total, count: widget.total,
color: Colors.blue, color: Colors.blue,
), ),
if (widget.showActive)
StatusCard( StatusCard(
label: appLocalization(context).active_devices_message, label: appLocalization(context).active_devices_message,
count: active, count: widget.active,
color: Colors.green, color: Colors.green,
), ),
if (widget.showInactive)
StatusCard( StatusCard(
label: appLocalization(context).inactive_devices_message, label: appLocalization(context).inactive_devices_message,
count: inactive, count: widget.inactive,
color: Colors.grey, color: Colors.grey,
), ),
if (widget.showWarning)
StatusCard( StatusCard(
label: appLocalization(context).warning_devices_message, label: appLocalization(context).warning_devices_message,
count: warning, count: widget.warning,
color: Colors.orange, color: Colors.orange,
), ),
if (widget.showUnused)
StatusCard( StatusCard(
label: appLocalization(context).unused_devices_message, label: appLocalization(context).unused_devices_message,
count: unused, count: widget.unused,
color: Colors.yellow, color: Colors.yellow,
), ),
], ],
@@ -71,6 +95,8 @@ class OverviewCard extends StatelessWidget {
], ],
), ),
), ),
),
),
); );
} }
} }

View File

@@ -1,5 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../product/extension/context_extension.dart';
class StatusCard extends StatelessWidget { class StatusCard extends StatelessWidget {
final String label; final String label;
final int count; final int count;
@@ -27,14 +29,8 @@ class StatusCard extends StatelessWidget {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text(label, style: const TextStyle(fontSize: 18)), Text(label, style: context.responsiveBodyLarge),
Text( Text(count.toString(), style: context.h2),
count.toString(),
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
], ],
), ),
); );

View File

@@ -3,7 +3,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:maps_launcher/maps_launcher.dart'; import 'package:maps_launcher/maps_launcher.dart';
import 'package:badges/badges.dart' as badges;
import '../../../product/shared/shared_rocket_container.dart';
import '../device_alias_model.dart'; import '../device_alias_model.dart';
import '../../../product/constant/icon/icon_constants.dart'; import '../../../product/constant/icon/icon_constants.dart';
import '../../../product/constant/image/image_constants.dart'; import '../../../product/constant/image/image_constants.dart';
@@ -21,7 +22,7 @@ Future<Widget> warningCard(BuildContext context, APIServices apiServices,
String fullLocation = ""; String fullLocation = "";
if (device.areaPath != "") { if (device.areaPath != "") {
fullLocation = await DeviceUtils.instance fullLocation = await DeviceUtils.instance
.getFullDeviceLocation(context, device.areaPath!); .getFullDeviceLocation(context, device.areaPath!, "");
} }
String time = ""; String time = "";
for (var sensor in device.status!.sensors!) { for (var sensor in device.status!.sensors!) {
@@ -33,7 +34,7 @@ Future<Widget> warningCard(BuildContext context, APIServices apiServices,
} }
if (device.state! == 3) { if (device.state! == 3) {
backgroundColor = Colors.grey; backgroundColor = Colors.grey;
textColor = Colors.black; textColor = Colors.white;
message = appLocalization(context).in_progress_message; message = appLocalization(context).in_progress_message;
} else if (device.state! == 2) { } else if (device.state! == 2) {
backgroundColor = const Color.fromARGB(255, 6, 138, 72); backgroundColor = const Color.fromARGB(255, 6, 138, 72);
@@ -42,32 +43,18 @@ Future<Widget> warningCard(BuildContext context, APIServices apiServices,
} else if (device.state! == 1) { } else if (device.state! == 1) {
backgroundColor = const Color.fromARGB(255, 250, 63, 63); backgroundColor = const Color.fromARGB(255, 250, 63, 63);
textColor = Colors.white; textColor = Colors.white;
if(device.isOwner == true){
message = appLocalization(context).button_fake_fire_message; message = appLocalization(context).button_fake_fire_message;
}else{
message = appLocalization(context).smoke_detecting_message_lowercase;
}
} else { } else {
backgroundColor = Colors.black; backgroundColor = Colors.black;
textColor = Colors.white; textColor = Colors.white;
message = appLocalization(context).disconnect_message_uppercase; message = appLocalization(context).disconnect_message_uppercase;
} }
return badges.Badge( return Card(
badgeAnimation: const badges.BadgeAnimation.fade(),
position: badges.BadgePosition.bottomStart(bottom: 15, start: 10),
badgeContent: device.isOwner!
? null
: Text(
appLocalization(context).interfamily_page_name,
style: TextStyle(
fontSize: 20,
color: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.bold),
),
badgeStyle: badges.BadgeStyle(
shape: badges.BadgeShape.square,
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: Colors.white, width: 2),
badgeColor: Theme.of(context).colorScheme.surfaceDim,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
),
child: Card(
child: Padding( child: Padding(
padding: context.paddingLow, padding: context.paddingLow,
child: Column( child: Column(
@@ -97,10 +84,7 @@ Future<Widget> warningCard(BuildContext context, APIServices apiServices,
SizedBox( SizedBox(
child: Text( child: Text(
"${appLocalization(context).device_title}: ${device.isOwner! ? device.name : device.alias}", "${appLocalization(context).device_title}: ${device.isOwner! ? device.name : device.alias}",
style: const TextStyle( style: context.responsiveBodyLargeWithBold,
fontWeight: FontWeight.bold,
fontSize: 18,
),
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
softWrap: true, softWrap: true,
@@ -128,7 +112,7 @@ Future<Widget> warningCard(BuildContext context, APIServices apiServices,
Expanded( Expanded(
child: Text( child: Text(
fullLocation, fullLocation,
style: const TextStyle(fontSize: 15), style: context.responsiveBodySmall,
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
softWrap: true, softWrap: true,
@@ -147,7 +131,7 @@ Future<Widget> warningCard(BuildContext context, APIServices apiServices,
Expanded( Expanded(
child: Text( child: Text(
time, time,
style: const TextStyle(fontSize: 15), style: context.responsiveBodySmall,
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
softWrap: true, softWrap: true,
@@ -159,14 +143,16 @@ Future<Widget> warningCard(BuildContext context, APIServices apiServices,
SizedBox( SizedBox(
height: context.lowValue, height: context.lowValue,
), ),
Row( device.isOwner == true
? Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
IconButton.outlined( IconButton.outlined(
onPressed: () async => {}, onPressed: () async => {},
// displayListOfFireStationPhoneNumbers(testDevice), // displayListOfFireStationPhoneNumbers(testDevice),
icon: IconConstants.instance.getMaterialIcon(Icons.call), icon:
IconConstants.instance.getMaterialIcon(Icons.call),
iconSize: 25, iconSize: 25,
style: ButtonStyle( style: ButtonStyle(
backgroundColor: backgroundColor:
@@ -199,7 +185,8 @@ Future<Widget> warningCard(BuildContext context, APIServices apiServices,
WidgetStatePropertyAll(backgroundColor)), WidgetStatePropertyAll(backgroundColor)),
onPressed: () async { onPressed: () async {
if (message == if (message ==
appLocalization(context).button_fake_fire_message) { appLocalization(context)
.button_fake_fire_message) {
await showDialog( await showDialog(
context: context, context: context,
builder: (context) => AlertDialog( builder: (context) => AlertDialog(
@@ -212,8 +199,11 @@ Future<Widget> warningCard(BuildContext context, APIServices apiServices,
actions: [ actions: [
TextButton( TextButton(
onPressed: () async { onPressed: () async {
await apiServices.execute(context,
() async {
int statusCode = await apiServices int statusCode = await apiServices
.confirmFakeFireByUser(device.thingId!); .confirmFakeFireByUser(
device.thingId!);
if (statusCode == 200) { if (statusCode == 200) {
showNoIconTopSnackBar( showNoIconTopSnackBar(
context, context,
@@ -229,13 +219,15 @@ Future<Widget> warningCard(BuildContext context, APIServices apiServices,
Colors.red, Colors.red,
Colors.red); Colors.red);
} }
});
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
child: Text( child: Text(
appLocalization(context) appLocalization(context)
.confirm_fake_fire_sure_message, .confirm_fake_fire_sure_message,
style: style: const TextStyle(
const TextStyle(color: Colors.red)), color: Colors.red)),
), ),
TextButton( TextButton(
onPressed: () { onPressed: () {
@@ -250,7 +242,8 @@ Future<Widget> warningCard(BuildContext context, APIServices apiServices,
} else { } else {
showNoIconTopSnackBar( showNoIconTopSnackBar(
context, context,
appLocalization(context).let_PCCC_handle_message, appLocalization(context)
.let_PCCC_handle_message,
Colors.orange, Colors.orange,
Colors.white); Colors.white);
} }
@@ -263,10 +256,45 @@ Future<Widget> warningCard(BuildContext context, APIServices apiServices,
), ),
), ),
], ],
)
: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ClipPath(
clipper: SharedRocketContainer(),
child: Container(
padding: EdgeInsets.all(context.lowValue),
height: context.mediumValue,
width: context.dynamicWidth(0.22),
decoration: BoxDecoration(
color: Colors.green[300],
),
child: Text(
appLocalization(context).interfamily_page_name,
),
),
),
SizedBox(width: context.mediumValue),
Expanded(
child: Align(
alignment: Alignment.centerRight,
child: OutlinedButton(
style: ButtonStyle(
backgroundColor:
WidgetStatePropertyAll(backgroundColor)),
onPressed: () async {},
child: Text(
message,
style: TextStyle(color: textColor),
),
),
),
),
],
), ),
], ],
), ),
), ),
),
); );
} }

View File

@@ -2,16 +2,17 @@
import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../product/extension/context_extension.dart';
import '../../../bloc/group_detail_bloc.dart'; import '../../../bloc/group_detail_bloc.dart';
import '../../../product/constant/icon/icon_constants.dart'; import '../../../product/constant/icon/icon_constants.dart';
import '../../../product/services/language_services.dart'; import '../../../product/services/language_services.dart';
import '../../devices/device_model.dart'; import '../../devices/device_model.dart';
import 'group_detail_model.dart'; import 'group_detail_model.dart';
addDeviceDialog(BuildContext context, DetailGroupBloc detailGroupBloc, addDeviceDialog(BuildContext context, DetailGroupBloc detailGroupBloc,
String groupID, List<DeviceOfGroup> devices) async { String groupID, List<DeviceOfGroup> devices) async {
List<Device> ownerDevices = await detailGroupBloc.getOwnerDevices(); List<Device> ownerDevices = await detailGroupBloc.getOwnerDevices(context);
List<String> selectedItems = []; List<String> selectedItems = [];
List<String> selectedDevices = []; List<String> selectedDevices = [];
if (devices.isNotEmpty) { if (devices.isNotEmpty) {
@@ -36,7 +37,7 @@ addDeviceDialog(BuildContext context, DetailGroupBloc detailGroupBloc,
), ),
hint: Text( hint: Text(
appLocalization(context).choose_device_dropdownButton, appLocalization(context).choose_device_dropdownButton,
style: const TextStyle(fontSize: 14), style: context.responsiveBodySmall,
), ),
items: ownerDevices items: ownerDevices
.map( .map(
@@ -73,7 +74,7 @@ addDeviceDialog(BuildContext context, DetailGroupBloc detailGroupBloc,
Expanded( Expanded(
child: Text( child: Text(
item.name!, item.name!,
style: const TextStyle(fontSize: 14), style: context.responsiveBodySmall,
), ),
), ),
], ],
@@ -130,7 +131,7 @@ addDeviceDialog(BuildContext context, DetailGroupBloc detailGroupBloc,
for (var device in selectedItems) { for (var device in selectedItems) {
await detailGroupBloc.addDeviceToGroup( await detailGroupBloc.addDeviceToGroup(
context, groupID, device); context, groupID, device);
await detailGroupBloc.getGroupDetail(groupID); await detailGroupBloc.getGroupDetail(context,groupID);
} }
Navigator.of(dialogContext).pop(); Navigator.of(dialogContext).pop();

View File

@@ -1,9 +1,10 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../bloc/group_detail_bloc.dart'; import '../../../bloc/group_detail_bloc.dart';
import '../../../product/shared/shared_loading_animation.dart';
import 'group_detail_model.dart'; import 'group_detail_model.dart';
import '../../../product/base/bloc/base_bloc.dart'; import '../../../product/base/bloc/base_bloc.dart';
import '../../../product/constant/app/app_constants.dart'; import '../../../product/constant/app/app_constants.dart';
@@ -13,7 +14,6 @@ import '../../../product/services/api_services.dart';
import '../../../product/services/language_services.dart'; import '../../../product/services/language_services.dart';
import '../../../product/utils/device_utils.dart'; import '../../../product/utils/device_utils.dart';
import '../../../product/utils/response_status_utils.dart'; import '../../../product/utils/response_status_utils.dart';
import 'add_device_to_group_dialog.dart'; import 'add_device_to_group_dialog.dart';
class DetailGroupScreen extends StatefulWidget { class DetailGroupScreen extends StatefulWidget {
@@ -35,14 +35,14 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
const duration = Duration(seconds: 10); const duration = Duration(seconds: 10);
getGroupDetailTimer = Timer.periodic( getGroupDetailTimer = Timer.periodic(
duration, duration,
(Timer t) => detailGroupBloc.getGroupDetail(widget.group), (Timer t) => detailGroupBloc.getGroupDetail(context, widget.group),
); );
} }
@override @override
void dispose() { void dispose() {
getGroupDetailTimer?.cancel(); getGroupDetailTimer?.cancel();
super.dispose();
} }
@override @override
@@ -51,10 +51,8 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
stream: detailGroupBloc.streamDetailGroup, stream: detailGroupBloc.streamDetailGroup,
builder: (context, detailGroupSnapshot) { builder: (context, detailGroupSnapshot) {
if (detailGroupSnapshot.data?.id == null) { if (detailGroupSnapshot.data?.id == null) {
detailGroupBloc.getGroupDetail(widget.group); detailGroupBloc.getGroupDetail(context, widget.group);
return const Center( return const SharedLoadingAnimation();
child: CircularProgressIndicator(),
);
} else { } else {
return Scaffold( return Scaffold(
key: scaffoldKey, key: scaffoldKey,
@@ -140,8 +138,8 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
widget.group, widget.group,
user.id!, user.id!,
user.name!); user.name!);
detailGroupBloc detailGroupBloc.getGroupDetail(
.getGroupDetail(widget.group); context, widget.group);
}, },
icon: const Icon( icon: const Icon(
Icons.check, Icons.check,
@@ -158,8 +156,8 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
widget.group, widget.group,
user.id!, user.id!,
user.name!); user.name!);
await detailGroupBloc await detailGroupBloc.getGroupDetail(
.getGroupDetail(widget.group); context, widget.group);
}, },
icon: const Icon( icon: const Icon(
Icons.close, Icons.close,
@@ -205,8 +203,8 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
widget.group, widget.group,
user.id!, user.id!,
user.name!); user.name!);
await detailGroupBloc await detailGroupBloc.getGroupDetail(
.getGroupDetail(widget.group); context, widget.group);
}, },
value: 2, value: 2,
child: Text(appLocalization(context) child: Text(appLocalization(context)
@@ -239,7 +237,7 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
? PopupMenuButton( ? PopupMenuButton(
icon: IconConstants.instance icon: IconConstants.instance
.getMaterialIcon(Icons.more_horiz), .getMaterialIcon(Icons.more_horiz),
itemBuilder: (contex) => [ itemBuilder: (context) => [
PopupMenuItem( PopupMenuItem(
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
@@ -325,6 +323,7 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
Future.delayed(context.lowDuration).then( Future.delayed(context.lowDuration).then(
(value) => Navigator.pop(context), (value) => Navigator.pop(context),
); );
await apiServices.execute(context, () async {
int statusCode = await apiServices int statusCode = await apiServices
.deleteGroup(widget.group); .deleteGroup(widget.group);
showSnackBarResponseByStatusCode( showSnackBarResponseByStatusCode(
@@ -334,6 +333,7 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
.notification_delete_group_success, .notification_delete_group_success,
appLocalization(context) appLocalization(context)
.notification_delete_group_failed); .notification_delete_group_failed);
});
}, },
child: Text( child: Text(
appLocalization(context) appLocalization(context)
@@ -429,7 +429,7 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
} }
} }
return Expanded( return SafeArea(
child: Center( child: Center(
child: devices.isEmpty child: devices.isEmpty
? Center( ? Center(
@@ -461,7 +461,8 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
DeviceUtils.instance.checkStateDevice( DeviceUtils.instance.checkStateDevice(
context, devices[index].state!), context, devices[index].state!),
style: TextStyle( style: TextStyle(
color: DeviceUtils.instance.getTableRowColor(context, color: DeviceUtils.instance.getTableRowColor(
context,
devices[index].state!, devices[index].state!,
), ),
), ),
@@ -493,10 +494,7 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text('${appLocalization(context).map_result}: ', Text('${appLocalization(context).map_result}: ',
style: const TextStyle( style: context.h3),
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.black)),
Container( Container(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: IconButton( child: IconButton(
@@ -526,7 +524,7 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
String alias = aliasController.text; String alias = aliasController.text;
await detailGroupBloc.updateDeviceNameInGroup( await detailGroupBloc.updateDeviceNameInGroup(
context, device.thingId!, alias); context, device.thingId!, alias);
await detailGroupBloc.getGroupDetail(widget.group); await detailGroupBloc.getGroupDetail(context, widget.group);
}, },
child: Text(appLocalization(context).confirm_button_content)), child: Text(appLocalization(context).confirm_button_content)),
) )

View File

@@ -1,165 +1,125 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import '../../../product/constant/enums/app_route_enums.dart'; import '../../../product/constant/enums/app_route_enums.dart';
import 'groups_model.dart'; import 'groups_model.dart';
import 'groups_widget.dart';
import '../../../bloc/inter_family_bloc.dart'; import '../../../bloc/inter_family_bloc.dart';
import '../inter_family_widget.dart'; import '../inter_family_widget.dart';
import '../../../product/base/bloc/base_bloc.dart';
import '../../../product/constant/app/app_constants.dart'; import '../../../product/constant/app/app_constants.dart';
import '../../../product/constant/icon/icon_constants.dart'; import '../../../product/constant/icon/icon_constants.dart';
import '../../../product/extension/context_extension.dart'; import '../../../product/extension/context_extension.dart';
import '../../../product/services/language_services.dart'; import '../../../product/services/language_services.dart';
import 'groups_widget.dart'; /// Stateless widget that renders a provided list of groups. The parent
/// screen owns fetching/updating the list; this widget only displays it and
class GroupsScreen extends StatefulWidget { /// forwards actions to the provided [InterFamilyBloc].
const GroupsScreen({super.key, required this.role}); class GroupsScreen extends StatelessWidget {
const GroupsScreen(
{super.key,
required this.role,
required this.groups,
required this.interFamilyBloc});
final String role; final String role;
final List<Group> groups;
@override final InterFamilyBloc interFamilyBloc;
State<GroupsScreen> createState() => _GroupsScreenState();
}
class _GroupsScreenState extends State<GroupsScreen> {
late InterFamilyBloc interFamilyBloc;
Timer? getAllGroupsTimer;
@override
void initState() {
super.initState();
interFamilyBloc = BlocProvider.of(context);
const duration = Duration(seconds: 10);
getAllGroupsTimer = Timer.periodic(
duration,
(Timer t) => interFamilyBloc.getAllGroup(widget.role),
);
}
@override
void dispose() {
getAllGroupsTimer?.cancel();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (widget.role == ApplicationConstants.OWNER_GROUP || if (role != ApplicationConstants.OWNER_GROUP &&
widget.role == ApplicationConstants.PARTICIPANT_GROUP) { role != ApplicationConstants.PARTICIPANT_GROUP) {
interFamilyBloc.getAllGroup(widget.role); return const SizedBox.shrink();
return StreamBuilder<List<Group>>( }
stream: interFamilyBloc.streamCurrentGroups,
builder: (context, groupsSnapshot) { if (groups.isEmpty) {
return Scaffold( return Center(child: Text(appLocalization(context).dont_have_group));
body: groupsSnapshot.data?.isEmpty ?? true }
? const Center(
child: CircularProgressIndicator(), return ListView.builder(
) itemCount: groups.length,
: ListView.builder(
itemCount: groupsSnapshot.data!.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final group = groups[index];
return ListTile( return ListTile(
onTap: () { onTap: () => context.pushNamed(AppRoutes.GROUP_DETAIL.name,
context.pushNamed(AppRoutes.GROUP_DETAIL.name, pathParameters: {"groupId": group.id!}, extra: role),
pathParameters: {"groupId": groupsSnapshot.data![index].id!},
extra: widget.role);
},
leading: IconConstants.instance.getMaterialIcon(Icons.diversity_2), leading: IconConstants.instance.getMaterialIcon(Icons.diversity_2),
title: Text( title: Text(group.name ?? '',
groupsSnapshot.data![index].name ?? '', style: const TextStyle(fontWeight: FontWeight.bold)),
style: const TextStyle(fontWeight: FontWeight.bold), subtitle: Text(group.description ?? ''),
), trailing: role == ApplicationConstants.OWNER_GROUP
subtitle: Text(groupsSnapshot.data![index].description ?? ""), ? _ownerPopupMenu(group, context)
trailing: widget.role == ApplicationConstants.OWNER_GROUP : null,
? PopupMenuButton( );
},
);
}
Widget _ownerPopupMenu(Group group, BuildContext context) {
return PopupMenuButton<int>(
shape: const RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only( borderRadius: BorderRadius.all(Radius.circular(8.0))),
bottomLeft: Radius.circular(8.0),
bottomRight: Radius.circular(8.0),
topLeft: Radius.circular(8.0),
topRight: Radius.circular(8.0),
),
),
itemBuilder: (ctx) => [ itemBuilder: (ctx) => [
_buildPopupMenuItem(groupsSnapshot.data![index], context, _buildPopupMenuItem(group, context,
appLocalization(context).share_group_title, Icons.share, 4), appLocalization(context).share_group_title, Icons.share, 4),
_buildPopupMenuItem( _buildPopupMenuItem(
groupsSnapshot.data![index], group,
context, context,
appLocalization(context).change_group_infomation_title, appLocalization(context).change_group_infomation_title,
Icons.settings_backup_restore, Icons.settings_backup_restore,
2), 2),
_buildPopupMenuItem( _buildPopupMenuItem(
groupsSnapshot.data![index], group,
context, context,
appLocalization(context).delete_group_title, appLocalization(context).delete_group_title,
Icons.delete_forever_rounded, Icons.delete_forever_rounded,
3), 3),
], ],
icon: const Icon(Icons.more_horiz), icon: const Icon(Icons.more_horiz),
)
: const SizedBox.shrink(),
); );
},
),
);
},
);
} else {
return const SizedBox.shrink();
}
} }
PopupMenuItem _buildPopupMenuItem( PopupMenuItem<int> _buildPopupMenuItem(Group group, BuildContext context,
Group group, BuildContext context, String title, IconData iconData, int position) { String title, IconData iconData, int value) {
return PopupMenuItem( return PopupMenuItem<int>(
value: value,
child: Row(children: [
Icon(iconData, color: Colors.black),
const SizedBox(width: 10),
Text(title)
]),
onTap: () { onTap: () {
Future.delayed(context.lowDuration, () {
if (title == appLocalization(context).share_group_title) { if (title == appLocalization(context).share_group_title) {
Future.delayed(context.lowDuration, () {
shareGroup(context, group); shareGroup(context, group);
}); } else if (title ==
} else if (title == appLocalization(context).change_group_infomation_title) { appLocalization(context).change_group_infomation_title) {
Future.delayed(context.lowDuration, () {
createOrJoinGroupDialog( createOrJoinGroupDialog(
context, context,
interFamilyBloc, interFamilyBloc,
widget.role, role,
appLocalization(context).change_group_infomation_content, appLocalization(context).change_group_infomation_content,
appLocalization(context).group_name_title, appLocalization(context).group_name_title,
group.name!, group.name ?? '',
false, false,
group.id!, group.id ?? '',
appLocalization(context).description_group, appLocalization(context).description_group,
group.description ?? ""); group.description ?? '',
}); );
} else if (title == appLocalization(context).delete_group_title) { } else if (title == appLocalization(context).delete_group_title) {
Future.delayed(context.lowDuration, () {
showActionDialog( showActionDialog(
context, context,
widget.role, role,
interFamilyBloc, interFamilyBloc,
appLocalization(context).delete_group_title, appLocalization(context).delete_group_title,
appLocalization(context).delete_group_content, appLocalization(context).delete_group_content,
group); group,
);
}
}); });
} else {}
}, },
value: position,
child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(
iconData,
color: Colors.black,
),
const SizedBox(width: 10),
Text(title),
],
),
); );
} }
} }

View File

@@ -1,10 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:qr_flutter/qr_flutter.dart'; import 'package:qr_flutter/qr_flutter.dart';
import '../../../bloc/inter_family_bloc.dart'; import '../../../bloc/inter_family_bloc.dart';
import '../../../product/constant/icon/icon_constants.dart'; import '../../../product/constant/icon/icon_constants.dart';
import '../../../product/extension/context_extension.dart'; import '../../../product/extension/context_extension.dart';
import '../../../product/services/language_services.dart'; import '../../../product/services/language_services.dart';
import 'groups_model.dart'; import 'groups_model.dart';
shareGroup(BuildContext context, Group group) { shareGroup(BuildContext context, Group group) {
@@ -80,7 +80,8 @@ showActionDialog(
if (dialogTitle == appLocalization(context).delete_group_title) { if (dialogTitle == appLocalization(context).delete_group_title) {
Navigator.of(dialogContext).pop(); Navigator.of(dialogContext).pop();
await interFamilyBloc.deleteGroup(context, group.id!); await interFamilyBloc.deleteGroup(context, group.id!);
interFamilyBloc.getAllGroup(role); // ignore: use_build_context_synchronously
interFamilyBloc.getAllGroup(context,role);
} else {} } else {}
}, },
child: Text( child: Text(

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'groups/groups_screen.dart'; import 'groups/groups_screen.dart';
import 'groups/groups_model.dart';
import '../../bloc/inter_family_bloc.dart'; import '../../bloc/inter_family_bloc.dart';
import 'inter_family_widget.dart'; import 'inter_family_widget.dart';
import '../../product/base/bloc/base_bloc.dart'; import '../../product/base/bloc/base_bloc.dart';
@@ -25,20 +26,29 @@ class _InterFamilyScreenState extends State<InterFamilyScreen> {
void initState() { void initState() {
super.initState(); super.initState();
interFamilyBloc = BlocProvider.of(context); interFamilyBloc = BlocProvider.of(context);
// fetch initial groups for the default selected tab
WidgetsBinding.instance.addPostFrameCallback((_) {
interFamilyBloc.getAllGroup(
context,
_selectedIndex == 0
? ApplicationConstants.OWNER_GROUP
: ApplicationConstants.PARTICIPANT_GROUP);
});
} }
final _widgetOptions = <Widget>[ List<Group> ownerGroups = [];
BlocProvider( List<Group> participantGroups = [];
blocBuilder: () => InterFamilyBloc(),
child: const GroupsScreen( List<Widget> get _widgetOptions => [
GroupsScreen(
role: ApplicationConstants.OWNER_GROUP, role: ApplicationConstants.OWNER_GROUP,
groups: ownerGroups,
interFamilyBloc: interFamilyBloc,
), ),
), GroupsScreen(
BlocProvider(
blocBuilder: () => InterFamilyBloc(),
child: const GroupsScreen(
role: ApplicationConstants.PARTICIPANT_GROUP, role: ApplicationConstants.PARTICIPANT_GROUP,
), groups: participantGroups,
interFamilyBloc: interFamilyBloc,
), ),
]; ];
@@ -49,6 +59,20 @@ class _InterFamilyScreenState extends State<InterFamilyScreen> {
stream: interFamilyBloc.streamSelectedScreen, stream: interFamilyBloc.streamSelectedScreen,
initialData: _selectedIndex, initialData: _selectedIndex,
builder: (context, selectSnapshot) { builder: (context, selectSnapshot) {
// subscribe to groups stream and update local lists so child widgets render instantly
return StreamBuilder<List<Group>>(
stream: interFamilyBloc.streamCurrentGroups,
builder: (context, groupsSnapshot) {
if (groupsSnapshot.hasData) {
final all = groupsSnapshot.data!;
ownerGroups = all
.where((g) => g.isOwner == true && g.visibility == 'PUBLIC')
.toList();
participantGroups = all
.where((g) => g.isOwner == null && g.visibility == 'PUBLIC')
.toList();
}
// build UI below
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
actions: [ actions: [
@@ -58,35 +82,32 @@ class _InterFamilyScreenState extends State<InterFamilyScreen> {
createOrJoinGroupDialog( createOrJoinGroupDialog(
context, context,
interFamilyBloc, interFamilyBloc,
selectSnapshot.data! == 0 ApplicationConstants.OWNER_GROUP,
? ApplicationConstants.OWNER_GROUP
: ApplicationConstants.PARTICIPANT_GROUP,
appLocalization(context).add_new_group, appLocalization(context).add_new_group,
appLocalization(context).group_name_title, appLocalization(context).group_name_title,
"", "",
false, false,
"", "",
"", "",
""); "",
);
} else { } else {
createOrJoinGroupDialog( createOrJoinGroupDialog(
context, context,
interFamilyBloc, interFamilyBloc,
selectSnapshot.data! == 0 ApplicationConstants.PARTICIPANT_GROUP,
? ApplicationConstants.OWNER_GROUP
: ApplicationConstants.PARTICIPANT_GROUP,
appLocalization(context).join_group, appLocalization(context).join_group,
appLocalization(context).group_id_title, appLocalization(context).group_id_title,
'', '',
true, true,
"", "",
appLocalization(context).group_name_title, appLocalization(context).group_name_title,
""); "",
);
} }
}, },
style: ElevatedButton.styleFrom( style:
shape: const CircleBorder(), ElevatedButton.styleFrom(shape: const CircleBorder()),
),
child: IconConstants.instance.getMaterialIcon(Icons.add), child: IconConstants.instance.getMaterialIcon(Icons.add),
), ),
], ],
@@ -111,7 +132,7 @@ class _InterFamilyScreenState extends State<InterFamilyScreen> {
), ),
), ),
drawer: Drawer( drawer: Drawer(
width: context.dynamicWidth(0.4), width: context.dynamicWidth(0.6),
child: ListView( child: ListView(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
children: [ children: [
@@ -142,6 +163,8 @@ class _InterFamilyScreenState extends State<InterFamilyScreen> {
); );
}, },
); );
},
);
} }
void checkTitle(int index) { void checkTitle(int index) {
@@ -159,5 +182,11 @@ class _InterFamilyScreenState extends State<InterFamilyScreen> {
interFamilyBloc.sinkSelectedScreen.add(_selectedIndex); interFamilyBloc.sinkSelectedScreen.add(_selectedIndex);
isLoading = false; isLoading = false;
interFamilyBloc.sinkIsLoading.add(isLoading); interFamilyBloc.sinkIsLoading.add(isLoading);
// fetch groups for the selected tab immediately
interFamilyBloc.getAllGroup(
context,
_selectedIndex == 0
? ApplicationConstants.OWNER_GROUP
: ApplicationConstants.PARTICIPANT_GROUP);
} }
} }

View File

@@ -122,7 +122,7 @@ createOrJoinGroupDialog(
try { try {
await interFamilyBloc.createGroup( await interFamilyBloc.createGroup(
context, groupName, description); context, groupName, description);
interFamilyBloc.getAllGroup(role); interFamilyBloc.getAllGroup(context, role);
Navigator.of(dialogContext).pop(); Navigator.of(dialogContext).pop();
} catch (e) { } catch (e) {
// log("Lỗi khi tạo nhóm: $e"); // log("Lỗi khi tạo nhóm: $e");
@@ -131,9 +131,9 @@ createOrJoinGroupDialog(
appLocalization(context) appLocalization(context)
.change_group_infomation_content) { .change_group_infomation_content) {
try { try {
await interFamilyBloc.changeGroupInfomation( await interFamilyBloc.changeGroupInformation(
context, groupID, groupName, description); context, groupID, groupName, description);
interFamilyBloc.getAllGroup(role); interFamilyBloc.getAllGroup(context, role);
Navigator.of(dialogContext).pop(); Navigator.of(dialogContext).pop();
} catch (e) { } catch (e) {
// log("Lỗi khi sửa nhóm: $e"); // log("Lỗi khi sửa nhóm: $e");

View File

@@ -1,22 +1,22 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'dart:developer'; import 'dart:developer';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:badges/badges.dart' as badges; import 'package:badges/badges.dart' as badges;
import 'package:persistent_bottom_nav_bar/persistent_bottom_nav_bar.dart'; import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
import '../../product/permission/notification_permission.dart';
import '../../product/shared/shared_snack_bar.dart';
import '../../product/services/notification_services.dart'; import '../../product/services/notification_services.dart';
import '../../product/utils/permission_handler.dart';
import '../../product/permission/notification_permission.dart';
import '../settings/profile/profile_model.dart'; import '../settings/profile/profile_model.dart';
import '../../product/extension/context_extension.dart'; import '../../product/extension/context_extension.dart';
import '../../bloc/home_bloc.dart'; import '../../bloc/home_bloc.dart';
import '../../product/constant/app/app_constants.dart'; import '../../product/constant/app/app_constants.dart';
import '../../product/constant/enums/app_route_enums.dart'; import '../../product/constant/enums/app_route_enums.dart';
import '../../product/permission/location_permission.dart';
import '../../product/services/theme_services.dart'; import '../../product/services/theme_services.dart';
import '../../bloc/devices_manager_bloc.dart'; import '../../bloc/devices_manager_bloc.dart';
import '../devices/devices_manager_screen.dart'; import '../devices/devices_manager_screen.dart';
@@ -44,20 +44,8 @@ class MainScreen extends StatefulWidget {
State<MainScreen> createState() => _MainScreenState(); State<MainScreen> createState() => _MainScreenState();
} }
PersistentTabController controller = PersistentTabController(initialIndex: 0);
@pragma('vm:entry-point')
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
log("Full background message payload: ${message.toMap()}");
await Firebase.initializeApp();
final notificationServices = NotificationServices();
await notificationServices.initLocalNotifications(controller);
await notificationServices.showNotification(message);
log("Background message handled: ${message.data['title']}");
}
class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver { class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
APIServices apiServices = APIServices(); APIServices apiServices = APIServices();
final NotificationServices notificationServices = NotificationServices(); final NotificationServices notificationServices = NotificationServices();
late MainBloc mainBloc; late MainBloc mainBloc;
@@ -86,36 +74,34 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
} }
mainBloc.sinkIsVNIcon.add(isVN); mainBloc.sinkIsVNIcon.add(isVN);
mainBloc.sinkThemeMode.add(isLight); mainBloc.sinkThemeMode.add(isLight);
LocationPermissionRequest.instance.checkLocationPermission(context); checkAndRequestPermission();
NotificationPermission.instance.checkNotificationPermission(context); NotificationServices.requestNotificationPermission();
// NotificationPermission.instance.checkNotificationPermission(context);
// bool? notificationStatus = await NotificationPermission.instance.requestNotificationPermission();
// if(notificationStatus == false){
// showNoIconTopSnackBar(context, "Yêu cầu quyền thông báo không thành công", Colors.orange, Colors.white12);
// }
} }
@override @override
void initState() { void initState() {
super.initState(); super.initState();
mainBloc = BlocProvider.of(context); mainBloc = BlocProvider.of(context);
mainBloc.getFCMTokenAndPresentations();
WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addObserver(this);
initialCheck(); initialCheck();
getBellNotification(); getBellNotification();
mainBloc.getUserProfile(); mainBloc.getUserProfile(context);
notificationServices.initLocalNotifications(controller);
notificationServices.firebaseInit(context);
NotificationServices().setupInteractMessage(controller);
}
@override FirebaseMessaging.instance.onTokenRefresh.listen((newToken) {
void didChangeAppLifecycleState(AppLifecycleState state) { log("New FCM Token: $newToken");
super.didChangeAppLifecycleState(state); // Gửi token mới lên server
if (state == AppLifecycleState.inactive) { mainBloc.sendNotificationToken(newToken);
log("App Inactive"); });
} else if (state == AppLifecycleState.resumed) {
log("App Resumed"); // notificationServices.initLocalNotifications(controller);
LocationPermissionRequest.instance.checkLocationPermission(context); // notificationServices.firebaseInit(context);
} else if (state == AppLifecycleState.paused) { // NotificationServices().setupInteractMessage(controller);
log("App paused");
} else if (state == AppLifecycleState.detached) {
log("App detached");
}
} }
@override @override
@@ -124,87 +110,75 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
WidgetsBinding.instance.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
} }
@override
List<PersistentBottomNavBarItem> _navBarsItems() { Widget build(BuildContext context) {
return [ List<PersistentTabConfig> tabs = [
PersistentBottomNavBarItem( PersistentTabConfig(
icon: IconConstants.instance.getMaterialIcon(Icons.home), screen: BlocProvider(
title: appLocalization(context).home_page_destination,
activeColorPrimary: Colors.blue,
inactiveColorPrimary: Colors.grey,
inactiveIcon:
IconConstants.instance.getMaterialIcon(Icons.home_outlined),
),
PersistentBottomNavBarItem(
icon: IconConstants.instance.getMaterialIcon(Icons.settings),
title: appLocalization(context).manager_page_destination,
activeColorPrimary: Colors.blue,
inactiveColorPrimary: Colors.grey,
inactiveIcon:
IconConstants.instance.getMaterialIcon(Icons.settings_outlined),
),
PersistentBottomNavBarItem(
icon: IconConstants.instance.getMaterialIcon(Icons.location_on),
title: appLocalization(context).map_page_destination,
activeColorPrimary: Colors.blue,
inactiveColorPrimary: Colors.grey,
inactiveIcon:
IconConstants.instance.getMaterialIcon(Icons.location_on_outlined),
),
PersistentBottomNavBarItem(
icon: IconConstants.instance.getMaterialIcon(Icons.history),
title: appLocalization(context).history_page_destination,
activeColorPrimary: Colors.blue,
inactiveColorPrimary: Colors.grey,
inactiveIcon:
IconConstants.instance.getMaterialIcon(Icons.history_outlined),
),
PersistentBottomNavBarItem(
icon: IconConstants.instance.getMaterialIcon(Icons.group),
title: appLocalization(context).group_page_destination,
activeColorPrimary: Colors.blue,
inactiveColorPrimary: Colors.grey,
inactiveIcon:
IconConstants.instance.getMaterialIcon(Icons.group_outlined),
),
// PersistentBottomNavBarItem(
// icon: IconConstants.instance
// .getMaterialIcon(Icons.notifications_outlined),
// title: appLocalization(context).notification,
// activeColorPrimary: Colors.blue,
// inactiveColorPrimary: Colors.grey,
// inactiveIcon: IconConstants.instance
// .getMaterialIcon(Icons.notifications_outlined),
// ),
];
}
List<Widget> _buildScreens() {
return [
BlocProvider(
child: const HomeScreen(), child: const HomeScreen(),
blocBuilder: () => HomeBloc(), blocBuilder: () => HomeBloc(),
), ),
BlocProvider( item: ItemConfig(
icon: IconConstants.instance.getMaterialIcon(Icons.home),
title: appLocalization(context).home_page_destination,
inactiveIcon:
IconConstants.instance.getMaterialIcon(Icons.home_outlined),
iconSize: 30,
),
),
PersistentTabConfig(
screen: BlocProvider(
child: const DevicesManagerScreen(), child: const DevicesManagerScreen(),
blocBuilder: () => DevicesManagerBloc()), blocBuilder: () => DevicesManagerBloc(),
BlocProvider( ),
item: ItemConfig(
icon: IconConstants.instance.getMaterialIcon(Icons.settings),
title: appLocalization(context).manager_page_destination,
inactiveIcon:
IconConstants.instance.getMaterialIcon(Icons.settings_outlined),
iconSize: 30,
),
),
PersistentTabConfig(
screen: BlocProvider(
child: const MapScreen(), child: const MapScreen(),
blocBuilder: () => MapBloc(), blocBuilder: () => MapBloc(),
), ),
BlocProvider( item: ItemConfig(
icon: IconConstants.instance.getMaterialIcon(Icons.location_on),
title: appLocalization(context).map_page_destination,
inactiveIcon: IconConstants.instance
.getMaterialIcon(Icons.location_on_outlined),
iconSize: 30,
),
),
PersistentTabConfig(
screen: BlocProvider(
child: const DeviceLogsScreen(), child: const DeviceLogsScreen(),
blocBuilder: () => DeviceLogsBloc(), blocBuilder: () => DeviceLogsBloc(),
), ),
BlocProvider( item: ItemConfig(
icon: IconConstants.instance.getMaterialIcon(Icons.history),
title: appLocalization(context).history_page_destination,
inactiveIcon:
IconConstants.instance.getMaterialIcon(Icons.history_outlined),
iconSize: 30,
),
),
PersistentTabConfig(
screen: BlocProvider(
child: const InterFamilyScreen(), child: const InterFamilyScreen(),
blocBuilder: () => InterFamilyBloc(), blocBuilder: () => InterFamilyBloc(),
), ),
item: ItemConfig(
icon: IconConstants.instance.getMaterialIcon(Icons.group),
title: appLocalization(context).group_page_destination,
inactiveIcon:
IconConstants.instance.getMaterialIcon(Icons.group_outlined),
iconSize: 30,
),
),
]; ];
}
@override
Widget build(BuildContext context) {
return StreamBuilder<bool>( return StreamBuilder<bool>(
stream: mainBloc.streamThemeMode, stream: mainBloc.streamThemeMode,
initialData: isLight, initialData: isLight,
@@ -220,7 +194,7 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
SizedBox( SizedBox(
width: context.lowValue, width: context.lowValue,
), ),
Text(userSnapshot.data?.name ?? "") Flexible(child: Text(userSnapshot.data?.name ?? ""))
], ],
); );
}), }),
@@ -353,41 +327,38 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
], ],
), ),
body: PersistentTabView( body: PersistentTabView(
context, stateManagement: false,
controller: controller, controller: controller,
screens: _buildScreens(), tabs: tabs,
items: _navBarsItems(), navBarBuilder: (navBarConfig) => Style6BottomNavBar(
handleAndroidBackButtonPress: true, navBarConfig: navBarConfig,
resizeToAvoidBottomInset: true, navBarDecoration: NavBarDecoration(
stateManagement: true, color: themeModeSnapshot.data! ? Colors.white : Colors.black,
borderRadius: BorderRadius.circular(context.mediumValue),
padding: const EdgeInsets.all(10)),
),
backgroundColor: backgroundColor:
themeModeSnapshot.data! ? Colors.white : Colors.black, themeModeSnapshot.data! ? Colors.white : Colors.black,
decoration: NavBarDecoration( navBarOverlap: const NavBarOverlap.none(),
borderRadius: BorderRadius.circular(30.0), // margin: EdgeInsets.only(
colorBehindNavBar: // left: context.lowValue,
themeModeSnapshot.data! ? Colors.white : Colors.black, // bottom: context.dynamicHeight(0.02),
), // right: context.lowValue),
animationSettings: const NavBarAnimationSettings( screenTransitionAnimation: const ScreenTransitionAnimation(
navBarItemAnimation: ItemAnimationSettings(
duration: Duration(milliseconds: 200),
curve: Curves.bounceInOut,
),
screenTransitionAnimation: ScreenTransitionAnimationSettings(
animateTabTransition: true,
curve: Curves.bounceInOut, curve: Curves.bounceInOut,
duration: Duration(milliseconds: 200), duration: Duration(milliseconds: 200),
), ),
), ),
navBarStyle: NavBarStyle.style13,
),
); );
}, },
); );
} }
Future<void> getBellNotification() async { Future<void> getBellNotification() async {
await apiServices.execute(context, () async {
bell = await apiServices.getBellNotifications("0", "20"); bell = await apiServices.getBellNotifications("0", "20");
mainBloc.bellBloc.add(bell); mainBloc.bellBloc.add(bell);
});
} }
bool checkStatus(List<BellItems> bells) { bool checkStatus(List<BellItems> bells) {

View File

@@ -1,21 +1,20 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart'; import 'package:geolocator/geolocator.dart';
import 'package:google_maps_cluster_manager_2/google_maps_cluster_manager_2.dart'; import 'package:google_maps_cluster_manager_2/google_maps_cluster_manager_2.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart' import 'package:google_maps_flutter/google_maps_flutter.dart'
hide ClusterManager, Cluster; hide ClusterManager, Cluster;
import 'package:sfm_app/feature/devices/device_model.dart';
import 'package:sfm_app/bloc/map_bloc.dart'; import '../../bloc/map_bloc.dart';
import 'package:sfm_app/feature/map/widget/on_tap_marker_widget.dart'; import '../../product/base/bloc/base_bloc.dart';
import 'package:sfm_app/product/base/bloc/base_bloc.dart';
import 'package:sfm_app/product/constant/icon/icon_constants.dart';
import 'package:sfm_app/product/permission/location_permission.dart';
import 'package:sfm_app/product/services/api_services.dart';
import '../../product/constant/enums/app_theme_enums.dart'; import '../../product/constant/enums/app_theme_enums.dart';
import '../../product/constant/icon/icon_constants.dart';
import '../../product/services/api_services.dart';
import '../../product/utils/permission_handler.dart';
import '../devices/device_model.dart';
import 'widget/on_tap_marker_widget.dart';
class MapScreen extends StatefulWidget { class MapScreen extends StatefulWidget {
const MapScreen({super.key}); const MapScreen({super.key});
@@ -37,7 +36,7 @@ class _MapScreenState extends State<MapScreen> with WidgetsBindingObserver {
APIServices apiServices = APIServices(); APIServices apiServices = APIServices();
final streamController = StreamController<GoogleMapController>.broadcast(); final streamController = StreamController<GoogleMapController>.broadcast();
List<Device> devices = []; List<Device> devices = [];
Completer<GoogleMapController> _controller = Completer(); final Completer<GoogleMapController> _controller = Completer();
List<String> imageAssets = [ List<String> imageAssets = [
IconConstants.instance.getIcon("normal_icon"), IconConstants.instance.getIcon("normal_icon"),
IconConstants.instance.getIcon("offline_icon"), IconConstants.instance.getIcon("offline_icon"),
@@ -57,6 +56,7 @@ class _MapScreenState extends State<MapScreen> with WidgetsBindingObserver {
Timer? checkThemeTimer; Timer? checkThemeTimer;
Timer? getMarker; Timer? getMarker;
String themeMode = ''; String themeMode = '';
bool _isDisposed = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@@ -72,19 +72,21 @@ class _MapScreenState extends State<MapScreen> with WidgetsBindingObserver {
@override @override
void dispose() { void dispose() {
super.dispose(); _isDisposed = true;
checkThemeTimer?.cancel(); checkThemeTimer?.cancel();
getMarker?.cancel(); getMarker?.cancel();
_controller = Completer();
streamController.close(); streamController.close();
super.dispose();
} }
void onMapCreated(GoogleMapController controller) { void onMapCreated(GoogleMapController controller) {
if (!_isDisposed && !streamController.isClosed) {
_controller.complete(controller); _controller.complete(controller);
streamController.add(controller); streamController.add(controller);
clusterManager.setMapId(controller.mapId); clusterManager.setMapId(controller.mapId);
checkTheme(); checkTheme();
} }
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -116,12 +118,15 @@ class _MapScreenState extends State<MapScreen> with WidgetsBindingObserver {
clusterManager.updateMap(); clusterManager.updateMap();
}, },
polylines: { polylines: {
if (polylinesSnapshot.data != null &&
polylinesSnapshot.data!.isNotEmpty) ...[
Polyline( Polyline(
polylineId: const PolylineId('router'), polylineId: const PolylineId('router'),
points: polylinesSnapshot.data ?? [], points: polylinesSnapshot.data!,
color: Colors.deepPurpleAccent, color: Colors.deepPurpleAccent,
width: 8, width: 8,
), ),
]
}, },
style: mapThemeSnapshot.data, style: mapThemeSnapshot.data,
); );
@@ -169,11 +174,11 @@ class _MapScreenState extends State<MapScreen> with WidgetsBindingObserver {
return ClusterManager<Device>( return ClusterManager<Device>(
devices, devices,
_updateMarkers, _updateMarkers,
markerBuilder: _getmarkerBuilder(), markerBuilder: _getMarkerBuilder(),
); );
} }
Future<Marker> Function(Cluster<Device>) _getmarkerBuilder() => Future<Marker> Function(Cluster<Device>) _getMarkerBuilder() =>
(cluster) async { (cluster) async {
return Marker( return Marker(
markerId: MarkerId( markerId: MarkerId(
@@ -181,8 +186,9 @@ class _MapScreenState extends State<MapScreen> with WidgetsBindingObserver {
), ),
position: cluster.location, position: cluster.location,
onTap: () async { onTap: () async {
bool check = await checkLocationPermission(context); LocationPermission permission = await checkAndRequestPermission();
if (check == true) { if (permission == LocationPermission.whileInUse ||
permission == LocationPermission.always) {
Position position = await Geolocator.getCurrentPosition(); Position position = await Geolocator.getCurrentPosition();
onTapMarker( onTapMarker(
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
@@ -270,23 +276,12 @@ class _MapScreenState extends State<MapScreen> with WidgetsBindingObserver {
} }
void getAllMarkers() async { void getAllMarkers() async {
String response = await apiServices.getOwnerDevices(); await apiServices.execute(context, () async {
if (response != "") {
final data = jsonDecode(response);
List<dynamic> result = data['items'];
if (result.isNotEmpty) {
devices.clear(); devices.clear();
final devicesList = Device.fromJsonDynamicList(result); final devicesList = await apiServices.getOwnerDevices();
for (var device in devicesList) { for (var device in devicesList) {
devices.add(device); devices.add(device);
} }
} else {} });
}
}
Future<bool> checkLocationPermission(context) async {
bool check = await LocationPermissionRequest.instance
.checkLocationPermission(context);
return check;
} }
} }

View File

@@ -3,6 +3,7 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'show_direction_widget.dart'; import 'show_direction_widget.dart';
import 'show_nearest_place.dart'; import 'show_nearest_place.dart';
import '../../../product/constant/icon/icon_constants.dart'; import '../../../product/constant/icon/icon_constants.dart';
@@ -11,7 +12,6 @@ import '../../../bloc/map_bloc.dart';
import '../../../product/services/api_services.dart'; import '../../../product/services/api_services.dart';
import '../../../product/services/language_services.dart'; import '../../../product/services/language_services.dart';
import '../../../product/utils/device_utils.dart'; import '../../../product/utils/device_utils.dart';
import '../../devices/device_model.dart'; import '../../devices/device_model.dart';
onTapMarker( onTapMarker(
@@ -72,14 +72,14 @@ onTapMarker(
double.parse(device.settings!.latitude!), double.parse(device.settings!.latitude!),
double.parse(device.settings!.longitude!), double.parse(device.settings!.longitude!),
); );
mapBloc.findTheWay( await mapBloc.findTheWay(
context, context,
controller, controller,
myLocation, myLocation,
destination, destination,
); );
String deviceLocations = await DeviceUtils.instance String deviceLocations = await DeviceUtils.instance
.getFullDeviceLocation(context, device.areaPath!); .getFullDeviceLocation(context, device.areaPath!,device.name);
String yourLocation = String yourLocation =
appLocalization(context).map_your_location; appLocalization(context).map_your_location;
showDirections( showDirections(

View File

@@ -1,12 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:maps_launcher/maps_launcher.dart'; import 'package:maps_launcher/maps_launcher.dart';
import '../../../product/constant/icon/icon_constants.dart'; import '../../../product/constant/icon/icon_constants.dart';
import '../../../product/extension/context_extension.dart'; import '../../../product/extension/context_extension.dart';
import '../../../product/services/language_services.dart'; import '../../../product/services/language_services.dart';
import '../../../bloc/map_bloc.dart'; import '../../../bloc/map_bloc.dart';
showDirections( showDirections(
@@ -16,16 +15,14 @@ showDirections(
MapBloc mapBloc, MapBloc mapBloc,
String originalName, String originalName,
String destinationLocation, String destinationLocation,
double devicelat, double deviceLat,
double devicelng, double deviceLng,
) { ) {
TextEditingController originController = TextEditingController originController = TextEditingController(text: originalName);
TextEditingController(text: originalName); TextEditingController destinationController = TextEditingController(text: destinationLocation);
TextEditingController destinationController =
TextEditingController(text: destinationLocation);
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
backgroundColor: Colors.transparent, backgroundColor: Theme.of(context).colorScheme.surface,
// dismissDirection: DismissDirection.none, // dismissDirection: DismissDirection.none,
duration: const Duration(minutes: 5), duration: const Duration(minutes: 5),
content: Column( content: Column(
@@ -38,21 +35,20 @@ showDirections(
children: [ children: [
Text( Text(
appLocalization(context).map_show_direction, appLocalization(context).map_show_direction,
style: context.titleLargeTextStyle, style: context.responsiveBodyLargeWithBold,
), ),
Container( Container(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: IconButton.outlined( child: IconButton.outlined(
onPressed: () async { onPressed: () async {
mapBloc.sinkPolylines.add([]);
markers.clear();
await mapBloc.updateCameraPosition( await mapBloc.updateCameraPosition(
controller, controller,
devicelat, deviceLat,
devicelng, deviceLng,
13.0, 13.0,
); );
List<LatLng> polylineCoordinates = [];
mapBloc.sinkPolylines.add(polylineCoordinates);
markers.clear();
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).hideCurrentSnackBar();
} }
@@ -103,17 +99,12 @@ showDirections(
ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).hideCurrentSnackBar();
List<LatLng> polylineCoordinates = []; List<LatLng> polylineCoordinates = [];
mapBloc.sinkPolylines.add(polylineCoordinates); mapBloc.sinkPolylines.add(polylineCoordinates);
MapsLauncher.launchCoordinates(devicelat, devicelng); MapsLauncher.launchCoordinates(deviceLat, deviceLng);
}, },
icon: IconConstants.instance icon: IconConstants.instance.getMaterialIcon(Icons.near_me_rounded),
.getMaterialIcon(Icons.near_me_rounded),
label: Text( label: Text(
appLocalization(context).map_stream, appLocalization(context).map_stream,
), ),
style: ButtonStyle(
backgroundColor:
WidgetStateProperty.all<Color>(Colors.blue[300]!),
),
), ),
], ],
), ),

View File

@@ -1,9 +1,9 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../../../bloc/map_bloc.dart'; import '../../../bloc/map_bloc.dart';
import 'show_direction_widget.dart'; import 'show_direction_widget.dart';
import '../../../product/constant/icon/icon_constants.dart'; import '../../../product/constant/icon/icon_constants.dart';
@@ -61,10 +61,7 @@ showNearPlacesSideSheet(
Center( Center(
child: Text( child: Text(
'${appLocalization(modalBottomSheetContext).map_result}: ', '${appLocalization(modalBottomSheetContext).map_result}: ',
style: const TextStyle( style: context.h3
fontSize: 20,
fontWeight: FontWeight.bold,
),
), ),
), ),
Container( Container(
@@ -118,10 +115,7 @@ showNearPlacesSideSheet(
children: [ children: [
Text( Text(
place.result!.name!, place.result!.name!,
style: const TextStyle( style: context.responsiveBodyMediumWithBold,
fontSize: 16,
fontWeight: FontWeight.w500,
),
), ),
SizedBox(height: listViewContext.lowValue), SizedBox(height: listViewContext.lowValue),
Text( Text(

View File

@@ -1,8 +1,8 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'dart:convert';
import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../product/shared/shared_snack_bar.dart'; import '../../../product/shared/shared_snack_bar.dart';
import '../../../bloc/device_notification_settings_bloc.dart'; import '../../../bloc/device_notification_settings_bloc.dart';
import 'device_notification_settings_model.dart'; import 'device_notification_settings_model.dart';
@@ -73,7 +73,7 @@ class _DeviceNotificationSettingsScreenState
hint: Text( hint: Text(
appLocalization(context) appLocalization(context)
.choose_device_dropdownButton, .choose_device_dropdownButton,
style: const TextStyle(fontSize: 14), style: context.responsiveBodySmall,
), ),
iconStyleData: const IconStyleData( iconStyleData: const IconStyleData(
icon: Icon( icon: Icon(
@@ -111,14 +111,12 @@ class _DeviceNotificationSettingsScreenState
} }
void getNotificationSetting() async { void getNotificationSetting() async {
String? response = await apiServices.getAllSettingsNotificationOfDevices(); await apiServices.execute(context, () async {
final data = jsonDecode(response);
final result = data['data'];
// log("Data ${DeviceNotificationSettings.mapFromJson(jsonDecode(data)).values.toList()}");
deviceNotifications = deviceNotifications =
DeviceNotificationSettings.mapFromJson(result).values.toList(); await apiServices.getAllSettingsNotificationOfDevices();
deviceNotificationSettingsBloc.sinkListNotifications deviceNotificationSettingsBloc.sinkListNotifications
.add(deviceNotifications); .add(deviceNotifications);
});
} }
Widget listNotificationSetting( Widget listNotificationSetting(
@@ -292,12 +290,14 @@ class _DeviceNotificationSettingsScreenState
void updateDeviceNotification(String thingID, Map<String, int> notifiSettings, void updateDeviceNotification(String thingID, Map<String, int> notifiSettings,
bool isDataChange) async { bool isDataChange) async {
await apiServices.execute(context, () async {
int statusCode = await apiServices.updateDeviceNotificationSettings( int statusCode = await apiServices.updateDeviceNotificationSettings(
thingID, notifiSettings); thingID, notifiSettings);
if (statusCode == 200) { if (statusCode == 200) {
showNoIconTopSnackBar( showNoIconTopSnackBar(
context, context,
appLocalization(context).notification_update_device_settings_success, appLocalization(context)
.notification_update_device_settings_success,
Colors.green, Colors.green,
Colors.white); Colors.white);
} else { } else {
@@ -309,5 +309,6 @@ class _DeviceNotificationSettingsScreenState
} }
isDataChange = false; isDataChange = false;
deviceNotificationSettingsBloc.sinkIsDataChange.add(isDataChange); deviceNotificationSettingsBloc.sinkIsDataChange.add(isDataChange);
});
} }
} }

View File

@@ -1,6 +1,7 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../product/shared/shared_snack_bar.dart'; import '../../../product/shared/shared_snack_bar.dart';
import '../../../product/constant/icon/icon_constants.dart'; import '../../../product/constant/icon/icon_constants.dart';
import '../../../product/services/api_services.dart'; import '../../../product/services/api_services.dart';
@@ -8,7 +9,6 @@ import '../../../bloc/settings_bloc.dart';
import '../../../product/shared/shared_input_decoration.dart'; import '../../../product/shared/shared_input_decoration.dart';
import '../../../product/extension/context_extension.dart'; import '../../../product/extension/context_extension.dart';
import '../../../product/services/language_services.dart'; import '../../../product/services/language_services.dart';
import 'profile_model.dart'; import 'profile_model.dart';
changeUserInfomation( changeUserInfomation(
@@ -38,6 +38,7 @@ changeUserInfomation(
? IconButton( ? IconButton(
onPressed: () async { onPressed: () async {
if (formKey.currentState!.validate()) { if (formKey.currentState!.validate()) {
await apiServices.execute(context,() async {
formKey.currentState!.save(); formKey.currentState!.save();
String latitude = user.latitude ?? ""; String latitude = user.latitude ?? "";
String longitude = user.longitude ?? ""; String longitude = user.longitude ?? "";
@@ -66,7 +67,44 @@ changeUserInfomation(
Colors.redAccent, Colors.redAccent,
Colors.white); Colors.white);
} }
settingsBloc.getUserProfile(context);
Navigator.pop(modalBottomSheetContext); Navigator.pop(modalBottomSheetContext);
});
// try {
// formKey.currentState!.save();
// String latitude = user.latitude ?? "";
// String longitude = user.longitude ?? "";
// Map<String, dynamic> body = {
// "name": username,
// "email": email,
// "phone": tel,
// "address": address,
// "latitude": latitude,
// "longitude": longitude
// };
// int statusCode =
// await apiServices.updateUserProfile(body);
// if (statusCode == 200) {
// showNoIconTopSnackBar(
// modalBottomSheetContext,
// appLocalization(context)
// .notification_update_profile_success,
// Colors.green,
// Colors.white);
// } else {
// showNoIconTopSnackBar(
// modalBottomSheetContext,
// appLocalization(context)
// .notification_update_profile_failed,
// Colors.redAccent,
// Colors.white);
// }
// settingsBloc.getUserProfile(context);
// Navigator.pop(modalBottomSheetContext);
// } catch (e) {
// if (!context.mounted) return;
// showErrorTopSnackBarCustom(context, e.toString());
// }
} }
}, },
icon: icon:
@@ -205,6 +243,7 @@ changeUserInfomation(
child: TextButton( child: TextButton(
onPressed: () async { onPressed: () async {
if (formKey.currentState!.validate()) { if (formKey.currentState!.validate()) {
await apiServices.execute(context,() async {
formKey.currentState!.save(); formKey.currentState!.save();
String latitude = user.latitude ?? ""; String latitude = user.latitude ?? "";
String longitude = user.longitude ?? ""; String longitude = user.longitude ?? "";
@@ -233,7 +272,9 @@ changeUserInfomation(
Colors.redAccent, Colors.redAccent,
Colors.white); Colors.white);
} }
settingsBloc.getUserProfile(context);
Navigator.pop(modalBottomSheetContext); Navigator.pop(modalBottomSheetContext);
});
} }
}, },
style: const ButtonStyle( style: const ButtonStyle(
@@ -283,6 +324,7 @@ changeUserPassword(BuildContext context, SettingsBloc settingsBloc) {
? IconButton( ? IconButton(
onPressed: () async { onPressed: () async {
if (formKey.currentState!.validate()) { if (formKey.currentState!.validate()) {
await apiServices.execute(context,() async {
formKey.currentState!.save(); formKey.currentState!.save();
Map<String, dynamic> body = { Map<String, dynamic> body = {
"password_old": oldPass, "password_old": oldPass,
@@ -306,6 +348,7 @@ changeUserPassword(BuildContext context, SettingsBloc settingsBloc) {
Colors.white); Colors.white);
} }
Navigator.pop(modalBottomSheetContext); Navigator.pop(modalBottomSheetContext);
});
} }
}, },
icon: icon:
@@ -390,6 +433,7 @@ changeUserPassword(BuildContext context, SettingsBloc settingsBloc) {
? Center( ? Center(
child: TextButton( child: TextButton(
onPressed: () async { onPressed: () async {
await apiServices.execute(context,() async {
if (formKey.currentState!.validate()) { if (formKey.currentState!.validate()) {
formKey.currentState!.save(); formKey.currentState!.save();
Map<String, dynamic> body = { Map<String, dynamic> body = {
@@ -415,6 +459,38 @@ changeUserPassword(BuildContext context, SettingsBloc settingsBloc) {
} }
Navigator.pop(modalBottomSheetContext); Navigator.pop(modalBottomSheetContext);
} }
});
// try {
// if (formKey.currentState!.validate()) {
// formKey.currentState!.save();
// Map<String, dynamic> body = {
// "password_old": oldPass,
// "password_new": newPass,
// };
// int statusCode = await apiServices
// .updateUserPassword(body);
// if (statusCode == 200) {
// showNoIconTopSnackBar(
// modalBottomSheetContext,
// appLocalization(context)
// .notification_update_password_success,
// Colors.green,
// Colors.white);
// } else {
// showNoIconTopSnackBar(
// modalBottomSheetContext,
// appLocalization(context)
// .notification_update_password_failed,
// Colors.redAccent,
// Colors.white);
// }
// Navigator.pop(modalBottomSheetContext);
// }
// } catch (e) {
// if (!context.mounted) return;
// showErrorTopSnackBarCustom(
// context, e.toString());
// }
}, },
style: const ButtonStyle( style: const ButtonStyle(
backgroundColor: backgroundColor:

View File

@@ -1,8 +1,8 @@
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import '../../product/constant/app/app_constants.dart'; import '../../product/constant/app/app_constants.dart';
import '../../product/shared/shared_loading_animation.dart';
import 'profile/profile_screen.dart'; import 'profile/profile_screen.dart';
import '../../product/constant/icon/icon_constants.dart'; import '../../product/constant/icon/icon_constants.dart';
import '../../product/extension/context_extension.dart'; import '../../product/extension/context_extension.dart';
@@ -27,7 +27,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
void initState() { void initState() {
super.initState(); super.initState();
settingsBloc = BlocProvider.of(context); settingsBloc = BlocProvider.of(context);
getUserProfile();
} }
@override @override
@@ -39,15 +38,12 @@ class _SettingsScreenState extends State<SettingsScreen> {
), ),
body: StreamBuilder<User>( body: StreamBuilder<User>(
stream: settingsBloc.streamUserProfile, stream: settingsBloc.streamUserProfile,
initialData: user,
builder: (context, userSnapshot) { builder: (context, userSnapshot) {
return userSnapshot.data?.id == "" || user.id == "" if (userSnapshot.data == null) {
? Center( settingsBloc.getUserProfile(context);
child: CircularProgressIndicator( return const SharedLoadingAnimation();
value: context.highValue, } else {
), return Column(
)
: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
@@ -60,12 +56,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
child: CircleAvatar( child: CircleAvatar(
radius: 50, radius: 50,
child: Text( child: Text(
getAvatarContent( getAvatarContent(userSnapshot.data?.username ?? ""),
userSnapshot.data?.username ?? ""), style: context.dynamicResponsiveSize(36),
style: const TextStyle(
fontSize: 35,
fontWeight: FontWeight.bold,
),
), ),
), ),
), ),
@@ -76,8 +68,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
children: [ children: [
Text( Text(
userSnapshot.data?.name ?? "User Name", userSnapshot.data?.name ?? "User Name",
style: const TextStyle( style: context.h2,
fontWeight: FontWeight.w900, fontSize: 26),
) )
], ],
), ),
@@ -89,29 +80,36 @@ class _SettingsScreenState extends State<SettingsScreen> {
cardContent( cardContent(
Icons.account_circle_rounded, Icons.account_circle_rounded,
appLocalization(context).profile_change_info, appLocalization(context).profile_change_info,
), userSnapshot.data ?? user),
SizedBox(height: context.lowValue), SizedBox(height: context.lowValue),
cardContent( cardContent(
Icons.lock_outline, Icons.lock_outline,
appLocalization(context).profile_change_pass, appLocalization(context).profile_change_pass,
), userSnapshot.data ?? user),
SizedBox(height: context.lowValue), SizedBox(height: context.lowValue),
cardContent( cardContent(
Icons.settings_outlined, Icons.settings_outlined,
appLocalization(context).profile_setting, appLocalization(context).profile_setting,
), userSnapshot.data ?? user),
SizedBox(height: context.lowValue),
cardContent(
Icons.sim_card,
appLocalization(context).profile_sim_data,
userSnapshot.data ?? user),
SizedBox(height: context.lowValue), SizedBox(height: context.lowValue),
cardContent( cardContent(
Icons.logout_outlined, Icons.logout_outlined,
appLocalization(context).log_out, appLocalization(context).log_out,
), userSnapshot.data ?? user),
], ],
); );
}), }
},
),
); );
} }
cardContent(IconData icon, String content) { cardContent(IconData icon, String content, User user) {
return GestureDetector( return GestureDetector(
onTap: () async { onTap: () async {
if (icon == Icons.account_circle_rounded) { if (icon == Icons.account_circle_rounded) {
@@ -120,7 +118,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
changeUserPassword(context, settingsBloc); changeUserPassword(context, settingsBloc);
} else if (icon == Icons.settings_outlined) { } else if (icon == Icons.settings_outlined) {
context.push(ApplicationConstants.DEVICE_NOTIFICATIONS_SETTINGS); context.push(ApplicationConstants.DEVICE_NOTIFICATIONS_SETTINGS);
} else { } else if(icon == Icons.sim_card){
context.push(ApplicationConstants.SIM_DATA_SETTINGS);
}
else {
await apiServices.logOut(context); await apiServices.logOut(context);
} }
}, },
@@ -132,7 +133,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
leading: IconConstants.instance.getMaterialIcon(icon), leading: IconConstants.instance.getMaterialIcon(icon),
title: Text( title: Text(
content, content,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), style: context.responsiveBodyMediumWithBold,
), ),
trailing: const Icon( trailing: const Icon(
Icons.arrow_forward_ios_outlined, Icons.arrow_forward_ios_outlined,
@@ -142,12 +143,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
); );
} }
void getUserProfile() async {
String data = await apiServices.getUserDetail();
user = User.fromJson(jsonDecode(data));
settingsBloc.sinkUserProfile.add(user);
}
String getAvatarContent(String username) { String getAvatarContent(String username) {
String name = ""; String name = "";
if (username.isNotEmpty) { if (username.isNotEmpty) {

View File

@@ -0,0 +1,170 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../../devices/device_model.dart';
import '../../../product/extension/context_extension.dart';
import '../../../product/services/language_services.dart';
import '../../../product/utils/device_utils.dart';
class SharedSimComponent extends StatelessWidget {
const SharedSimComponent({super.key, required this.device});
final Device device;
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
double screenHeight = MediaQuery.of(context).size.height;
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: screenWidth,
height: screenHeight / 5.5,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
gradient: getGradientColor(getMonthLeft())
),
child: Padding(
padding: context.paddingLow,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
device.name ?? "name",
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
_buildStatusChip(context,device.state ?? -1),
],
),
SizedBox(height: context.lowValue),
// Time period
Text(
"${appLocalization(context).time_title}: ${convertStartTime()} - ${convertExpireTime()}",
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
SizedBox(height: context.lowValue),
_buildDurationDisplay(context),
],
),
),
),
);
}
Widget _buildStatusChip(BuildContext context,int state) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
border: Border.all(color: Colors.white),
borderRadius: BorderRadius.circular(15),
color: Colors.white,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.circle,
color: DeviceUtils.instance.getTableRowColor(context, state),
size: 12,
),
const SizedBox(width: 5),
Text(
DeviceUtils.instance.checkStateDevice(context, state),
style: TextStyle(
color: DeviceUtils.instance.getTableRowColor(context, state),
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
],
),
);
}
Widget _buildDurationDisplay(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: [
Text(
"${getMonthLeft()}",
style: const TextStyle(
fontSize: 40,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
const SizedBox(width: 8),
Text(
appLocalization(context).sim_data_month_left_message,
style: const TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.w500,
),
),
],
);
}
String convertStartTime(){
return DateFormat('dd/MM/yyyy').format(device.createdAt!);
}
String convertExpireTime() {
final expireDate = _calculateExpireDate();
return DateFormat('dd/MM/yyyy').format(expireDate);
}
int getMonthLeft() {
final expireDate = _calculateExpireDate();
final now = DateTime.now();
int totalMonths = (expireDate.year - now.year) * 12 + (expireDate.month - now.month);
if (expireDate.day < now.day) {
totalMonths -= 1;
}
return totalMonths;
}
DateTime _calculateExpireDate() {
return DateTime(
device.createdAt!.year + 3,
device.createdAt!.month,
device.createdAt!.day,
device.createdAt!.hour,
device.createdAt!.minute,
device.createdAt!.second,
device.createdAt!.millisecond,
device.createdAt!.microsecond,
);
}
LinearGradient getGradientColor(int monthLeft){
if(monthLeft <= 3){
return const LinearGradient(
colors: [Color(0xFFff4b1f), Color(0xFFff9068)],
);
}else{
return const LinearGradient(
colors: [Color(0xFF56ab2f), Color(0xFFa8e063)],
);
}
}
}

View File

@@ -0,0 +1,55 @@
import 'package:flutter/material.dart';
import 'shared_sim_component.dart';
import '../../../product/services/language_services.dart';
import '../../../product/shared/shared_loading_animation.dart';
import '../../../bloc/sim_data_bloc.dart';
import '../../../product/base/bloc/base_bloc.dart';
class SimDataScreen extends StatefulWidget {
const SimDataScreen({super.key});
@override
State<SimDataScreen> createState() => _SimDataScreenState();
}
class _SimDataScreenState extends State<SimDataScreen> {
late SimDataBloc simDataBloc;
@override
void initState() {
super.initState();
simDataBloc = BlocProvider.of(context);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(appLocalization(context).profile_sim_data),
centerTitle: true,
),
body: StreamBuilder(
stream: simDataBloc.streamDevices,
builder: (context, devicesSnapshot) {
if (devicesSnapshot.data == null) {
simDataBloc.getOwnerDevices(context);
return const SharedLoadingAnimation();
} else if (devicesSnapshot.data!.isEmpty) {
return Center(
child: Text(appLocalization(context).main_no_data),
);
} else {
return SafeArea(
child: Column(
children: devicesSnapshot.data!.map(
(device) {
return SharedSimComponent(device: device,);
},
).toList(),
),
);
}
},
),
);
}
}

View File

@@ -1,10 +1,12 @@
import 'dart:developer'; import 'package:alarm/alarm.dart';
import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart'
import 'feature/main/main_screen.dart'; show PersistentTabController;
import 'firebase_options.dart';
import 'product/lang/l10n/app_localizations.dart';
import 'product/services/api_services.dart'; import 'product/services/api_services.dart';
import 'product/services/notification_services.dart'; import 'product/services/notification_services.dart';
import 'product/services/theme_services.dart'; import 'product/services/theme_services.dart';
@@ -13,13 +15,17 @@ import 'bloc/main_bloc.dart';
import 'product/base/bloc/base_bloc.dart'; import 'product/base/bloc/base_bloc.dart';
import 'product/constant/navigation/navigation_router.dart'; import 'product/constant/navigation/navigation_router.dart';
PersistentTabController controller = PersistentTabController(initialIndex: 0);
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(); await Firebase.initializeApp(
FirebaseMessaging options: DefaultFirebaseOptions.currentPlatform,
.onBackgroundMessage(firebaseMessagingBackgroundHandler); name: "sfm-notification");
// NotificationServices().setupInteractMessage(); FirebaseMessaging.onBackgroundMessage(
NotificationServices.firebaseMessagingBackgroundHandler);
await Alarm.init();
await Alarm.stopAll();
runApp( runApp(
BlocProvider( BlocProvider(
child: const MyApp(), child: const MyApp(),
@@ -28,15 +34,6 @@ void main() async {
); );
} }
// @pragma('vm:entry-point')
// Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// log("Full background message payload: ${message.toMap()}");
// await Firebase.initializeApp();
// final notificationServices = NotificationServices();
// await notificationServices.initLocalNotifications();
// await notificationServices.showNotification(message);
// log("Background message handled: ${message.data['title']}");
// }
class MyApp extends StatefulWidget { class MyApp extends StatefulWidget {
const MyApp({super.key}); const MyApp({super.key});
@@ -70,22 +67,14 @@ class _MyAppState extends State<MyApp> {
_themeData = theme; _themeData = theme;
mainBloc.sinkTheme.add(_themeData); mainBloc.sinkTheme.add(_themeData);
} }
@override @override
void initState() { void initState() {
super.initState(); super.initState();
mainBloc = BlocProvider.of(context); mainBloc = BlocProvider.of(context);
// notificationServices.initLocalNotifications(); notificationServices.initialize();
// notificationServices.firebaseInit(context); notificationServices.firebaseInit(context);
// notificationServices.setupInteractMessage(); notificationServices.setupInteractMessage(controller);
notificationServices.getDeviceToken().then((token){
print("Firebase Token: $token");
sendNotificationToken(token);
});
}
void sendNotificationToken (String token) async {
int statusCode = await apiServices.sendNotificationToken(token);
log("Notification Send StatusCode : $statusCode");
} }
@override @override
@@ -114,8 +103,7 @@ class _MyAppState extends State<MyApp> {
supportedLocales: AppLocalizations.supportedLocales, supportedLocales: AppLocalizations.supportedLocales,
locale: languageSnapshot.data, locale: languageSnapshot.data,
); );
} });
);
}, },
); );
} }

View File

@@ -30,8 +30,7 @@ class RequestPermissionDialog {
child: Text( child: Text(
"Alow app to use $content permission", "Alow app to use $content permission",
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: const TextStyle( style: context.responsiveBodyLargeWithBold,
fontWeight: FontWeight.bold, fontSize: 18),
), ),
), ),
Divider(height: dialogContext.lowValue), Divider(height: dialogContext.lowValue),
@@ -87,8 +86,8 @@ class RequestPermissionDialog {
child: Text( child: Text(
appLocalization(showCupertinoDialogContext).allow_message), appLocalization(showCupertinoDialogContext).allow_message),
onPressed: () { onPressed: () {
Navigator.pop(showCupertinoDialogContext);
AppSettings.openAppSettings(type: appSettingsType); AppSettings.openAppSettings(type: appSettingsType);
Navigator.pop(showCupertinoDialogContext);
}, },
), ),
], ],

View File

@@ -1,4 +1,4 @@
// ignore_for_file: constant_identifier_names // ignore_for_file: constant_identifier_names, non_constant_identifier_names
class ApplicationConstants { class ApplicationConstants {
static const APP_NAME = "Smatec SFM"; static const APP_NAME = "Smatec SFM";
@@ -21,8 +21,10 @@ class ApplicationConstants {
static const DEVICE_LOGS_PATH = "/device-logs"; static const DEVICE_LOGS_PATH = "/device-logs";
static const GROUP_PATH = "/groups"; static const GROUP_PATH = "/groups";
static const DEVICE_NOTIFICATIONS_SETTINGS = "/device-notifications-settings"; static const DEVICE_NOTIFICATIONS_SETTINGS = "/device-notifications-settings";
static const SIM_DATA_SETTINGS = "/sim-data-settings";
static const OWNER_GROUP = "owner"; static const OWNER_GROUP = "owner";
static const PARTICIPANT_GROUP = "participant"; static const PARTICIPANT_GROUP = "participant";
static const NO_DATA = "no_data"; static const NO_DATA = "no_data";
static const LOADING = "loading"; static const LOADING = "loading";
static int CALL_API_TIMEOUT = 30;
} }

View File

@@ -13,4 +13,5 @@ enum AppRoutes {
HISTORY, HISTORY,
GROUPS, GROUPS,
GROUP_DETAIL, GROUP_DETAIL,
SIM_DATA_SETTING,
} }

View File

@@ -1,4 +1,7 @@
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import '../../../bloc/sim_data_bloc.dart';
import '../../../feature/settings/sim_data/sim_data_screen.dart';
import '../../../bloc/device_detail_bloc.dart'; import '../../../bloc/device_detail_bloc.dart';
import '../../../feature/devices/device_detail/device_detail_screen.dart'; import '../../../feature/devices/device_detail/device_detail_screen.dart';
import '../../../bloc/device_notification_settings_bloc.dart'; import '../../../bloc/device_notification_settings_bloc.dart';
@@ -151,6 +154,16 @@ GoRouter goRouter() {
), ),
transitionsBuilder: transitionsRightToLeft), transitionsBuilder: transitionsRightToLeft),
), ),
GoRoute(
path: ApplicationConstants.SIM_DATA_SETTINGS,
name: AppRoutes.SIM_DATA_SETTING.name,
pageBuilder: (context, state) => CustomTransitionPage(
child: BlocProvider(
child: const SimDataScreen(),
blocBuilder: () => SimDataBloc(),
),
transitionsBuilder: transitionsRightToLeft),
),
], ],
); );
} }

Some files were not shown because too many files have changed in this diff Show More