Compare commits

2 Commits
main ... vypq

Author SHA1 Message Date
33f5f27eaa update 2025-04-14 15:35:47 +07:00
84f10271e9 update 2025-04-14 15:35:26 +07:00
114 changed files with 3237 additions and 7939 deletions

View File

@@ -7,10 +7,6 @@
# 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:
@@ -26,8 +22,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,10 +1,3 @@
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()) {
@@ -13,6 +6,11 @@ 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'
@@ -23,12 +21,21 @@ 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
@@ -37,12 +44,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_17 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_1_8
} }
kotlinOptions { kotlinOptions {
jvmTarget = '17' jvmTarget = '1.8'
} }
sourceSets { sourceSets {
@@ -95,9 +102,9 @@ flutter {
} }
dependencies { dependencies {
// implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation platform('com.google.firebase:firebase-bom:33.15.0') implementation 'com.google.firebase:firebase-messaging-directboot:20.2.0'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.2.2'
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,36 +1,30 @@
<manifest xmlns:tools="http://schemas.android.com/tools" <manifest xmlns:android="http://schemas.android.com/apk/res/android">
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="AIzaSyDI8b-PUgKUgj5rHdtgEHCwWjUXYJrqYhE"/> android:value="AIzaSyA9C7Pmxw6Gw3H2mM4WA_XGngRIIr2VS7k"/>
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:launchMode="singleTop" android:launchMode="singleTop"
@@ -40,7 +34,6 @@
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
@@ -67,25 +60,7 @@
<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.

Before

Width:  |  Height:  |  Size: 442 B

View File

@@ -1,9 +1,21 @@
plugins { buildscript {
id 'com.google.gms.google-services' version '4.3.15' apply false ext.kotlin_version = '1.8.22'
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()
} }
@@ -31,7 +43,3 @@ 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.14.2-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip

View File

@@ -18,11 +18,8 @@ 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.3.2" apply false id "com.android.application" version "8.2.1" apply false
// START: FlutterFire Configuration id "org.jetbrains.kotlin.android" version "1.8.22" apply false
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.

View File

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

View File

@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project # Uncomment this line to define a global platform for your project
platform :ios, '15.0' platform :ios, '14.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency. # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true' ENV['COCOAPODS_DISABLE_STATS'] = 'true'
@@ -37,35 +37,6 @@ 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)
target.build_configurations.each do |config|
# You can remove unused permissions here
# for more information: https://github.com/BaseflowIT/flutter-permission-handler/blob/master/permission_handler/ios/Classes/PermissionHandlerEnums.h
# e.g. when you don't need camera permission, just add 'PERMISSION_CAMERA=0'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
## dart: PermissionGroup.camera
'PERMISSION_CAMERA=1',
## The 'PERMISSION_LOCATION' macro enables the `locationWhenInUse` and `locationAlways` permission. If
## the application only requires `locationWhenInUse`, only specify the `PERMISSION_LOCATION_WHENINUSE`
## macro.
##
## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse]
'PERMISSION_LOCATION=0',
'PERMISSION_LOCATION_WHENINUSE=1',
## dart: PermissionGroup.notification
'PERMISSION_NOTIFICATIONS=1',
## dart: PermissionGroup.criticalAlerts
'PERMISSION_CRITICAL_ALERTS=1',
]
end
target.build_configurations.each do |config| target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '14.0' config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '14.0'
end end

View File

@@ -1,197 +1,62 @@
PODS: 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 (1.0.0)
- flutter_barcode_scanner_plus (3.0.7): - flutter_barcode_scanner_plus (3.0.7):
- Flutter - Flutter
- flutter_fgbg (0.0.1):
- Flutter
- flutter_local_notifications (0.0.1): - flutter_local_notifications (0.0.1):
- Flutter - Flutter
- geolocator_apple (1.2.0):
- Flutter
- FlutterMacOS
- Google-Maps-iOS-Utils (5.0.0): - Google-Maps-iOS-Utils (5.0.0):
- GoogleMaps (~> 8.0) - GoogleMaps (~> 8.0)
- google_maps_flutter_ios (0.0.1): - google_maps_flutter_ios (0.0.1):
- Flutter - Flutter
- Google-Maps-iOS-Utils (< 7.0, >= 5.0) - Google-Maps-iOS-Utils (< 7.0, >= 5.0)
- GoogleMaps (< 10.0, >= 8.4) - GoogleMaps (< 10.0, >= 8.4)
- GoogleDataTransport (10.1.0):
- nanopb (~> 3.30910.0)
- PromisesObjC (~> 2.4)
- GoogleMaps (8.4.0): - GoogleMaps (8.4.0):
- GoogleMaps/Maps (= 8.4.0) - GoogleMaps/Maps (= 8.4.0)
- GoogleMaps/Base (8.4.0) - GoogleMaps/Base (8.4.0)
- GoogleMaps/Maps (8.4.0): - GoogleMaps/Maps (8.4.0):
- GoogleMaps/Base - 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): - maps_launcher (0.0.1):
- Flutter - 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): - permission_handler_apple (9.3.0):
- Flutter - Flutter
- PromisesObjC (2.4.0)
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- url_launcher_ios (0.0.1):
- Flutter
DEPENDENCIES: 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 (from `Flutter`)
- flutter_barcode_scanner_plus (from `.symlinks/plugins/flutter_barcode_scanner_plus/ios`) - 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`) - 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`) - google_maps_flutter_ios (from `.symlinks/plugins/google_maps_flutter_ios/ios`)
- maps_launcher (from `.symlinks/plugins/maps_launcher/ios`) - maps_launcher (from `.symlinks/plugins/maps_launcher/ios`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/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: SPEC REPOS:
trunk: trunk:
- Firebase
- FirebaseCore
- FirebaseCoreInternal
- FirebaseInstallations
- FirebaseMessaging
- Google-Maps-iOS-Utils - Google-Maps-iOS-Utils
- GoogleDataTransport
- GoogleMaps - GoogleMaps
- GoogleUtilities
- nanopb
- PromisesObjC
EXTERNAL SOURCES: 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: Flutter:
:path: Flutter :path: Flutter
flutter_barcode_scanner_plus: flutter_barcode_scanner_plus:
:path: ".symlinks/plugins/flutter_barcode_scanner_plus/ios" :path: ".symlinks/plugins/flutter_barcode_scanner_plus/ios"
flutter_fgbg:
:path: ".symlinks/plugins/flutter_fgbg/ios"
flutter_local_notifications: flutter_local_notifications:
:path: ".symlinks/plugins/flutter_local_notifications/ios" :path: ".symlinks/plugins/flutter_local_notifications/ios"
geolocator_apple:
:path: ".symlinks/plugins/geolocator_apple/darwin"
google_maps_flutter_ios: google_maps_flutter_ios:
:path: ".symlinks/plugins/google_maps_flutter_ios/ios" :path: ".symlinks/plugins/google_maps_flutter_ios/ios"
maps_launcher: maps_launcher:
:path: ".symlinks/plugins/maps_launcher/ios" :path: ".symlinks/plugins/maps_launcher/ios"
permission_handler_apple: permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios" :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: 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: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_barcode_scanner_plus: e5ef7f41cdbf3086e1b9348dce986dde3aa08696 flutter_barcode_scanner_plus: 5777819a85622aed4284fb14d1fa33b72a64003d
flutter_fgbg: d3da78df78454b1808f0829a5da9cd17dfe16444 flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
flutter_local_notifications: ad39620c743ea4c15127860f4b5641649a988100
geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e
Google-Maps-iOS-Utils: 66d6de12be1ce6d3742a54661e7a79cb317a9321 Google-Maps-iOS-Utils: 66d6de12be1ce6d3742a54661e7a79cb317a9321
google_maps_flutter_ios: 0291eb2aa252298a769b04d075e4a9d747ff7264 google_maps_flutter_ios: e31555a04d1986ab130f2b9f24b6cdc861acc6d3
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleMaps: 8939898920281c649150e0af74aa291c60f2e77d GoogleMaps: 8939898920281c649150e0af74aa291c60f2e77d
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 maps_launcher: 2e5b6a2d664ec6c27f82ffa81b74228d770ab203
maps_launcher: edf829809ba9e894d70e569bab11c16352dedb45 permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
PODFILE CHECKSUM: 72b62bbc58143b910914489c5ddbf2bfa99b3dd4 PODFILE CHECKSUM: 9de95a6a40372eef38c524a1823cf12342740f38
COCOAPODS: 1.16.2 COCOAPODS: 1.16.2

View File

@@ -11,13 +11,12 @@
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 */; }; 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 */; }; 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
8C56D767FE4FABDBA3A6FEC5 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9FD75834CF1BDB8A25BFFEC /* Pods_Runner.framework */; };
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 */
@@ -46,18 +45,17 @@
/* 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>"; }; 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>"; };
78334C166CCB562F36E48821 /* 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>"; };
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>"; };
7F880EC2F80617123414BCDC /* 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>"; };
932B4AD0BAFC1BC9FBBFA388 /* 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>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -65,7 +63,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>"; }; F9FD75834CF1BDB8A25BFFEC /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@@ -74,13 +72,21 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */,
634E6B9E5E8AF15560F03990 /* Pods_Runner.framework in Frameworks */, 8C56D767FE4FABDBA3A6FEC5 /* Pods_Runner.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
316923E64F63A0F9D874FB14 /* Frameworks */ = {
isa = PBXGroup;
children = (
F9FD75834CF1BDB8A25BFFEC /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
331C8082294A63A400263BE5 /* RunnerTests */ = { 331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -89,14 +95,6 @@
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 = (
@@ -111,13 +109,12 @@
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 */, E4B9BF767E8449E8CDAC1C45 /* Pods */,
7E17DC341A9804E98C0A4D9D /* Frameworks */, 316923E64F63A0F9D874FB14 /* Frameworks */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@@ -150,9 +147,9 @@
E4B9BF767E8449E8CDAC1C45 /* Pods */ = { E4B9BF767E8449E8CDAC1C45 /* Pods */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
30C8386A287397C265D21D0C /* Pods-Runner.debug.xcconfig */, 7F880EC2F80617123414BCDC /* Pods-Runner.debug.xcconfig */,
25C6B6667708ECABC11CB63A /* Pods-Runner.release.xcconfig */, 78334C166CCB562F36E48821 /* Pods-Runner.release.xcconfig */,
22127402AC507377520FA076 /* Pods-Runner.profile.xcconfig */, 932B4AD0BAFC1BC9FBBFA388 /* Pods-Runner.profile.xcconfig */,
); );
path = Pods; path = Pods;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -181,15 +178,15 @@
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 */, 9EC01057D3A8F2BAF61BC6F6 /* [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 */, F412CDB3CB7EB2B89B8A1F67 /* [CP] Embed Pods Frameworks */,
1DD8F2A74CC23CF717FB4664 /* [CP] Copy Pods Resources */, 7A68DAC237480EA02778D297 /* [CP] Copy Pods Resources */,
); );
buildRules = ( buildRules = (
); );
@@ -259,7 +256,6 @@
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 */, 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 */,
); );
@@ -268,23 +264,6 @@
/* 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;
@@ -301,7 +280,39 @@
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 */ = { 7A68DAC237480EA02778D297 /* [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;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
9EC01057D3A8F2BAF61BC6F6 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
@@ -323,7 +334,7 @@
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"; 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; showEnvVarsInLog = 0;
}; };
6E40E7EA960942FA803CE7C6 /* [CP] Embed Pods Frameworks */ = { F412CDB3CB7EB2B89B8A1F67 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
@@ -340,21 +351,6 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
@@ -446,7 +442,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 = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
@@ -464,17 +460,17 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = 6;
DEVELOPMENT_TEAM = WM843WW2NF; DEVELOPMENT_TEAM = WM843WW2NF;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SmartFM; INFOPLIST_KEY_CFBundleDisplayName = SmartFM;
IPHONEOS_DEPLOYMENT_TARGET = 15.6; IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.5; MARKETING_VERSION = 1.0.6;
PRODUCT_BUNDLE_IDENTIFIER = vn.smatec.sfm; PRODUCT_BUNDLE_IDENTIFIER = vn.smatec.sfm;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@@ -578,7 +574,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 = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@@ -627,7 +623,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 = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
@@ -647,17 +643,17 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = 6;
DEVELOPMENT_TEAM = WM843WW2NF; DEVELOPMENT_TEAM = WM843WW2NF;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SmartFM; INFOPLIST_KEY_CFBundleDisplayName = SmartFM;
IPHONEOS_DEPLOYMENT_TARGET = 15.6; IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.5; MARKETING_VERSION = 1.0.6;
PRODUCT_BUNDLE_IDENTIFIER = vn.smatec.sfm; PRODUCT_BUNDLE_IDENTIFIER = vn.smatec.sfm;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@@ -677,17 +673,17 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = 6;
DEVELOPMENT_TEAM = WM843WW2NF; DEVELOPMENT_TEAM = WM843WW2NF;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SmartFM; INFOPLIST_KEY_CFBundleDisplayName = SmartFM;
IPHONEOS_DEPLOYMENT_TARGET = 15.6; IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.5; MARKETING_VERSION = 1.0.6;
PRODUCT_BUNDLE_IDENTIFIER = vn.smatec.sfm; PRODUCT_BUNDLE_IDENTIFIER = vn.smatec.sfm;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";

View File

@@ -0,0 +1,132 @@
{
"originHash" : "55c94c702657474632f237e12a19340acc7419ac493c42deecaaa9d0cd699fd8",
"pins" : [
{
"identity" : "abseil-cpp-binary",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/abseil-cpp-binary.git",
"state" : {
"revision" : "bbe8b69694d7873315fd3a4ad41efe043e1c07c5",
"version" : "1.2024072200.0"
}
},
{
"identity" : "app-check",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/app-check.git",
"state" : {
"revision" : "61b85103a1aeed8218f17c794687781505fbbef5",
"version" : "11.2.0"
}
},
{
"identity" : "firebase-ios-sdk",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/firebase-ios-sdk",
"state" : {
"revision" : "d1f7c7e8eaa74d7e44467184dc5f592268247d33",
"version" : "11.11.0"
}
},
{
"identity" : "flutterfire",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/flutterfire",
"state" : {
"revision" : "a80a123386fd4904cad6938673020a7bcf31b2f2",
"version" : "3.13.0-firebase-core-swift"
}
},
{
"identity" : "googleappmeasurement",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleAppMeasurement.git",
"state" : {
"revision" : "dd89fc79a77183830742a16866d87e4e54785734",
"version" : "11.11.0"
}
},
{
"identity" : "googledatatransport",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleDataTransport.git",
"state" : {
"revision" : "617af071af9aa1d6a091d59a202910ac482128f9",
"version" : "10.1.0"
}
},
{
"identity" : "googleutilities",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleUtilities.git",
"state" : {
"revision" : "53156c7ec267db846e6b64c9f4c4e31ba4cf75eb",
"version" : "8.0.2"
}
},
{
"identity" : "grpc-binary",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/grpc-binary.git",
"state" : {
"revision" : "cc0001a0cf963aa40501d9c2b181e7fc9fd8ec71",
"version" : "1.69.0"
}
},
{
"identity" : "gtm-session-fetcher",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/gtm-session-fetcher.git",
"state" : {
"revision" : "4d70340d55d7d07cc2fdf8e8125c4c126c1d5f35",
"version" : "4.4.0"
}
},
{
"identity" : "interop-ios-for-google-sdks",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/interop-ios-for-google-sdks.git",
"state" : {
"revision" : "040d087ac2267d2ddd4cca36c757d1c6a05fdbfe",
"version" : "101.0.0"
}
},
{
"identity" : "leveldb",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/leveldb.git",
"state" : {
"revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1",
"version" : "1.22.5"
}
},
{
"identity" : "nanopb",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/nanopb.git",
"state" : {
"revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1",
"version" : "2.30910.0"
}
},
{
"identity" : "promises",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/promises.git",
"state" : {
"revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac",
"version" : "2.4.0"
}
},
{
"identity" : "swift-protobuf",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-protobuf.git",
"state" : {
"revision" : "d72aed98f8253ec1aa9ea1141e28150f408cf17f",
"version" : "1.29.0"
}
}
],
"version" : 3
}

View File

@@ -44,7 +44,6 @@
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
@@ -73,13 +72,11 @@
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

@@ -0,0 +1,132 @@
{
"originHash" : "55c94c702657474632f237e12a19340acc7419ac493c42deecaaa9d0cd699fd8",
"pins" : [
{
"identity" : "abseil-cpp-binary",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/abseil-cpp-binary.git",
"state" : {
"revision" : "bbe8b69694d7873315fd3a4ad41efe043e1c07c5",
"version" : "1.2024072200.0"
}
},
{
"identity" : "app-check",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/app-check.git",
"state" : {
"revision" : "61b85103a1aeed8218f17c794687781505fbbef5",
"version" : "11.2.0"
}
},
{
"identity" : "firebase-ios-sdk",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/firebase-ios-sdk",
"state" : {
"revision" : "d1f7c7e8eaa74d7e44467184dc5f592268247d33",
"version" : "11.11.0"
}
},
{
"identity" : "flutterfire",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/flutterfire",
"state" : {
"revision" : "a80a123386fd4904cad6938673020a7bcf31b2f2",
"version" : "3.13.0-firebase-core-swift"
}
},
{
"identity" : "googleappmeasurement",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleAppMeasurement.git",
"state" : {
"revision" : "dd89fc79a77183830742a16866d87e4e54785734",
"version" : "11.11.0"
}
},
{
"identity" : "googledatatransport",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleDataTransport.git",
"state" : {
"revision" : "617af071af9aa1d6a091d59a202910ac482128f9",
"version" : "10.1.0"
}
},
{
"identity" : "googleutilities",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleUtilities.git",
"state" : {
"revision" : "53156c7ec267db846e6b64c9f4c4e31ba4cf75eb",
"version" : "8.0.2"
}
},
{
"identity" : "grpc-binary",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/grpc-binary.git",
"state" : {
"revision" : "cc0001a0cf963aa40501d9c2b181e7fc9fd8ec71",
"version" : "1.69.0"
}
},
{
"identity" : "gtm-session-fetcher",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/gtm-session-fetcher.git",
"state" : {
"revision" : "4d70340d55d7d07cc2fdf8e8125c4c126c1d5f35",
"version" : "4.4.0"
}
},
{
"identity" : "interop-ios-for-google-sdks",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/interop-ios-for-google-sdks.git",
"state" : {
"revision" : "040d087ac2267d2ddd4cca36c757d1c6a05fdbfe",
"version" : "101.0.0"
}
},
{
"identity" : "leveldb",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/leveldb.git",
"state" : {
"revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1",
"version" : "1.22.5"
}
},
{
"identity" : "nanopb",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/nanopb.git",
"state" : {
"revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1",
"version" : "2.30910.0"
}
},
{
"identity" : "promises",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/promises.git",
"state" : {
"revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac",
"version" : "2.4.0"
}
},
{
"identity" : "swift-protobuf",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-protobuf.git",
"state" : {
"revision" : "d72aed98f8253ec1aa9ea1141e28150f408cf17f",
"version" : "1.29.0"
}
}
],
"version" : 3
}

View File

@@ -4,8 +4,6 @@ import flutter_local_notifications
import GoogleMaps import GoogleMaps
import FirebaseCore import FirebaseCore
import FirebaseMessaging import FirebaseMessaging
import UserNotifications
import alarm
@main @main
@objc class AppDelegate: FlutterAppDelegate { @objc class AppDelegate: FlutterAppDelegate {
@@ -16,7 +14,6 @@ import alarm
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() FirebaseApp.configure()
GMSServices.provideAPIKey("AIzaSyA9C7Pmxw6Gw3H2mM4WA_XGngRIIr2VS7k") GMSServices.provideAPIKey("AIzaSyA9C7Pmxw6Gw3H2mM4WA_XGngRIIr2VS7k")
GeneratedPluginRegistrant.register(with: self) GeneratedPluginRegistrant.register(with: self)

View File

@@ -1,10 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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"> <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">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<scenes> <scenes>
<!--Flutter View Controller--> <!--Flutter View Controller-->
@@ -16,14 +14,13 @@
<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="393" height="852"/> <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</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

@@ -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>SmartFM</string> <string>Sfm App</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
@@ -28,18 +28,19 @@
<true/> <true/>
<key>NSCameraUsageDescription</key> <key>NSCameraUsageDescription</key>
<string>Camera permission is required for barcode scanning.</string> <string>Camera permission is required for barcode scanning.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Always and when in use!</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Can I have location always?</string>
<key>NSLocationUsageDescription</key>
<string>Older devices need location.</string>
<key>NSLocationWhenInUseUsageDescription</key> <key>NSLocationWhenInUseUsageDescription</key>
<string>Cần vị trí để hiển thị bản đồ hoặc tìm địa điểm gần bạn</string> <string>Need location when in use</string>
<key>NSUserNotificationsUsageDescription</key>
<string>Chúng tôi cần gửi thông báo khẩn cấp để cảnh báo về việc thiết bị phát hiện có cháy.</string>
<key>com.apple.developer.usernotifications.critical-alerts</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key> <key>UIApplicationSupportsIndirectInputEvents</key>
<true/> <true/>
<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>
@@ -61,9 +62,5 @@
</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.

1
ios/build/.last_build_id Normal file
View File

@@ -0,0 +1 @@
4f4dfcdc103d3d3b4ca43bd2cac6c5ac

View File

@@ -0,0 +1 @@
{}

Binary file not shown.

View File

@@ -4,6 +4,7 @@ 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,15 +1,19 @@
// 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();
@@ -38,12 +42,14 @@ class DetailDeviceBloc extends BlocBase {
String thingID, String thingID,
Completer<GoogleMapController> controller, Completer<GoogleMapController> controller,
) async { ) async {
await apiServices.execute(context, () async { String body = await apiServices.getDeviceInfomation(thingID);
Device device = await apiServices.getDeviceInformation(thingID); if (body != "") {
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);
} }
@@ -68,103 +74,40 @@ 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(BuildContext context, String thingID) async { void getNearerSensorValue(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 = Map<String, dynamic> params = {
DateTimeUtils.instance.formatDateTimeToString(DateTime.now()); 'thing_id': thingID,
Map<String, dynamic> params = { 'from': from,
'thing_id': thingID, 'to': now,
'from': from, 'limit': '100',
'to': now, 'n': '7',
'limit': '100', };
'n': '7', final body = await apiServices.getLogsOfDevice(thingID, params);
}; if (body != "") {
DeviceLog devicesListLog = final data = jsonDecode(body);
await apiServices.getLogsOfDevice(thingID, params); 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);
} }
sensorTemps = sensorTemps.reversed.toList(); sensorTemps = sensorTemps.reversed.toList();
sinkSensorTemps.add(sensorTemps); sinkSensorTemps.add(sensorTemps);
} 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,11 +1,12 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'dart:convert';
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';
@@ -34,38 +35,40 @@ class DeviceLogsBloc extends BlocBase {
@override @override
void dispose() {} void dispose() {}
void getAllDevices(BuildContext context) async { void getAllDevices() async {
await apiServices.execute(context, () async { String body = await apiServices.getOwnerDevices();
List<Device> originalDevices = await apiServices.getOwnerDevices(); if (body != "") {
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 = Map<String, dynamic> params = {
DateTimeUtils.instance.formatDateTimeToString(DateTime.now()); 'thing_id': thingID,
Map<String, dynamic> params = { 'from': fromDateString,
'thing_id': thingID, 'to': now,
'from': fromDateString, 'limit': '30',
'to': now, "offset": offset.toString(),
'limit': '30', "asc": "true"
"offset": offset.toString(), };
"asc": "true" final body = await apiServices.getLogsOfDevice(thingID, params);
}; if (body != "") {
DeviceLog devicesListLog = final data = jsonDecode(body);
await apiServices.getLogsOfDevice(thingID, params); 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);
@@ -78,38 +81,6 @@ 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,4 +1,5 @@
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';
@@ -35,6 +36,7 @@ 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:developer'; import 'dart:convert';
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,281 +75,151 @@ class DeviceUpdateBloc extends BlocBase {
// deviceInfo.done; // deviceInfo.done;
} }
Future<void> getAllProvinces(BuildContext context) async { Future<void> getAllProvinces() async {
List<DropdownMenuItem<Province>> provincesData = []; List<DropdownMenuItem<Province>> provincesData = [];
provincesData.clear(); provincesData.clear();
sinkListProvinces.add(provincesData); sinkListProvinces.add(provincesData);
await apiServices.execute(context, () async { final body = await apiServices.getAllProvinces();
List<Province> provinces = await apiServices.getAllProvinces(); final data = jsonDecode(body);
for (var province in provinces) { List<dynamic> items = data["items"];
provincesData.add(
DropdownMenuItem(value: province, child: Text(province.fullName!))); final provinces = Province.fromJsonDynamicList(items);
} for (var province in provinces) {
sinkListProvinces.add(provincesData); provincesData.add(
}); DropdownMenuItem(value: province, child: Text(province.fullName!)));
}
sinkListProvinces.add(provincesData);
} }
Future<void> getAllDistricts(BuildContext context, String provinceID) async { Future<void> getAllDistricts(String provinceID) async {
List<DropdownMenuItem<District>> districtsData = []; List<DropdownMenuItem<District>> districtsData = [];
districtsData.clear(); districtsData.clear();
sinkListDistricts.add(districtsData); sinkListDistricts.add(districtsData);
await apiServices.execute(context, () async { final body = await apiServices.getAllDistricts(provinceID);
final districts = await apiServices.getAllDistricts(provinceID); final data = jsonDecode(body);
for (var district in districts) { List<dynamic> items = data["items"];
districtsData.add( final districts = District.fromJsonDynamicList(items);
DropdownMenuItem(value: district, child: Text(district.fullName!))); for (var district in districts) {
} districtsData.add(
sinkListDistricts.add(districtsData); DropdownMenuItem(value: district, child: Text(district.fullName!)));
}); }
sinkListDistricts.add(districtsData);
} }
Future<void> getAllWards(BuildContext context, String districtID) async { Future<void> getAllWards(String districtID) async {
List<DropdownMenuItem<Ward>> wardsData = []; List<DropdownMenuItem<Ward>> wardsData = [];
wardsData.clear(); wardsData.clear();
sinkListWards.add(wardsData); sinkListWards.add(wardsData);
await apiServices.execute(context, () async { final body = await apiServices.getAllWards(districtID);
final wards = await apiServices.getAllWards(districtID); final data = jsonDecode(body);
for (var ward in wards) { List<dynamic> items = data["items"];
wardsData final wards = Ward.fromJsonDynamicList(items);
.add(DropdownMenuItem(value: ward, child: Text(ward.fullName!))); for (var ward in wards) {
} wardsData.add(DropdownMenuItem(value: ward, child: Text(ward.fullName!)));
sinkListWards.add(wardsData); }
}); sinkListWards.add(wardsData);
} }
Future<void> getDeviceInformation( Future<void> getDeviceInfomation(
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 {
await apiServices.execute(context, () async { String body = await apiServices.getDeviceInfomation(thingID);
Device device = await apiServices.getDeviceInformation(thingID); final data = jsonDecode(body);
sinkDeviceInfo.add(device); Device device = Device.fromJson(data);
deviceNameController.text = device.name ?? ""; sinkDeviceInfo.add(device);
latitudeController.text = device.settings!.latitude ?? ""; deviceNameController.text = device.name ?? "";
longitudeController.text = device.settings!.longitude ?? ""; latitudeController.text = device.settings!.latitude ?? "";
longitudeController.text = device.settings!.longitude ?? "";
if (device.areaPath != null && device.areaPath!.isNotEmpty) { if (device.areaPath != "") {
List<String> areaPath = device.areaPath!.split('_'); List<String> areaPath = device.areaPath!.split('_');
String provinceCode = areaPath[0];
// Kiểm tra độ dài của areaPath String districtCode = areaPath[1];
if (areaPath.length >= 3) { String wardCode = areaPath[2];
String provinceCode = areaPath[0]; getAllDistricts(provinceCode);
String districtCode = areaPath[1]; getAllWards(districtCode);
String wardCode = areaPath[2]; final provinceResponse = await apiServices.getProvinceByID(provinceCode);
final provincesData = jsonDecode(provinceResponse);
// Kiểm tra các mã có hợp lệ không (không rỗng) Province province = Province.fromJson(provincesData['data']);
if (provinceCode.isNotEmpty && final districtResponse = await apiServices.getDistrictByID(districtCode);
districtCode.isNotEmpty && final districtData = jsonDecode(districtResponse);
wardCode.isNotEmpty) { District district = District.fromJson(districtData['data']);
try { final wardResponse = await apiServices.getWardByID(wardCode);
// Lấy danh sách districts và wards final wardData = jsonDecode(wardResponse);
await getAllDistricts(context, provinceCode); Ward ward = Ward.fromJson(wardData['data']);
await getAllWards(context, districtCode); Map<String, String> provinceData = {
"name": province.fullName!,
// Xử lý Province "code": province.code!
try { };
Province province = sinkProvinceData.add(provinceData);
await apiServices.getProvinceByID(provinceCode); Map<String, String> districData = {
Map<String, String> provinceData = { "name": district.fullName!,
"name": province.fullName ?? "Unknown Province", "code": district.code!,
"code": province.code ?? provinceCode };
}; sinkDistrictData.add(districData);
sinkProvinceData.add(provinceData); Map<String, String> wardMap = {
} catch (e) { "name": ward.fullName!,
// Thêm dữ liệu mặc định khi lỗi "code": ward.code!,
Map<String, String> provinceData = { };
"name": "Error Loading Province", sinkWardData.add(wardMap);
"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) {
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);
}
} 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(BuildContext context, String name) async { Future<Province> getProvinceByName(String name) async {
return await apiServices.execute(context, () async { final response = await apiServices.getProvincesByName(name);
List<Province> provinces = await apiServices.getProvincesByName(name); final data = jsonDecode(response);
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( Future<District> getDistrictByName(String name, String provinceCode) async {
BuildContext context, String name, String provinceCode) async { final response = await apiServices.getDistrictsByName(name);
return apiServices.execute(context, () async { if (response != "") {
final districts = await apiServices.getDistrictsByName(name); final data = jsonDecode(response);
return districts.firstWhere( List<dynamic> items = data['items'];
(district) => district.provinceCode == provinceCode, if (items.isNotEmpty) {
orElse: () => District(name: "null"), 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( Future<Ward> getWardByName(String name, String districtCode) async {
BuildContext context, String name, String districtCode) async { final response = await apiServices.getWarsdByName(name);
return apiServices.execute(context, () async { final data = jsonDecode(response);
final wards = await apiServices.getWardsByName(name); if (data != null && data['items'] != null) {
return wards.firstWhere( List<dynamic> items = data['items'];
(ward) => ward.districtCode == districtCode, if (items.isNotEmpty) {
orElse: () => Ward(name: "null"), List<Ward> wards = Ward.fromJsonDynamicList(items);
); if (wards.isNotEmpty) {
}); for (var ward in wards) {
if (ward.districtCode == districtCode) {
return ward;
}
}
}
}
}
return Ward(name: "null");
} }
Future<void> updateDevice( Future<void> updateDevice(
@@ -362,50 +232,24 @@ 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); Map<String, dynamic> body = {
Map<String, dynamic> body = { "name": name,
"name": name, "area_province": provinceCode,
"area_province": provinceCode, "area_district": districtCode,
"area_district": districtCode, "area_ward": wardCode,
"area_ward": wardCode, "latitude": latitude,
"latitude": latitude, "longitude": longitude,
"longitude": longitude, "note": "User updated device infomation at $formattedDateTime",
"note": "User updated device infomation at $formattedDateTime", };
}; int statusCode = await apiServices.updateOwnerDevice(thingID, body);
int statusCode = await apiServices.updateOwnerDevice(thingID, body); showSnackBarResponseByStatusCodeNoIcon(
showSnackBarResponseByStatusCodeNoIcon( context,
context, statusCode,
statusCode, 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 'package:flutter/cupertino.dart'; import 'dart:convert';
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,51 +73,46 @@ class DevicesManagerBloc extends BlocBase {
// } // }
// } // }
void getDeviceByState(BuildContext context, int state) async { void getDeviceByState(int state) async {
try { sinkTagStates.add([state]);
sinkTagStates.add([state]);
Map<String, List<Device>> deviceByState = { Map<String, List<Device>> deviceByState = {
ApplicationConstants.OFFLINE_STATE: [], ApplicationConstants.OFFLINE_STATE: [],
ApplicationConstants.NORMAL_STATE: [], ApplicationConstants.NORMAL_STATE: [],
ApplicationConstants.WARNING_STATE: [], ApplicationConstants.WARNING_STATE: [],
ApplicationConstants.INPROGRESS_STATE: [], ApplicationConstants.INPROGRESS_STATE: [],
ApplicationConstants.ERROR_STATE: [], ApplicationConstants.ERROR_STATE: [],
}; };
List<Device> devices = []; List<Device> devices = [];
List<Device> originalDevices = []; String body;
if (state != -2) {
originalDevices = await apiServices
.getOwnerDeviceByState({"state": state.toString()});
} else {
originalDevices = await apiServices.getOwnerDevices();
}
List<Device> publicDevices = []; if (state != -2) {
body =
await apiServices.getOwnerDeviceByState({"state": state.toString()});
} else {
body = await apiServices.getOwnerDevices();
}
if (body.isNotEmpty) {
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(publicDevices) ? DeviceUtils.instance.sortDeviceAZByName(originalDevices)
: DeviceUtils.instance.sortDeviceByState(publicDevices); : DeviceUtils.instance.sortDeviceByState(originalDevices);
if (state == -2) { if (state == -2) {
for (var device in publicDevices) { for (var device in originalDevices) {
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);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(context, e.toString());
} }
sinkAllDevices.add(devices);
} }
String _getStateKey(int state) { String _getStateKey(int state) {

View File

@@ -1,13 +1,15 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'dart:async'; import 'dart:async';
import 'package:flutter/widgets.dart'; import 'dart:convert';
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 {
@@ -27,10 +29,12 @@ class DetailGroupBloc extends BlocBase {
@override @override
void dispose() {} void dispose() {}
Future<void> getGroupDetail(BuildContext context, String groupID) async { Future<void> getGroupDetail(String groupID) async {
await apiServices.execute(context, () async { final body = await apiServices.getGroupDetail(groupID);
List<DeviceOfGroup> warningDevices = []; final data = jsonDecode(body);
GroupDetail group = await apiServices.getGroupDetail(groupID); List<DeviceOfGroup> warningDevices = [];
if (data != null) {
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!) {
@@ -40,135 +44,62 @@ 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( context,
context, statusCode,
statusCode, 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(BuildContext context) { Future<List<Device>> getOwnerDevices() async {
return apiServices.execute(context, () async { List<Device> allDevices = [];
final originalDevices = await apiServices.getOwnerDevices(); String body = await apiServices.getOwnerDevices();
return originalDevices if (body != "") {
.where((device) => device.visibility == "PUBLIC") final data = jsonDecode(body);
.toList(); List<dynamic> items = data['items'];
}); allDevices = Device.fromJsonDynamicList(items);
}
return allDevices;
} }
Future<void> addDeviceToGroup( Future<void> addDeviceToGroup(
@@ -176,26 +107,12 @@ 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, statusCode,
statusCode, 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,143 +1,34 @@
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 = final allDevicesAliasMap = StreamController<Map<String,List<DeviceWithAlias>>>.broadcast();
StreamController<Map<String, List<DeviceWithAlias>>?>.broadcast(); StreamSink<Map<String,List<DeviceWithAlias>>> get sinkAllDevicesAliasMap =>
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 = final allDevicesAliasJoinedMap = StreamController<Map<String,List<DeviceWithAlias>>>.broadcast();
// StreamController<Map<String, List<DeviceWithAlias>>>.broadcast(); StreamSink<Map<String,List<DeviceWithAlias>>> get sinkAllDevicesAliasJoinedMap =>
// StreamSink<Map<String, List<DeviceWithAlias>>> allDevicesAliasJoinedMap.sink;
// get sinkAllDevicesAliasJoinedMap => allDevicesAliasJoinedMap.sink; Stream<Map<String,List<DeviceWithAlias>>> get streamAllDevicesAliasJoinedMap =>
// Stream<Map<String, List<DeviceWithAlias>>> allDevicesAliasJoinedMap.stream;
// 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(); final ownerDevicesStatus =
StreamSink<bool?> get sinkHasJoinedDevice => hasJoinedDevice.sink;
Stream<bool?> get streamHasJoinedDevice => hasJoinedDevice.stream;
final ownerDevicesStatus =
StreamController<Map<String, List<DeviceWithAlias>>>.broadcast(); StreamController<Map<String, List<DeviceWithAlias>>>.broadcast();
StreamSink<Map<String, List<DeviceWithAlias>>> get sinkOwnerDevicesStatus => StreamSink<Map<String, List<DeviceWithAlias>>>
ownerDevicesStatus.sink; get sinkOwnerDevicesStatus => ownerDevicesStatus.sink;
Stream<Map<String, List<DeviceWithAlias>>> get streamOwnerDevicesStatus => Stream<Map<String, List<DeviceWithAlias>>> get streamOwnerDevicesStatus =>
ownerDevicesStatus.stream; ownerDevicesStatus.stream;
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 @override
void dispose() { void dispose() {}
allDevicesAliasMap.close();
// allDevicesAliasJoinedMap.close();
countNotification.close();
hasJoinedDevice.close();
ownerDevicesStatus.close();
aliasDevices.close();
}
} }

View File

@@ -1,9 +1,12 @@
// 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 'dart:convert';
import 'dart:developer';
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';
@@ -32,78 +35,82 @@ class InterFamilyBloc extends BlocBase {
@override @override
void dispose() {} void dispose() {}
void getAllGroup(BuildContext context, String role) async { void getAllGroup(String role) async {
List<Group> groups = []; List<Group> groups = [];
sinkCurrentGroups.add(groups); sinkCurrentGroups.add(groups);
await apiServices.execute(context, () async { final body = await apiServices.getAllGroups();
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 {
await apiServices.execute(context, () async { APIServices apiServices = APIServices();
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(
context, context,
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> changeGroupInformation(BuildContext context, String groupID, Future<void> changeGroupInfomation(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( context,
context, 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

@@ -1,15 +1,18 @@
import 'dart:async'; 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();
StreamSink<Map<String, dynamic>> get sinkLoginRequest => loginRequest.sink; final loginRequest = StreamController<Map<String,dynamic>>.broadcast();
Stream<Map<String, dynamic>> get streamLoginRequest => loginRequest.stream; StreamSink<Map<String,dynamic>> get sinkLoginRequest => loginRequest.sink;
Stream<Map<String,dynamic>> get streamLoginRequest => loginRequest.stream;
final isShowPassword = StreamController<bool>.broadcast(); final isShowPassword = StreamController<bool>.broadcast();
StreamSink<bool> get sinkIsShowPassword => isShowPassword.sink; StreamSink<bool> get sinkIsShowPassword => isShowPassword.sink;
Stream<bool> get streamIsShowPassword => isShowPassword.stream; Stream<bool> get streamIsShowPassword => isShowPassword.stream;
@override @override
void dispose() {} void dispose() {
}
} }

View File

@@ -1,12 +1,13 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
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:sfm_app/product/cache/local_manager.dart';
import '../product/cache/local_manager.dart'; import 'package:sfm_app/product/constant/app/api_path_constant.dart';
import '../product/constant/app/api_path_constant.dart'; import 'package:sfm_app/product/constant/enums/local_keys_enums.dart';
import '../product/constant/enums/local_keys_enums.dart'; import 'package:sfm_app/product/network/network_manager.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';
@@ -41,14 +42,14 @@ class MainBloc extends BlocBase {
@override @override
void dispose() {} void dispose() {}
void getUserProfile(BuildContext context) async { void getUserProfile() async {
await apiServices.execute(context, () async { String data = await apiServices.getUserDetail();
User user = await apiServices.getUserDetail(); User user = User.fromJson(jsonDecode(data));
sinkUserProfile.add(user); sinkUserProfile.add(user);
});
} }
getFCMTokenAndPresentations() async { getFCMTokenAndPresentations() async {
String? firebaseAppToken = await FirebaseMessaging.instance.getToken(); String? firebaseAppToken = await FirebaseMessaging.instance.getToken();
if (firebaseAppToken != null) { if (firebaseAppToken != null) {
@@ -59,11 +60,14 @@ class MainBloc extends BlocBase {
} }
} }
Future<int> sendNotificationToken(String token) async { Future<int> sendNotificationToken(String token) async{
String uid = await getUID(); String uid = await getUID();
Map<String, dynamic> body = {"user_id": uid, "app_token": token}; Map<String,dynamic> body = {
int statusCode = await NetworkManager.instance! "user_id": uid,
.updateDataInServer(APIPathConstants.NOTIFICATION_TOKEN_PATH, body); "app_token": token
};
int statusCode = await NetworkManager.instance!.updateDataInServer(
APIPathConstants.NOTIFICATION_TOKEN_PATH, body);
return statusCode; return statusCode;
} }

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,4 +75,6 @@ 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,17 +1,15 @@
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 =>
@@ -19,13 +17,9 @@ 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

@@ -1,34 +0,0 @@
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,12 +1,11 @@
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 {
@@ -57,7 +56,11 @@ class _BellScreenState extends State<BellScreen> {
initialData: items, initialData: items,
builder: (context, bellSnapshot) { builder: (context, bellSnapshot) {
return check return check
? const SharedLoadingAnimation() ? Center(
child: CircularProgressIndicator(
value: context.highValue,
),
)
: bellSnapshot.data?.isEmpty ?? true : bellSnapshot.data?.isEmpty ?? true
? Center( ? Center(
child: Text( child: Text(
@@ -75,8 +78,16 @@ class _BellScreenState extends State<BellScreen> {
if (index < bellSnapshot.data!.length) { if (index < bellSnapshot.data!.length) {
return GestureDetector( return GestureDetector(
onTap: () async { onTap: () async {
readNotification( List<String> read = [];
bellSnapshot.data![index].id!); read.add(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: [
@@ -132,7 +143,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 SharedComponentLoadingAnimation() ? const CircularProgressIndicator()
: Text( : Text(
appLocalization(context) appLocalization(context)
.bell_read_all, .bell_read_all,
@@ -162,31 +173,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);
} else { } else {
for (var item in bell.items!) { for (var item in bell.items!) {
items.add(item); items.add(item);
}
} }
bellBloc.bellItems.add(items); }
check = false; bellBloc.bellItems.add(items);
}); check = false;
} }
String timeAgo(BuildContext context, DateTime dateTime) { String timeAgo(BuildContext context, DateTime dateTime) {

View File

@@ -1,10 +1,7 @@
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';
@@ -15,6 +12,7 @@ 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';
@@ -44,7 +42,7 @@ class _DeviceLogsScreenState extends State<DeviceLogsScreen> {
() { () {
if (controller.position.maxScrollExtent == controller.offset) { if (controller.position.maxScrollExtent == controller.offset) {
offset += 30; offset += 30;
deviceLogsBloc.getDeviceLogByThingID(context, deviceLogsBloc.getDeviceLogByThingID(
offset, thingID, dateTime!, sensors); offset, thingID, dateTime!, sensors);
} }
}, },
@@ -65,9 +63,11 @@ 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 == null) { if (allDevicesSnapshot.data?[0].thingId == null) {
deviceLogsBloc.getAllDevices(context); deviceLogsBloc.getAllDevices();
return const SharedLoadingAnimation(); return const Center(
child: CircularProgressIndicator(),
);
} else { } else {
return StreamBuilder<List<SensorLogs>>( return StreamBuilder<List<SensorLogs>>(
stream: deviceLogsBloc.streamSensors, stream: deviceLogsBloc.streamSensors,
@@ -90,7 +90,9 @@ class _DeviceLogsScreenState extends State<DeviceLogsScreen> {
hint: Text( hint: Text(
appLocalization(context) appLocalization(context)
.choose_device_dropdownButton, .choose_device_dropdownButton,
style: context.responsiveBodySmall style: const TextStyle(
fontSize: 14,
),
), ),
items: allDevicesSnapshot.data?.isNotEmpty ?? false items: allDevicesSnapshot.data?.isNotEmpty ?? false
? allDevicesSnapshot.data! ? allDevicesSnapshot.data!
@@ -99,7 +101,9 @@ class _DeviceLogsScreenState extends State<DeviceLogsScreen> {
value: device.thingId, value: device.thingId,
child: Text( child: Text(
device.name!, device.name!,
style: context.responsiveBodySmall style: const TextStyle(
fontSize: 14,
),
), ),
), ),
) )
@@ -188,7 +192,6 @@ class _DeviceLogsScreenState extends State<DeviceLogsScreen> {
); );
} else { } else {
deviceLogsBloc.getDeviceLogByThingID( deviceLogsBloc.getDeviceLogByThingID(
context,
offset, offset,
thingID, thingID,
dateTime!, dateTime!,
@@ -247,7 +250,7 @@ class _DeviceLogsScreenState extends State<DeviceLogsScreen> {
return Center( return Center(
child: hasMoreSnapshot.data ?? child: hasMoreSnapshot.data ??
hasMore hasMore
? const SharedComponentLoadingAnimation() ? const CircularProgressIndicator()
: Text( : Text(
appLocalization(context) appLocalization(context)
.main_no_data), .main_no_data),
@@ -311,7 +314,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(context,offset, thingID, dateTime!, sensors); deviceLogsBloc.getDeviceLogByThingID(offset, thingID, dateTime!, sensors);
} }
Widget leadingList(SensorLogs sensor) { Widget leadingList(SensorLogs sensor) {

View File

@@ -1,9 +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 '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';
@@ -12,8 +10,7 @@ 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, addNewDevice(BuildContext context, String role) async {
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(
@@ -28,7 +25,8 @@ addNewDevice(BuildContext context, String role,
children: [ children: [
Text( Text(
'${appLocalization(context).add_device_title}: ', '${appLocalization(context).add_device_title}: ',
style: context.responsiveBodyLargeWithBold, style:
const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
), ),
Container( Container(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
@@ -79,8 +77,7 @@ addNewDevice(BuildContext context, String role,
Colors.white); Colors.white);
ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).hideCurrentSnackBar();
} else { } else {
addDevices( addDevices(context, role, extID, deviceName);
context, role, extID, deviceName, deviceManagerBloc);
ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).hideCurrentSnackBar();
} }
}, },
@@ -93,31 +90,25 @@ addNewDevice(BuildContext context, String role,
); );
} }
void addDevices(BuildContext context, String role, String extID, void addDevices(
String deviceName, DevicesManagerBloc deviceManagerBloc) async { BuildContext context, String role, String extID, String deviceName) 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( context,
context, 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( context,
context, 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,7 +1,6 @@
// 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';
@@ -48,24 +47,20 @@ 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);
});
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,6 @@
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';
@@ -9,6 +8,7 @@ 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(context); deviceUpdateBloc.getAllProvinces();
} }
@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.getDeviceInformation( deviceUpdateBloc.getDeviceInfomation(
context,
widget.thingID, widget.thingID,
// provincesData, districtsData,
// districtsData, wardsData,
// wardsData,
deviceNameController, deviceNameController,
deviceLatitudeController, deviceLatitudeController,
deviceLongitudeController); deviceLongitudeController);
return const SharedLoadingAnimation(); return const Center(
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(context, .getAllDistricts(
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(context,value.code); .getAllWards(value.code);
selectedWard = ""; selectedWard = "";
wardData['name'] = wardData['name'] =
selectedWard!; selectedWard!;

View File

@@ -2,14 +2,15 @@ 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:google_maps_flutter/google_maps_flutter.dart';
import 'package:flutter/material.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';
@@ -63,8 +64,7 @@ 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( getDataFromApi(latitude, longitude, deviceUpdateBloc);
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(BuildContext context, String latitude, String longitude, void getDataFromApi(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(BuildContext context, String latitude, String longitude,
log("$key: $value"); log("$key: $value");
}); });
await _processLocations(context, locations, deviceUpdateBloc); await _processLocations(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(BuildContext context, Future<void> _processLocations(
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(context, provinceNameFromAPI); await deviceUpdateBloc.getProvinceByName(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(context); deviceUpdateBloc.getAllProvinces();
final district = await deviceUpdateBloc.getDistrictByName( final district = await deviceUpdateBloc.getDistrictByName(
context, districtNameFromAPI, province.code!); districtNameFromAPI, province.code!);
log("Districtname: ${district.fullName}, districtCode: ${district.code}"); log("Districtname: ${district.fullName}, districtCode: ${district.code}");
deviceUpdateBloc.getAllDistricts(context, province.code!); deviceUpdateBloc.getAllDistricts(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 = await deviceUpdateBloc.getWardByName( final ward =
context, wardNameFromAPI, district.code!); await deviceUpdateBloc.getWardByName(wardNameFromAPI, district.code!);
log("Wardname: ${ward.fullName}, WardCode: ${ward.code}"); log("Wardname: ${ward.fullName}, WardCode: ${ward.code}");
deviceUpdateBloc.getAllWards(context, district.code!); deviceUpdateBloc.getAllWards(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,11 +1,9 @@
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';
@@ -31,7 +29,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;
@@ -43,7 +41,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(context,tagIndex), (Timer t) => devicesManagerBloc.getDeviceByState(tagIndex),
); );
} }
@@ -60,60 +58,36 @@ 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>( return SafeArea(
stream: devicesManagerBloc.streamUserRole, child: StreamBuilder<List<Device>>(
initialData: role, stream: devicesManagerBloc.streamAllDevices,
builder: (context, roleSnapshot) { initialData: devices,
return SafeArea( builder: (context, allDeviceSnapshot) {
child: StreamBuilder<List<Device>>( if (allDeviceSnapshot.data?.isEmpty ?? devices.isEmpty) {
stream: devicesManagerBloc.streamAllDevices, devicesManagerBloc
builder: (context, allDeviceSnapshot) { .getDeviceByState(tagSnapshot.data?[0] ?? -2);
if(allDeviceSnapshot.data == null){ return const Center(child: CircularProgressIndicator());
devicesManagerBloc } else {
.getDeviceByState(context,tagSnapshot.data?[0] ?? -2); if (tagSnapshot.data!.isNotEmpty) {
return const SharedLoadingAnimation(); tagIndex = tagSnapshot.data![0];
} devicesManagerBloc.sinkTagStates.add([tagIndex]);
if (allDeviceSnapshot.data!.isEmpty) { }
return Center( return SingleChildScrollView(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, children: [
crossAxisAlignment: CrossAxisAlignment.center, if (tagSnapshot.hasData &&
children: [ tagSnapshot.data!.isNotEmpty &&
Container( tagSnapshot.data![0] != -2)
width: 100, TagState(
height: 100, state: tagSnapshot.data![0],
decoration: BoxDecoration( devicesManagerBloc: devicesManagerBloc,
border: Border.all(color: Theme.of(context).colorScheme.primary), ),
borderRadius: BorderRadius.circular(50) SizedBox(height: context.lowValue),
), StreamBuilder<String>(
child: IconButton(onPressed: (){ stream: devicesManagerBloc.streamUserRole,
ScaffoldMessenger.of(context) initialData: role,
.clearSnackBars(); builder: (context, roleSnapshot) {
addNewDevice(context, return SizedBox(
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 {
if (tagSnapshot.data!.isNotEmpty) {
tagIndex = tagSnapshot.data![0];
devicesManagerBloc.sinkTagStates.add([tagIndex]);
}
return SingleChildScrollView(
child: Column(
children: [
if (tagSnapshot.hasData &&
tagSnapshot.data!.isNotEmpty &&
tagSnapshot.data![0] != -2)
TagState(
state: tagSnapshot.data![0],
devicesManagerBloc: devicesManagerBloc,
),
SizedBox(height: context.lowValue),
SizedBox(
height: getTableHeight(allDeviceSnapshot.data?.length ?? 1), height: getTableHeight(allDeviceSnapshot.data?.length ?? 1),
child: PaginatedDataTable2( child: PaginatedDataTable2(
wrapInCard: false, wrapInCard: false,
@@ -129,16 +103,15 @@ class _DevicesManagerScreenState extends State<DevicesManagerScreen> {
child: Text( child: Text(
appLocalization(context) appLocalization(context)
.paginated_data_table_title, .paginated_data_table_title,
style: context.responsiveBodyLargeWithBold, style: context.headlineMediumTextStyle,
), ),
), ),
columns: [ columns: [
if (roleSnapshot.data == if (roleSnapshot.data ==
RoleEnums.ADMIN.name || RoleEnums.ADMIN.name ||
roleSnapshot.data == roleSnapshot.data ==
RoleEnums.USER.name) RoleEnums.USER.name)
DataColumn2( DataColumn(
fixedWidth: context.dynamicWidth(0.3),
label: Text(appLocalization(context) label: Text(appLocalization(context)
.paginated_data_table_column_deviceName), .paginated_data_table_column_deviceName),
), ),
@@ -175,23 +148,23 @@ class _DevicesManagerScreenState extends State<DevicesManagerScreen> {
// log('Chuyen page: $pageIndex'); // log('Chuyen page: $pageIndex');
}, },
rowsPerPage: rowsPerPage:
(allDeviceSnapshot.data?.length ?? 1) < 6 (allDeviceSnapshot.data?.length ?? 1) < 6
? (allDeviceSnapshot.data?.length ?? ? (allDeviceSnapshot.data?.length ??
0) 0)
: 5, : 5,
actions: [ actions: [
if (roleSnapshot.data == if (roleSnapshot.data ==
RoleEnums.USER.name || RoleEnums.USER.name ||
roleSnapshot.data == roleSnapshot.data ==
RoleEnums.ADMIN.name) RoleEnums.ADMIN.name)
IconButton( IconButton(
style: ButtonStyle( style: ButtonStyle(
backgroundColor: backgroundColor:
WidgetStateProperty.all<Color>( WidgetStateProperty.all<Color>(
Colors.green), Colors.green),
iconColor: iconColor:
WidgetStateProperty.all<Color>( WidgetStateProperty.all<Color>(
Colors.white, Colors.white,
), ),
), ),
@@ -199,48 +172,52 @@ class _DevicesManagerScreenState extends State<DevicesManagerScreen> {
ScaffoldMessenger.of(context) ScaffoldMessenger.of(context)
.clearSnackBars(); .clearSnackBars();
addNewDevice(context, addNewDevice(context,
roleSnapshot.data ?? role, devicesManagerBloc); roleSnapshot.data ?? role);
}, },
icon: IconConstants.instance icon: IconConstants.instance
.getMaterialIcon(Icons.add)) .getMaterialIcon(Icons.add))
], ],
source: DeviceSource( source: DeviceSource(
devices: allDeviceSnapshot.data ?? [], devices: allDeviceSnapshot.data ?? devices,
context: context, context: context,
devicesBloc: devicesManagerBloc, devicesBloc: devicesManagerBloc,
role: role, role: role,
), ),
), ),
), );
SizedBox(height: context.lowValue), },
Text(
appLocalization(context).overview_message,
style: context.responsiveBodyLargeWithBold
),
StreamBuilder<Map<String, List<Device>>>(
stream: devicesManagerBloc.streamDeviceByState,
builder: (context, devicesByStateSnapshot) {
if (devicesByStateSnapshot.data == null) {
devicesManagerBloc.getDeviceByState(context,
tagSnapshot.data?[0] ?? -2);
return const SharedComponentLoadingAnimation();
} else {
return SharedPieChart(
deviceByState:
devicesByStateSnapshot.data ?? {},
devicesManagerBloc: devicesManagerBloc,
);
}
},
),
],
), ),
); SizedBox(height: context.lowValue),
} Text(
}, appLocalization(context).overview_message,
), style: const TextStyle(
); fontSize: 20,
} fontWeight: FontWeight.bold,
),
),
StreamBuilder<Map<String, List<Device>>>(
stream: devicesManagerBloc.streamDeviceByState,
builder: (context, devicesByStateSnapshot) {
if (devicesByStateSnapshot.data == null) {
devicesManagerBloc.getDeviceByState(
tagSnapshot.data?[0] ?? -2);
return const Center(
child: CircularProgressIndicator());
} else {
return SharedPieChart(
deviceByState:
devicesByStateSnapshot.data ?? {},
devicesManagerBloc: devicesManagerBloc,
);
}
},
),
],
),
);
}
},
),
); );
}), }),
); );
@@ -253,7 +230,7 @@ class _DevicesManagerScreenState extends State<DevicesManagerScreen> {
double getTableHeight(int dataLength){ double getTableHeight(int dataLength){
if(dataLength < 3){ if(dataLength < 3){
return context.dynamicHeight(0.35); return context.dynamicHeight(0.3);
}else { }else {
return context.dynamicHeight(0.4); return context.dynamicHeight(0.4);
} }
@@ -393,7 +370,7 @@ class TagState extends StatelessWidget {
), ),
GestureDetector( GestureDetector(
onTap: () { onTap: () {
devicesManagerBloc.getDeviceByState(context,-2); devicesManagerBloc.getDeviceByState(-2);
}, },
child: const Icon( child: const Icon(
Icons.close, Icons.close,

View File

@@ -1,8 +1,7 @@
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 '../../product/constant/enums/app_route_enums.dart'; import 'package:sfm_app/product/constant/image/image_constants.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,12 +1,11 @@
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 '../../product/shared/shared_loading_animation.dart'; import 'package:flutter/material.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';
@@ -18,6 +17,7 @@ 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,284 +25,305 @@ 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);
WidgetsBinding.instance.addPostFrameCallback((_) { getAllDevicesTimer = Timer.periodic(duration, (Timer t) => getOwnerAndJoinedDevices());
// 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>?>( return Scaffold(
stream: homeBloc.streamAliasDevices, body: Padding(
builder: (context, aliasDevicesSnapshot) { padding: context.paddingLow,
if (aliasDevicesSnapshot.data == null) { child: SingleChildScrollView(
homeBloc.getOwnerAndJoinedDevices(context); child: Column(
return const SharedLoadingAnimation(); children: <Widget>[
} else { Row(
homeBloc.getOwnerDeviceState( children: [
context, aliasDevicesSnapshot.data ?? []); Text(
homeBloc.getDeviceStatusAliasMap(aliasDevicesSnapshot.data ?? []); appLocalization(context).notification,
checkSettingDevice(aliasDevicesSnapshot.data ?? []); style: context.titleMediumTextStyle,
return Scaffold( ),
floatingActionButton: FloatingActionButton( SizedBox(width: context.lowValue),
onPressed: () { StreamBuilder<int>(
loadAlarms(); stream: homeBloc.streamCountNotification,
}, builder: (context, countSnapshot) {
child: const Icon(Icons.alarm), return Text(
"(${countSnapshot.data ?? 0})",
style: context.titleMediumTextStyle,
);
},
)
],
), ),
body: Padding( SizedBox(
padding: context.paddingLow,
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( scrollDirection: Axis.horizontal,
children: <Widget>[ child: StreamBuilder<Map<String, List<DeviceWithAlias>>>(
Row( stream: homeBloc.streamOwnerDevicesStatus,
children: [ builder: (context, snapshot) {
Text( if (snapshot.data?['state'] != null || snapshot.data?['battery'] != null) {
appLocalization(context).notification, return Row(
style: context.titleMediumTextStyle, mainAxisAlignment: MainAxisAlignment.start,
), crossAxisAlignment: CrossAxisAlignment.start,
SizedBox(width: context.lowValue), children: [
StreamBuilder<int>( if (snapshot.data?['state'] != null)
stream: homeBloc.streamCountNotification, ...snapshot.data!['state']!
builder: (context, countSnapshot) { .map(
if (countSnapshot.data == null) { (item) => FutureBuilder<Widget>(
homeBloc.getOwnerDeviceState( future: warningCard(context, apiServices, item),
context, aliasDevicesSnapshot.data ?? []); builder: (context, warningCardSnapshot) {
return const Text("0"); if (warningCardSnapshot.hasData) {
} else { return ConstrainedBox(
return Text( constraints: const BoxConstraints(
"(${countSnapshot.data ?? 0})", maxWidth: 400,
style: context.titleMediumTextStyle, maxHeight: 260,
);
}
},
)
],
),
SizedBox(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child:
StreamBuilder<Map<String, List<DeviceWithAlias>>>(
stream: homeBloc.streamOwnerDevicesStatus,
builder: (context, ownerDevicesStatusSnapshot) {
if (ownerDevicesStatusSnapshot.data == null) {
homeBloc.getOwnerDeviceState(
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(
mainAxisAlignment:
MainAxisAlignment.start,
children: [
if (ownerDevicesStatusSnapshot
.data?['state'] !=
null)
...ownerDevicesStatusSnapshot
.data!['state']!
.map(
(item) => SizedBox(
width: context
.dynamicWidth(0.95),
child: FutureBuilder<
Widget>(
future: warningCard(
context,
apiServices,
item),
builder: (context,
warningCardSnapshot) {
if (warningCardSnapshot
.hasData) {
return warningCardSnapshot
.data!;
} else {
return const SizedBox
.shrink();
}
},
),
),
)
.toList(),
if (ownerDevicesStatusSnapshot
.data?['battery'] !=
null)
...ownerDevicesStatusSnapshot
.data!['battery']!
.map(
(batteryItem) => SizedBox(
width: context
.dynamicWidth(0.95),
child: FutureBuilder<
Widget>(
future:
notificationCard(
context,
"lowBattery",
appLocalization(
context)
.low_battery_message,
batteryItem,
),
builder: (context,
warningCardSnapshot) {
if (warningCardSnapshot
.hasData) {
return warningCardSnapshot
.data!;
} else {
return const SizedBox
.shrink();
}
},
),
),
)
.toList(),
],
),
)
: Padding(
key: const ValueKey('no_data'),
padding: context.paddingMedium,
child: Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons
.check_circle_outline_rounded,
size: 40,
color: Colors.green,
),
SizedBox(
width: context.lowValue),
Text(
appLocalization(context)
.notification_description,
maxLines: 2,
overflow:
TextOverflow.ellipsis,
softWrap: true,
textAlign: TextAlign.start,
),
],
), ),
), child: warningCardSnapshot.data!,
), );
); } else {
} return const SizedBox.shrink();
}, }
},
),
)
.toList(),
if (snapshot.data?['battery'] != null)
...snapshot.data!['battery']!
.map(
(batteryItem) => FutureBuilder<Widget>(
future: notificationCard(
context, "lowBattery", appLocalization(context).low_battery_message, batteryItem),
builder: (context, warningCardSnapshot) {
if (warningCardSnapshot.hasData) {
return ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 400,
maxHeight: 260,
),
child: warningCardSnapshot.data!,
);
} else {
return const SizedBox.shrink();
}
},
),
)
.toList(),
],
);
} else {
return Padding(
padding: context.paddingMedium,
child: Center(
child: Row(
children: [
const Icon(
Icons.check_circle_outline_rounded,
size: 40,
color: Colors.green,
),
SizedBox(width: context.lowValue),
Text(
appLocalization(context).notification_description,
maxLines: 2,
overflow: TextOverflow.ellipsis,
softWrap: true,
textAlign: TextAlign.start,
),
],
),
), ),
), );
), }
StreamBuilder<Map<String, List<DeviceWithAlias>>?>( },
stream: homeBloc.streamAllDevicesAliasMap,
builder: (context, allDevicesAliasMapSnapshot) {
if (allDevicesAliasMapSnapshot.data == null) {
homeBloc.getDeviceStatusAliasMap(
aliasDevicesSnapshot.data ?? []);
return const SharedComponentLoadingAnimation();
} else {
final data = allDevicesAliasMapSnapshot.data!;
return OverviewCard(
isOwner: true,
total: data['all']?.length ?? 0,
active: data['online']?.length ?? 0,
inactive: data['offline']?.length ?? 0,
warning: data['warn']?.length ?? 0,
unused: data['not-use']?.length ?? 0,
showUnused: false,
);
}
},
),
],
), ),
), ),
), ),
); StreamBuilder<Map<String, List<DeviceWithAlias>>>(
stream: homeBloc.streamAllDevicesAliasMap,
builder: (context, allDevicesAliasMapSnapshot) {
if (!allDevicesAliasMapSnapshot.hasData ||
allDevicesAliasMapSnapshot.data == null) {
return const Center(child: CircularProgressIndicator());
}
final data = allDevicesAliasMapSnapshot.data!;
return OverviewCard(
isOwner: true,
total: data['all']?.length ?? 0,
active: data['online']?.length ?? 0,
inactive: data['offline']?.length ?? 0,
warning: data['warn']?.length ?? 0,
unused: data['not-use']?.length ?? 0);
}),
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,
);
},
),
],
),
),
),
);
}
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 {
await apiServices.execute(context, () async { String? response = await apiServices.getAllSettingsNotificationOfDevices();
if (response != "") {
final data = jsonDecode(response);
final result = data['data'];
// log("Data ${DeviceNotificationSettings.mapFromJson(jsonDecode(data)).values.toList()}");
List<DeviceNotificationSettings> list = List<DeviceNotificationSettings> list =
await apiServices.getAllSettingsNotificationOfDevices(); DeviceNotificationSettings.mapFromJson(result).values.toList();
// log("List: $list"); // log("List: $list");
Set<String> thingIdsInList = Set<String> thingIdsInList = list.map((device) => device.thingId!).toSet();
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.execute(context, () async { await apiServices.setupDeviceNotification(device.thingId!, device.name!);
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 '../device_alias_model.dart';
Future<Widget> notificationCard(BuildContext context, String notificationType, import '../../../product/constant/icon/icon_constants.dart';
Future<Widget> notificationCard(BuildContext context, String notiticationType,
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 notificationType,
time = DateFormat('yyyy-MM-dd HH:mm:ss').format(dateTime); time = DateFormat('yyyy-MM-dd HH:mm:ss').format(dateTime);
} }
} }
if (notificationType == "lowBattery") { if (notiticationType == "lowBattery") {
path = ImageConstants.instance.getImage("low_battery"); path = ImageConstants.instance.getImage("low_battery");
} }
return Card( return Card(
@@ -62,7 +62,10 @@ Future<Widget> notificationCard(BuildContext context, String notificationType,
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: context.responsiveBodyLargeWithBold, style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
softWrap: true, softWrap: true,
@@ -89,7 +92,7 @@ Future<Widget> notificationCard(BuildContext context, String notificationType,
Expanded( Expanded(
child: Text( child: Text(
location, location,
style: context.responsiveBodySmall, style: const TextStyle(fontSize: 15),
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
softWrap: true, softWrap: true,
@@ -108,7 +111,7 @@ Future<Widget> notificationCard(BuildContext context, String notificationType,
Expanded( Expanded(
child: Text( child: Text(
time.toString(), time.toString(),
style: context.responsiveBodySmall, style: const TextStyle(fontSize: 15),
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
softWrap: true, softWrap: true,
@@ -117,46 +120,24 @@ Future<Widget> notificationCard(BuildContext context, String notificationType,
), ),
], ],
), ),
SizedBox( Align(
height: context.lowValue, alignment: Alignment.centerRight,
), child: OutlinedButton(
device.isOwner! style: const ButtonStyle(
? Align( backgroundColor: WidgetStatePropertyAll(Colors.blueAccent),
alignment: Alignment.centerRight, ),
child: OutlinedButton( onPressed: () {
style: const ButtonStyle( context.pushNamed(AppRoutes.DEVICE_DETAIL.name,
backgroundColor: pathParameters: {'thingID': device.thingId!});
WidgetStatePropertyAll(Colors.blueAccent), },
), child: Text(
onPressed: () { appLocalization(context).detail_message,
context.pushNamed(AppRoutes.DEVICE_DETAIL.name, style: const TextStyle(
pathParameters: {'thingID': device.thingId!}); color: Colors.white,
},
child: Text(
appLocalization(context).detail_message,
style: const TextStyle(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,100 +1,75 @@
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 StatefulWidget { class OverviewCard extends StatelessWidget {
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 FittedBox( return Card(
alignment: Alignment.topCenter, margin: context.paddingLow,
child: SizedBox( elevation: 8,
width: context.width, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
child: Card( child: Padding(
// elevation: 8, padding: context.paddingNormal,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), child: Column(
child: Padding( children: [
padding: context.paddingNormal, Text(
child: Column( isOwner
mainAxisSize: MainAxisSize.min, ? appLocalization(context).overview_message
: appLocalization(context).interfamily_page_name,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: context.normalValue),
Column(
children: [ children: [
Text( StatusCard(
widget.isOwner label: appLocalization(context).total_nof_devices_message,
? appLocalization(context).overview_message count: total,
: appLocalization(context).interfamily_page_name, color: Colors.blue,
style: context.h2,
), ),
SizedBox(height: context.normalValue), StatusCard(
Column( label: appLocalization(context).active_devices_message,
children: [ count: active,
if (widget.showTotal) color: Colors.green,
StatusCard( ),
label: appLocalization(context).total_nof_devices_message, StatusCard(
count: widget.total, label: appLocalization(context).inactive_devices_message,
color: Colors.blue, count: inactive,
), color: Colors.grey,
if (widget.showActive) ),
StatusCard( StatusCard(
label: appLocalization(context).active_devices_message, label: appLocalization(context).warning_devices_message,
count: widget.active, count: warning,
color: Colors.green, color: Colors.orange,
), ),
if (widget.showInactive) StatusCard(
StatusCard( label: appLocalization(context).unused_devices_message,
label: appLocalization(context).inactive_devices_message, count: unused,
count: widget.inactive, color: Colors.yellow,
color: Colors.grey,
),
if (widget.showWarning)
StatusCard(
label: appLocalization(context).warning_devices_message,
count: widget.warning,
color: Colors.orange,
),
if (widget.showUnused)
StatusCard(
label: appLocalization(context).unused_devices_message,
count: widget.unused,
color: Colors.yellow,
),
],
), ),
], ],
), ),
), ],
), ),
), ),
); );

View File

@@ -1,7 +1,5 @@
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;
@@ -29,8 +27,14 @@ class StatusCard extends StatelessWidget {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text(label, style: context.responsiveBodyLarge), Text(label, style: const TextStyle(fontSize: 18)),
Text(count.toString(), style: context.h2), Text(
count.toString(),
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
], ],
), ),
); );

View File

@@ -3,8 +3,7 @@
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';
@@ -22,7 +21,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!) {
@@ -34,7 +33,7 @@ Future<Widget> warningCard(BuildContext context, APIServices apiServices,
} }
if (device.state! == 3) { if (device.state! == 3) {
backgroundColor = Colors.grey; backgroundColor = Colors.grey;
textColor = Colors.white; textColor = Colors.black;
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);
@@ -43,18 +42,32 @@ 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 Card( return badges.Badge(
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(
@@ -84,7 +97,10 @@ 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: context.responsiveBodyLargeWithBold, style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
softWrap: true, softWrap: true,
@@ -112,7 +128,7 @@ Future<Widget> warningCard(BuildContext context, APIServices apiServices,
Expanded( Expanded(
child: Text( child: Text(
fullLocation, fullLocation,
style: context.responsiveBodySmall, style: const TextStyle(fontSize: 15),
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
softWrap: true, softWrap: true,
@@ -131,7 +147,7 @@ Future<Widget> warningCard(BuildContext context, APIServices apiServices,
Expanded( Expanded(
child: Text( child: Text(
time, time,
style: context.responsiveBodySmall, style: const TextStyle(fontSize: 15),
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
softWrap: true, softWrap: true,
@@ -143,158 +159,114 @@ Future<Widget> warningCard(BuildContext context, APIServices apiServices,
SizedBox( SizedBox(
height: context.lowValue, height: context.lowValue,
), ),
device.isOwner == true Row(
? 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: iconSize: 25,
IconConstants.instance.getMaterialIcon(Icons.call), style: ButtonStyle(
iconSize: 25, backgroundColor:
style: ButtonStyle( WidgetStateProperty.all<Color>(Colors.blue[300]!),
backgroundColor:
WidgetStateProperty.all<Color>(Colors.blue[300]!),
),
),
const SizedBox(width: 10),
IconButton.outlined(
onPressed: () async {
String markerLabel = "Destination";
MapsLauncher.launchCoordinates(
double.parse(device.settings!.latitude!),
double.parse(device.settings!.longitude!),
markerLabel);
},
icon: const Icon(Icons.directions),
iconSize: 25,
style: ButtonStyle(
backgroundColor:
WidgetStateProperty.all<Color>(Colors.blue[300]!),
),
),
SizedBox(width: context.mediumValue),
Expanded(
child: Align(
alignment: Alignment.centerRight,
child: OutlinedButton(
style: ButtonStyle(
backgroundColor:
WidgetStatePropertyAll(backgroundColor)),
onPressed: () async {
if (message ==
appLocalization(context)
.button_fake_fire_message) {
await showDialog(
context: context,
builder: (context) => AlertDialog(
icon: const Icon(Icons.warning),
iconColor: Colors.red,
title: Text(appLocalization(context)
.confirm_fake_fire_message),
content: Text(appLocalization(context)
.confirm_fake_fire_body),
actions: [
TextButton(
onPressed: () async {
await apiServices.execute(context,
() async {
int statusCode = await apiServices
.confirmFakeFireByUser(
device.thingId!);
if (statusCode == 200) {
showNoIconTopSnackBar(
context,
appLocalization(context)
.notification_confirm_fake_fire_success,
Colors.green,
Colors.white);
} else {
showNoIconTopSnackBar(
context,
appLocalization(context)
.notification_confirm_fake_fire_failed,
Colors.red,
Colors.red);
}
});
Navigator.of(context).pop();
},
child: Text(
appLocalization(context)
.confirm_fake_fire_sure_message,
style: const TextStyle(
color: Colors.red)),
),
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(appLocalization(context)
.cancel_button_content),
),
],
),
);
} else {
showNoIconTopSnackBar(
context,
appLocalization(context)
.let_PCCC_handle_message,
Colors.orange,
Colors.white);
}
},
child: Text(
message,
style: TextStyle(color: textColor),
),
),
),
),
],
)
: 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),
),
),
),
),
],
), ),
),
const SizedBox(width: 10),
IconButton.outlined(
onPressed: () async {
String markerLabel = "Destination";
MapsLauncher.launchCoordinates(
double.parse(device.settings!.latitude!),
double.parse(device.settings!.longitude!),
markerLabel);
},
icon: const Icon(Icons.directions),
iconSize: 25,
style: ButtonStyle(
backgroundColor:
WidgetStateProperty.all<Color>(Colors.blue[300]!),
),
),
SizedBox(width: context.mediumValue),
Expanded(
child: Align(
alignment: Alignment.centerRight,
child: OutlinedButton(
style: ButtonStyle(
backgroundColor:
WidgetStatePropertyAll(backgroundColor)),
onPressed: () async {
if (message ==
appLocalization(context).button_fake_fire_message) {
await showDialog(
context: context,
builder: (context) => AlertDialog(
icon: const Icon(Icons.warning),
iconColor: Colors.red,
title: Text(appLocalization(context)
.confirm_fake_fire_message),
content: Text(appLocalization(context)
.confirm_fake_fire_body),
actions: [
TextButton(
onPressed: () async {
int statusCode = await apiServices
.confirmFakeFireByUser(device.thingId!);
if (statusCode == 200) {
showNoIconTopSnackBar(
context,
appLocalization(context)
.notification_confirm_fake_fire_success,
Colors.green,
Colors.white);
} else {
showNoIconTopSnackBar(
context,
appLocalization(context)
.notification_confirm_fake_fire_failed,
Colors.red,
Colors.red);
}
Navigator.of(context).pop();
},
child: Text(
appLocalization(context)
.confirm_fake_fire_sure_message,
style:
const TextStyle(color: Colors.red)),
),
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(appLocalization(context)
.cancel_button_content),
),
],
),
);
} else {
showNoIconTopSnackBar(
context,
appLocalization(context).let_PCCC_handle_message,
Colors.orange,
Colors.white);
}
},
child: Text(
message,
style: TextStyle(color: textColor),
),
),
),
),
],
),
], ],
), ),
), ),
); ),
);
} }

View File

@@ -2,17 +2,16 @@
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(context); List<Device> ownerDevices = await detailGroupBloc.getOwnerDevices();
List<String> selectedItems = []; List<String> selectedItems = [];
List<String> selectedDevices = []; List<String> selectedDevices = [];
if (devices.isNotEmpty) { if (devices.isNotEmpty) {
@@ -37,7 +36,7 @@ addDeviceDialog(BuildContext context, DetailGroupBloc detailGroupBloc,
), ),
hint: Text( hint: Text(
appLocalization(context).choose_device_dropdownButton, appLocalization(context).choose_device_dropdownButton,
style: context.responsiveBodySmall, style: const TextStyle(fontSize: 14),
), ),
items: ownerDevices items: ownerDevices
.map( .map(
@@ -74,7 +73,7 @@ addDeviceDialog(BuildContext context, DetailGroupBloc detailGroupBloc,
Expanded( Expanded(
child: Text( child: Text(
item.name!, item.name!,
style: context.responsiveBodySmall, style: const TextStyle(fontSize: 14),
), ),
), ),
], ],
@@ -131,7 +130,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(context,groupID); await detailGroupBloc.getGroupDetail(groupID);
} }
Navigator.of(dialogContext).pop(); Navigator.of(dialogContext).pop();

View File

@@ -1,10 +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 '../../../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';
@@ -14,6 +13,7 @@ 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(context, widget.group), (Timer t) => detailGroupBloc.getGroupDetail(widget.group),
); );
} }
@override @override
void dispose() { void dispose() {
getGroupDetailTimer?.cancel(); getGroupDetailTimer?.cancel();
super.dispose();
} }
@override @override
@@ -51,8 +51,10 @@ 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(context, widget.group); detailGroupBloc.getGroupDetail(widget.group);
return const SharedLoadingAnimation(); return const Center(
child: CircularProgressIndicator(),
);
} else { } else {
return Scaffold( return Scaffold(
key: scaffoldKey, key: scaffoldKey,
@@ -138,8 +140,8 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
widget.group, widget.group,
user.id!, user.id!,
user.name!); user.name!);
detailGroupBloc.getGroupDetail( detailGroupBloc
context, widget.group); .getGroupDetail(widget.group);
}, },
icon: const Icon( icon: const Icon(
Icons.check, Icons.check,
@@ -156,8 +158,8 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
widget.group, widget.group,
user.id!, user.id!,
user.name!); user.name!);
await detailGroupBloc.getGroupDetail( await detailGroupBloc
context, widget.group); .getGroupDetail(widget.group);
}, },
icon: const Icon( icon: const Icon(
Icons.close, Icons.close,
@@ -203,8 +205,8 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
widget.group, widget.group,
user.id!, user.id!,
user.name!); user.name!);
await detailGroupBloc.getGroupDetail( await detailGroupBloc
context, widget.group); .getGroupDetail(widget.group);
}, },
value: 2, value: 2,
child: Text(appLocalization(context) child: Text(appLocalization(context)
@@ -237,7 +239,7 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
? PopupMenuButton( ? PopupMenuButton(
icon: IconConstants.instance icon: IconConstants.instance
.getMaterialIcon(Icons.more_horiz), .getMaterialIcon(Icons.more_horiz),
itemBuilder: (context) => [ itemBuilder: (contex) => [
PopupMenuItem( PopupMenuItem(
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
@@ -323,17 +325,15 @@ 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( context,
context, statusCode,
statusCode, appLocalization(context)
appLocalization(context) .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 SafeArea( return Expanded(
child: Center( child: Center(
child: devices.isEmpty child: devices.isEmpty
? Center( ? Center(
@@ -461,8 +461,7 @@ 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( color: DeviceUtils.instance.getTableRowColor(context,
context,
devices[index].state!, devices[index].state!,
), ),
), ),
@@ -494,7 +493,10 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text('${appLocalization(context).map_result}: ', Text('${appLocalization(context).map_result}: ',
style: context.h3), style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.black)),
Container( Container(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: IconButton( child: IconButton(
@@ -524,7 +526,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(context, widget.group); await detailGroupBloc.getGroupDetail(widget.group);
}, },
child: Text(appLocalization(context).confirm_button_content)), child: Text(appLocalization(context).confirm_button_content)),
) )

View File

@@ -1,125 +1,165 @@
// 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';
/// Stateless widget that renders a provided list of groups. The parent import 'groups_widget.dart';
/// screen owns fetching/updating the list; this widget only displays it and
/// forwards actions to the provided [InterFamilyBloc]. class GroupsScreen extends StatefulWidget {
class GroupsScreen extends StatelessWidget { const GroupsScreen({super.key, required this.role});
const GroupsScreen(
{super.key,
required this.role,
required this.groups,
required this.interFamilyBloc});
final String role; final String role;
final List<Group> groups;
final InterFamilyBloc interFamilyBloc; @override
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 (role != ApplicationConstants.OWNER_GROUP && if (widget.role == ApplicationConstants.OWNER_GROUP ||
role != ApplicationConstants.PARTICIPANT_GROUP) { widget.role == ApplicationConstants.PARTICIPANT_GROUP) {
interFamilyBloc.getAllGroup(widget.role);
return StreamBuilder<List<Group>>(
stream: interFamilyBloc.streamCurrentGroups,
builder: (context, groupsSnapshot) {
return Scaffold(
body: groupsSnapshot.data?.isEmpty ?? true
? const Center(
child: CircularProgressIndicator(),
)
: ListView.builder(
itemCount: groupsSnapshot.data!.length,
itemBuilder: (context, index) {
return ListTile(
onTap: () {
context.pushNamed(AppRoutes.GROUP_DETAIL.name,
pathParameters: {"groupId": groupsSnapshot.data![index].id!},
extra: widget.role);
},
leading: IconConstants.instance.getMaterialIcon(Icons.diversity_2),
title: Text(
groupsSnapshot.data![index].name ?? '',
style: const TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text(groupsSnapshot.data![index].description ?? ""),
trailing: widget.role == ApplicationConstants.OWNER_GROUP
? PopupMenuButton(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(8.0),
bottomRight: Radius.circular(8.0),
topLeft: Radius.circular(8.0),
topRight: Radius.circular(8.0),
),
),
itemBuilder: (ctx) => [
_buildPopupMenuItem(groupsSnapshot.data![index], context,
appLocalization(context).share_group_title, Icons.share, 4),
_buildPopupMenuItem(
groupsSnapshot.data![index],
context,
appLocalization(context).change_group_infomation_title,
Icons.settings_backup_restore,
2),
_buildPopupMenuItem(
groupsSnapshot.data![index],
context,
appLocalization(context).delete_group_title,
Icons.delete_forever_rounded,
3),
],
icon: const Icon(Icons.more_horiz),
)
: const SizedBox.shrink(),
);
},
),
);
},
);
} else {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
if (groups.isEmpty) {
return Center(child: Text(appLocalization(context).dont_have_group));
}
return ListView.builder(
itemCount: groups.length,
itemBuilder: (context, index) {
final group = groups[index];
return ListTile(
onTap: () => context.pushNamed(AppRoutes.GROUP_DETAIL.name,
pathParameters: {"groupId": group.id!}, extra: role),
leading: IconConstants.instance.getMaterialIcon(Icons.diversity_2),
title: Text(group.name ?? '',
style: const TextStyle(fontWeight: FontWeight.bold)),
subtitle: Text(group.description ?? ''),
trailing: role == ApplicationConstants.OWNER_GROUP
? _ownerPopupMenu(group, context)
: null,
);
},
);
} }
Widget _ownerPopupMenu(Group group, BuildContext context) { PopupMenuItem _buildPopupMenuItem(
return PopupMenuButton<int>( Group group, BuildContext context, String title, IconData iconData, int position) {
shape: const RoundedRectangleBorder( return PopupMenuItem(
borderRadius: BorderRadius.all(Radius.circular(8.0))),
itemBuilder: (ctx) => [
_buildPopupMenuItem(group, context,
appLocalization(context).share_group_title, Icons.share, 4),
_buildPopupMenuItem(
group,
context,
appLocalization(context).change_group_infomation_title,
Icons.settings_backup_restore,
2),
_buildPopupMenuItem(
group,
context,
appLocalization(context).delete_group_title,
Icons.delete_forever_rounded,
3),
],
icon: const Icon(Icons.more_horiz),
);
}
PopupMenuItem<int> _buildPopupMenuItem(Group group, BuildContext context,
String title, IconData iconData, int value) {
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 == });
appLocalization(context).change_group_infomation_title) { } else if (title == appLocalization(context).change_group_infomation_title) {
Future.delayed(context.lowDuration, () {
createOrJoinGroupDialog( createOrJoinGroupDialog(
context, context,
interFamilyBloc, interFamilyBloc,
role, widget.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,
role, widget.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,8 +80,7 @@ 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!);
// ignore: use_build_context_synchronously interFamilyBloc.getAllGroup(role);
interFamilyBloc.getAllGroup(context,role);
} else {} } else {}
}, },
child: Text( child: Text(

View File

@@ -1,7 +1,6 @@
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';
@@ -26,31 +25,22 @@ 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);
});
} }
List<Group> ownerGroups = []; final _widgetOptions = <Widget>[
List<Group> participantGroups = []; BlocProvider(
blocBuilder: () => InterFamilyBloc(),
List<Widget> get _widgetOptions => [ child: const GroupsScreen(
GroupsScreen( role: ApplicationConstants.OWNER_GROUP,
role: ApplicationConstants.OWNER_GROUP, ),
groups: ownerGroups, ),
interFamilyBloc: interFamilyBloc, BlocProvider(
), blocBuilder: () => InterFamilyBloc(),
GroupsScreen( child: const GroupsScreen(
role: ApplicationConstants.PARTICIPANT_GROUP, role: ApplicationConstants.PARTICIPANT_GROUP,
groups: participantGroups, ),
interFamilyBloc: interFamilyBloc, ),
), ];
];
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -59,109 +49,96 @@ 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 Scaffold(
return StreamBuilder<List<Group>>( appBar: AppBar(
stream: interFamilyBloc.streamCurrentGroups, actions: [
builder: (context, groupsSnapshot) { ElevatedButton(
if (groupsSnapshot.hasData) { onPressed: () {
final all = groupsSnapshot.data!; if (selectSnapshot.data == 0) {
ownerGroups = all createOrJoinGroupDialog(
.where((g) => g.isOwner == true && g.visibility == 'PUBLIC') context,
.toList(); interFamilyBloc,
participantGroups = all selectSnapshot.data! == 0
.where((g) => g.isOwner == null && g.visibility == 'PUBLIC') ? ApplicationConstants.OWNER_GROUP
.toList(); : ApplicationConstants.PARTICIPANT_GROUP,
} appLocalization(context).add_new_group,
// build UI below appLocalization(context).group_name_title,
return Scaffold( "",
appBar: AppBar( false,
actions: [ "",
ElevatedButton( "",
onPressed: () { "");
if (selectSnapshot.data == 0) { } else {
createOrJoinGroupDialog( createOrJoinGroupDialog(
context, context,
interFamilyBloc, interFamilyBloc,
ApplicationConstants.OWNER_GROUP, selectSnapshot.data! == 0
appLocalization(context).add_new_group, ? ApplicationConstants.OWNER_GROUP
appLocalization(context).group_name_title, : ApplicationConstants.PARTICIPANT_GROUP,
"", appLocalization(context).join_group,
false, appLocalization(context).group_id_title,
"", '',
"", true,
"", "",
); appLocalization(context).group_name_title,
} else { "");
createOrJoinGroupDialog( }
context, },
interFamilyBloc, style: ElevatedButton.styleFrom(
ApplicationConstants.PARTICIPANT_GROUP, shape: const CircleBorder(),
appLocalization(context).join_group, ),
appLocalization(context).group_id_title, child: IconConstants.instance.getMaterialIcon(Icons.add),
'', ),
true, ],
"", leading: Builder(
appLocalization(context).group_name_title, builder: (context) {
"", return IconButton(
); onPressed: () {
} Scaffold.of(context).openDrawer();
}, },
style: icon: const Icon(Icons.menu),
ElevatedButton.styleFrom(shape: const CircleBorder()), );
child: IconConstants.instance.getMaterialIcon(Icons.add), },
), ),
], title: StreamBuilder<String>(
leading: Builder( stream: interFamilyBloc.streamTitleScreen,
builder: (context) { initialData: title,
return IconButton( builder: (context, titleSnapshot) {
onPressed: () { return Center(
Scaffold.of(context).openDrawer(); child: Text(titleSnapshot.data ?? title),
}, );
icon: const Icon(Icons.menu), },
); ),
),
drawer: Drawer(
width: context.dynamicWidth(0.4),
child: ListView(
padding: EdgeInsets.zero,
children: [
ListTile(
title: Text(appLocalization(context).my_group_title),
selected: _selectedIndex == 0,
onTap: () {
_onItemTapped(0);
title = appLocalization(context).my_group_title;
interFamilyBloc.sinkTitleScreen.add(title);
Navigator.pop(context);
}, },
), ),
title: StreamBuilder<String>( ListTile(
stream: interFamilyBloc.streamTitleScreen, title: Text(appLocalization(context).invite_group),
initialData: title, selected: _selectedIndex == 1,
builder: (context, titleSnapshot) { onTap: () {
return Center( _onItemTapped(1);
child: Text(titleSnapshot.data ?? title), title = appLocalization(context).invite_group;
); interFamilyBloc.sinkTitleScreen.add(title);
Navigator.pop(context);
}, },
), ),
), ],
drawer: Drawer( ),
width: context.dynamicWidth(0.6), ),
child: ListView( body: _widgetOptions[selectSnapshot.data ?? _selectedIndex],
padding: EdgeInsets.zero,
children: [
ListTile(
title: Text(appLocalization(context).my_group_title),
selected: _selectedIndex == 0,
onTap: () {
_onItemTapped(0);
title = appLocalization(context).my_group_title;
interFamilyBloc.sinkTitleScreen.add(title);
Navigator.pop(context);
},
),
ListTile(
title: Text(appLocalization(context).invite_group),
selected: _selectedIndex == 1,
onTap: () {
_onItemTapped(1);
title = appLocalization(context).invite_group;
interFamilyBloc.sinkTitleScreen.add(title);
Navigator.pop(context);
},
),
],
),
),
body: _widgetOptions[selectSnapshot.data ?? _selectedIndex],
);
},
); );
}, },
); );
@@ -182,11 +159,5 @@ 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(context, role); interFamilyBloc.getAllGroup(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.changeGroupInformation( await interFamilyBloc.changeGroupInfomation(
context, groupID, groupName, description); context, groupID, groupName, description);
interFamilyBloc.getAllGroup(context, role); interFamilyBloc.getAllGroup(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,24 @@
// 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:flutter_local_notifications/flutter_local_notifications.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_v2/persistent_bottom_nav_bar_v2.dart'; import 'package:persistent_bottom_nav_bar/persistent_bottom_nav_bar.dart';
import 'package:sfm_app/product/utils/permission_handler.dart';
import '../../product/shared/shared_snack_bar.dart';
import '../../product/services/notification_services.dart';
import '../../product/utils/permission_handler.dart';
import '../../product/permission/notification_permission.dart'; import '../../product/permission/notification_permission.dart';
import '../../product/services/notification_services.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,10 +46,22 @@ class MainScreen extends StatefulWidget {
State<MainScreen> createState() => _MainScreenState(); State<MainScreen> createState() => _MainScreenState();
} }
class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver { 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 {
APIServices apiServices = APIServices(); APIServices apiServices = APIServices();
final NotificationServices notificationServices = NotificationServices(); // final NotificationServices notificationServices = NotificationServices();
late MainBloc mainBloc; late MainBloc mainBloc;
bool isVN = true; bool isVN = true;
bool isLight = true; bool isLight = true;
@@ -75,12 +89,7 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
mainBloc.sinkIsVNIcon.add(isVN); mainBloc.sinkIsVNIcon.add(isVN);
mainBloc.sinkThemeMode.add(isLight); mainBloc.sinkThemeMode.add(isLight);
checkAndRequestPermission(); checkAndRequestPermission();
NotificationServices.requestNotificationPermission(); NotificationPermission.instance.checkNotificationPermission(context);
// 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
@@ -91,7 +100,7 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addObserver(this);
initialCheck(); initialCheck();
getBellNotification(); getBellNotification();
mainBloc.getUserProfile(context); mainBloc.getUserProfile();
FirebaseMessaging.instance.onTokenRefresh.listen((newToken) { FirebaseMessaging.instance.onTokenRefresh.listen((newToken) {
log("New FCM Token: $newToken"); log("New FCM Token: $newToken");
@@ -99,86 +108,129 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
mainBloc.sendNotificationToken(newToken); mainBloc.sendNotificationToken(newToken);
}); });
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification?.android;
if (notification != null && android != null ) {
const AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails(
'your channel id', 'your channel name',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker');
const NotificationDetails platformChannelSpecifics =
NotificationDetails(android: androidPlatformChannelSpecifics);
flutterLocalNotificationsPlugin.show(
notification.hashCode, notification.title, notification.body, platformChannelSpecifics,
);
}
});
// notificationServices.initLocalNotifications(controller); // notificationServices.initLocalNotifications(controller);
// notificationServices.firebaseInit(context); // notificationServices.firebaseInit(context);
// NotificationServices().setupInteractMessage(controller); // NotificationServices().setupInteractMessage(controller);
} }
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.inactive) {
log("App Inactive");
} else if (state == AppLifecycleState.resumed) {
log("App Resumed");
} else if (state == AppLifecycleState.paused) {
log("App paused");
} else if (state == AppLifecycleState.detached) {
log("App detached");
}
}
@override @override
void dispose() { void dispose() {
super.dispose(); super.dispose();
WidgetsBinding.instance.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
} }
@override
Widget build(BuildContext context) { List<PersistentBottomNavBarItem> _navBarsItems() {
List<PersistentTabConfig> tabs = [ return [
PersistentTabConfig( PersistentBottomNavBarItem(
screen: BlocProvider( icon: IconConstants.instance.getMaterialIcon(Icons.home),
child: const HomeScreen(), title: appLocalization(context).home_page_destination,
blocBuilder: () => HomeBloc(), activeColorPrimary: Colors.blue,
), inactiveColorPrimary: Colors.grey,
item: ItemConfig( inactiveIcon:
icon: IconConstants.instance.getMaterialIcon(Icons.home), IconConstants.instance.getMaterialIcon(Icons.home_outlined),
title: appLocalization(context).home_page_destination,
inactiveIcon:
IconConstants.instance.getMaterialIcon(Icons.home_outlined),
iconSize: 30,
),
), ),
PersistentTabConfig( PersistentBottomNavBarItem(
screen: BlocProvider( 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(),
blocBuilder: () => HomeBloc(),
),
BlocProvider(
child: const DevicesManagerScreen(), child: const DevicesManagerScreen(),
blocBuilder: () => DevicesManagerBloc(), blocBuilder: () => DevicesManagerBloc()),
), BlocProvider(
item: ItemConfig( child: const MapScreen(),
icon: IconConstants.instance.getMaterialIcon(Icons.settings), blocBuilder: () => MapBloc(),
title: appLocalization(context).manager_page_destination,
inactiveIcon:
IconConstants.instance.getMaterialIcon(Icons.settings_outlined),
iconSize: 30,
),
), ),
PersistentTabConfig( BlocProvider(
screen: BlocProvider( child: const DeviceLogsScreen(),
child: const MapScreen(), blocBuilder: () => DeviceLogsBloc(),
blocBuilder: () => MapBloc(),
),
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( BlocProvider(
screen: BlocProvider( child: const InterFamilyScreen(),
child: const DeviceLogsScreen(), blocBuilder: () => InterFamilyBloc(),
blocBuilder: () => DeviceLogsBloc(),
),
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(),
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,
@@ -194,7 +246,7 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
SizedBox( SizedBox(
width: context.lowValue, width: context.lowValue,
), ),
Flexible(child: Text(userSnapshot.data?.name ?? "")) Flexible( child: Text(userSnapshot.data?.name ?? ""))
], ],
); );
}), }),
@@ -327,27 +379,32 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
], ],
), ),
body: PersistentTabView( body: PersistentTabView(
stateManagement: false, context,
controller: controller, controller: controller,
tabs: tabs, screens: _buildScreens(),
navBarBuilder: (navBarConfig) => Style6BottomNavBar( items: _navBarsItems(),
navBarConfig: navBarConfig, handleAndroidBackButtonPress: true,
navBarDecoration: NavBarDecoration( resizeToAvoidBottomInset: true,
color: themeModeSnapshot.data! ? Colors.white : Colors.black, stateManagement: true,
borderRadius: BorderRadius.circular(context.mediumValue),
padding: const EdgeInsets.all(10)),
),
backgroundColor: backgroundColor:
themeModeSnapshot.data! ? Colors.white : Colors.black, themeModeSnapshot.data! ? Colors.white : Colors.black,
navBarOverlap: const NavBarOverlap.none(), decoration: NavBarDecoration(
// margin: EdgeInsets.only( borderRadius: BorderRadius.circular(30.0),
// left: context.lowValue, colorBehindNavBar:
// bottom: context.dynamicHeight(0.02), themeModeSnapshot.data! ? Colors.white : Colors.black,
// right: context.lowValue),
screenTransitionAnimation: const ScreenTransitionAnimation(
curve: Curves.bounceInOut,
duration: Duration(milliseconds: 200),
), ),
animationSettings: const NavBarAnimationSettings(
navBarItemAnimation: ItemAnimationSettings(
duration: Duration(milliseconds: 200),
curve: Curves.bounceInOut,
),
screenTransitionAnimation: ScreenTransitionAnimationSettings(
animateTabTransition: true,
curve: Curves.bounceInOut,
duration: Duration(milliseconds: 200),
),
),
navBarStyle: NavBarStyle.style13,
), ),
); );
}, },
@@ -355,10 +412,8 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
} }
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,20 +1,22 @@
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 '../../bloc/map_bloc.dart'; import 'package:sfm_app/bloc/map_bloc.dart';
import '../../product/base/bloc/base_bloc.dart'; import 'package:sfm_app/feature/map/widget/on_tap_marker_widget.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 'package:sfm_app/product/utils/permission_handler.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});
@@ -36,7 +38,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 = [];
final Completer<GoogleMapController> _controller = Completer(); 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"),
@@ -56,7 +58,6 @@ 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,20 +73,18 @@ class _MapScreenState extends State<MapScreen> with WidgetsBindingObserver {
@override @override
void dispose() { void dispose() {
_isDisposed = true; super.dispose();
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
@@ -118,15 +117,12 @@ class _MapScreenState extends State<MapScreen> with WidgetsBindingObserver {
clusterManager.updateMap(); clusterManager.updateMap();
}, },
polylines: { polylines: {
if (polylinesSnapshot.data != null && Polyline(
polylinesSnapshot.data!.isNotEmpty) ...[ polylineId: const PolylineId('router'),
Polyline( points: polylinesSnapshot.data ?? [],
polylineId: const PolylineId('router'), color: Colors.deepPurpleAccent,
points: polylinesSnapshot.data!, width: 8,
color: Colors.deepPurpleAccent, ),
width: 8,
),
]
}, },
style: mapThemeSnapshot.data, style: mapThemeSnapshot.data,
); );
@@ -174,11 +170,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(
@@ -187,8 +183,7 @@ class _MapScreenState extends State<MapScreen> with WidgetsBindingObserver {
position: cluster.location, position: cluster.location,
onTap: () async { onTap: () async {
LocationPermission permission = await checkAndRequestPermission(); LocationPermission permission = await checkAndRequestPermission();
if (permission == LocationPermission.whileInUse || if (permission == LocationPermission.whileInUse || permission == LocationPermission.always) {
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
@@ -240,9 +235,9 @@ class _MapScreenState extends State<MapScreen> with WidgetsBindingObserver {
if (hasStateOne) { if (hasStateOne) {
return flameIcon; // flameIcon return flameIcon; // flameIcon
} else if (hasOtherState) { } else if (hasOtherState) {
return offlineIcon; // normalIcon return normalIcon; // normalIcon
} else { } else {
return normalIcon; // offlineIcon return offlineIcon; // offlineIcon
} }
} }
@@ -276,12 +271,26 @@ class _MapScreenState extends State<MapScreen> with WidgetsBindingObserver {
} }
void getAllMarkers() async { void getAllMarkers() async {
await apiServices.execute(context, () async { String response = await apiServices.getOwnerDevices();
devices.clear(); if (response != "") {
final devicesList = await apiServices.getOwnerDevices(); final data = jsonDecode(response);
for (var device in devicesList) { List<dynamic> result = data['items'];
devices.add(device); if(result.isNotEmpty){
devices.clear();
final devicesList = Device.fromJsonDynamicList(result);
for (var device in devicesList) {
devices.add(device);
}
}else{
} }
});
}
} }
// Future<bool> checkLocationPermission(context) async {
// bool check = await LocationPermissionRequest.instance
// .checkLocationPermission(context);
// return check;
// }
} }

View File

@@ -3,7 +3,6 @@
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';
@@ -12,6 +11,7 @@ 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!),
); );
await mapBloc.findTheWay( mapBloc.findTheWay(
context, context,
controller, controller,
myLocation, myLocation,
destination, destination,
); );
String deviceLocations = await DeviceUtils.instance String deviceLocations = await DeviceUtils.instance
.getFullDeviceLocation(context, device.areaPath!,device.name); .getFullDeviceLocation(context, device.areaPath!);
String yourLocation = String yourLocation =
appLocalization(context).map_your_location; appLocalization(context).map_your_location;
showDirections( showDirections(
@@ -163,7 +163,6 @@ onTapMarker(
style: const ButtonStyle( style: const ButtonStyle(
backgroundColor: WidgetStatePropertyAll(Colors.red), backgroundColor: WidgetStatePropertyAll(Colors.red),
foregroundColor: WidgetStatePropertyAll(Colors.white), foregroundColor: WidgetStatePropertyAll(Colors.white),
iconColor: WidgetStatePropertyAll(Colors.white),
), ),
icon: IconConstants.instance icon: IconConstants.instance
.getMaterialIcon(Icons.fire_truck_outlined), .getMaterialIcon(Icons.fire_truck_outlined),

View File

@@ -1,11 +1,12 @@
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(
@@ -15,14 +16,16 @@ showDirections(
MapBloc mapBloc, MapBloc mapBloc,
String originalName, String originalName,
String destinationLocation, String destinationLocation,
double deviceLat, double devicelat,
double deviceLng, double devicelng,
) { ) {
TextEditingController originController = TextEditingController(text: originalName); TextEditingController originController =
TextEditingController destinationController = TextEditingController(text: destinationLocation); TextEditingController(text: originalName);
TextEditingController destinationController =
TextEditingController(text: destinationLocation);
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
backgroundColor: Theme.of(context).colorScheme.surface, backgroundColor: Colors.transparent,
// dismissDirection: DismissDirection.none, // dismissDirection: DismissDirection.none,
duration: const Duration(minutes: 5), duration: const Duration(minutes: 5),
content: Column( content: Column(
@@ -35,20 +38,21 @@ showDirections(
children: [ children: [
Text( Text(
appLocalization(context).map_show_direction, appLocalization(context).map_show_direction,
style: context.responsiveBodyLargeWithBold, style: context.titleLargeTextStyle,
), ),
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();
} }
@@ -99,12 +103,17 @@ 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.getMaterialIcon(Icons.near_me_rounded), icon: IconConstants.instance
.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';
@@ -52,6 +52,7 @@ showNearPlacesSideSheet(
padding: context.paddingLow, padding: context.paddingLow,
width: screenWidth, width: screenWidth,
height: screenHeight / 3, height: screenHeight / 3,
color: Colors.white,
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@@ -61,7 +62,10 @@ showNearPlacesSideSheet(
Center( Center(
child: Text( child: Text(
'${appLocalization(modalBottomSheetContext).map_result}: ', '${appLocalization(modalBottomSheetContext).map_result}: ',
style: context.h3 style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
), ),
), ),
Container( Container(
@@ -115,7 +119,10 @@ showNearPlacesSideSheet(
children: [ children: [
Text( Text(
place.result!.name!, place.result!.name!,
style: context.responsiveBodyMediumWithBold, style: const TextStyle(
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: context.responsiveBodySmall, style: const TextStyle(fontSize: 14),
), ),
iconStyleData: const IconStyleData( iconStyleData: const IconStyleData(
icon: Icon( icon: Icon(
@@ -111,12 +111,14 @@ class _DeviceNotificationSettingsScreenState
} }
void getNotificationSetting() async { void getNotificationSetting() async {
await apiServices.execute(context, () async { String? response = await apiServices.getAllSettingsNotificationOfDevices();
deviceNotifications = final data = jsonDecode(response);
await apiServices.getAllSettingsNotificationOfDevices(); final result = data['data'];
deviceNotificationSettingsBloc.sinkListNotifications // log("Data ${DeviceNotificationSettings.mapFromJson(jsonDecode(data)).values.toList()}");
.add(deviceNotifications); deviceNotifications =
}); DeviceNotificationSettings.mapFromJson(result).values.toList();
deviceNotificationSettingsBloc.sinkListNotifications
.add(deviceNotifications);
} }
Widget listNotificationSetting( Widget listNotificationSetting(
@@ -290,25 +292,22 @@ 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) Colors.green,
.notification_update_device_settings_success, Colors.white);
Colors.green, } else {
Colors.white); showNoIconTopSnackBar(
} else { context,
showNoIconTopSnackBar( appLocalization(context).notification_update_device_settings_failed,
context, Colors.red,
appLocalization(context).notification_update_device_settings_failed, Colors.white);
Colors.red, }
Colors.white); isDataChange = false;
} deviceNotificationSettingsBloc.sinkIsDataChange.add(isDataChange);
isDataChange = false;
deviceNotificationSettingsBloc.sinkIsDataChange.add(isDataChange);
});
} }
} }

View File

@@ -1,7 +1,6 @@
// 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';
@@ -9,6 +8,7 @@ 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,73 +38,35 @@ 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 ?? ""; Map<String, dynamic> body = {
Map<String, dynamic> body = { "name": username,
"name": username, "email": email,
"email": email, "phone": tel,
"phone": tel, "address": address,
"address": address, "latitude": latitude,
"latitude": latitude, "longitude": longitude
"longitude": longitude };
}; int statusCode =
int statusCode = await apiServices.updateUserProfile(body);
await apiServices.updateUserProfile(body); if (statusCode == 200) {
if (statusCode == 200) { showNoIconTopSnackBar(
showNoIconTopSnackBar( modalBottomSheetContext,
modalBottomSheetContext, appLocalization(context)
appLocalization(context) .notification_update_profile_success,
.notification_update_profile_success, Colors.green,
Colors.green, Colors.white);
Colors.white); } else {
} else { showNoIconTopSnackBar(
showNoIconTopSnackBar( modalBottomSheetContext,
modalBottomSheetContext, appLocalization(context)
appLocalization(context) .notification_update_profile_failed,
.notification_update_profile_failed, Colors.redAccent,
Colors.redAccent, Colors.white);
Colors.white); }
} Navigator.pop(modalBottomSheetContext);
settingsBloc.getUserProfile(context);
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:
@@ -243,38 +205,35 @@ 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 ?? ""; Map<String, dynamic> body = {
Map<String, dynamic> body = { "name": username,
"name": username, "email": email,
"email": email, "phone": tel,
"phone": tel, "address": address,
"address": address, "latitude": latitude,
"latitude": latitude, "longitude": longitude
"longitude": longitude };
}; int statusCode = await apiServices
int statusCode = await apiServices .updateUserProfile(body);
.updateUserProfile(body); if (statusCode == 200) {
if (statusCode == 200) { showNoIconTopSnackBar(
showNoIconTopSnackBar( modalBottomSheetContext,
modalBottomSheetContext, appLocalization(context)
appLocalization(context) .notification_update_profile_success,
.notification_update_profile_success, Colors.green,
Colors.green, Colors.white);
Colors.white); } else {
} else { showNoIconTopSnackBar(
showNoIconTopSnackBar( modalBottomSheetContext,
modalBottomSheetContext, appLocalization(context)
appLocalization(context) .notification_update_profile_failed,
.notification_update_profile_failed, Colors.redAccent,
Colors.redAccent, Colors.white);
Colors.white); }
} Navigator.pop(modalBottomSheetContext);
settingsBloc.getUserProfile(context);
Navigator.pop(modalBottomSheetContext);
});
} }
}, },
style: const ButtonStyle( style: const ButtonStyle(
@@ -324,31 +283,29 @@ 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, "password_new": newPass,
"password_new": newPass, };
}; int statusCode =
int statusCode = await apiServices.updateUserPassword(body);
await apiServices.updateUserPassword(body); if (statusCode == 200) {
if (statusCode == 200) { showNoIconTopSnackBar(
showNoIconTopSnackBar( modalBottomSheetContext,
modalBottomSheetContext, appLocalization(context)
appLocalization(context) .notification_update_password_success,
.notification_update_password_success, Colors.green,
Colors.green, Colors.white);
Colors.white); } else {
} else { showNoIconTopSnackBar(
showNoIconTopSnackBar( modalBottomSheetContext,
modalBottomSheetContext, appLocalization(context)
appLocalization(context) .notification_update_password_failed,
.notification_update_password_failed, Colors.redAccent,
Colors.redAccent, Colors.white);
Colors.white); }
} Navigator.pop(modalBottomSheetContext);
Navigator.pop(modalBottomSheetContext);
});
} }
}, },
icon: icon:
@@ -433,64 +390,31 @@ 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 = { "password_old": oldPass,
"password_old": oldPass, "password_new": newPass,
"password_new": newPass, };
}; int statusCode = await apiServices
int statusCode = await apiServices .updateUserPassword(body);
.updateUserPassword(body); if (statusCode == 200) {
if (statusCode == 200) { showNoIconTopSnackBar(
showNoIconTopSnackBar( modalBottomSheetContext,
modalBottomSheetContext, appLocalization(context)
appLocalization(context) .notification_update_password_success,
.notification_update_password_success, Colors.green,
Colors.green, Colors.white);
Colors.white); } else {
} else { showNoIconTopSnackBar(
showNoIconTopSnackBar( modalBottomSheetContext,
modalBottomSheetContext, appLocalization(context)
appLocalization(context) .notification_update_password_failed,
.notification_update_password_failed, Colors.redAccent,
Colors.redAccent, Colors.white);
Colors.white);
}
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,6 +27,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
void initState() { void initState() {
super.initState(); super.initState();
settingsBloc = BlocProvider.of(context); settingsBloc = BlocProvider.of(context);
getUserProfile();
} }
@override @override
@@ -37,79 +38,80 @@ class _SettingsScreenState extends State<SettingsScreen> {
centerTitle: true, centerTitle: true,
), ),
body: StreamBuilder<User>( body: StreamBuilder<User>(
stream: settingsBloc.streamUserProfile, stream: settingsBloc.streamUserProfile,
builder: (context, userSnapshot) { initialData: user,
if (userSnapshot.data == null) { builder: (context, userSnapshot) {
settingsBloc.getUserProfile(context); return userSnapshot.data?.id == "" || user.id == ""
return const SharedLoadingAnimation(); ? Center(
} else { child: CircularProgressIndicator(
return Column( value: context.highValue,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
CircleAvatar(
backgroundColor: Theme.of(context).focusColor,
radius: 70,
child: CircleAvatar(
backgroundColor: Theme.of(context).highlightColor,
radius: 60,
child: CircleAvatar(
radius: 50,
child: Text(
getAvatarContent(userSnapshot.data?.username ?? ""),
style: context.dynamicResponsiveSize(36),
),
), ),
), )
), : Column(
SizedBox(height: context.lowValue), mainAxisAlignment: MainAxisAlignment.center,
Row( crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center, children: [
children: [ CircleAvatar(
Text( backgroundColor: Theme.of(context).focusColor,
userSnapshot.data?.name ?? "User Name", radius: 70,
style: context.h2, child: CircleAvatar(
) backgroundColor: Theme.of(context).highlightColor,
], radius: 60,
), child: CircleAvatar(
Row( radius: 50,
mainAxisAlignment: MainAxisAlignment.center, child: Text(
children: [Text(userSnapshot.data?.email ?? "Email")], getAvatarContent(
), userSnapshot.data?.username ?? ""),
SizedBox(height: context.mediumValue), style: const TextStyle(
cardContent( fontSize: 35,
Icons.account_circle_rounded, fontWeight: FontWeight.bold,
appLocalization(context).profile_change_info, ),
userSnapshot.data ?? user), ),
SizedBox(height: context.lowValue), ),
cardContent( ),
Icons.lock_outline, ),
appLocalization(context).profile_change_pass, SizedBox(height: context.lowValue),
userSnapshot.data ?? user), Row(
SizedBox(height: context.lowValue), mainAxisAlignment: MainAxisAlignment.center,
cardContent( children: [
Icons.settings_outlined, Text(
appLocalization(context).profile_setting, userSnapshot.data?.name ?? "User Name",
userSnapshot.data ?? user), style: const TextStyle(
SizedBox(height: context.lowValue), fontWeight: FontWeight.w900, fontSize: 26),
cardContent( )
Icons.sim_card, ],
appLocalization(context).profile_sim_data, ),
userSnapshot.data ?? user), Row(
SizedBox(height: context.lowValue), mainAxisAlignment: MainAxisAlignment.center,
cardContent( children: [Text(userSnapshot.data?.email ?? "Email")],
Icons.logout_outlined, ),
appLocalization(context).log_out, SizedBox(height: context.mediumValue),
userSnapshot.data ?? user), cardContent(
], Icons.account_circle_rounded,
); appLocalization(context).profile_change_info,
} ),
}, SizedBox(height: context.lowValue),
), cardContent(
Icons.lock_outline,
appLocalization(context).profile_change_pass,
),
SizedBox(height: context.lowValue),
cardContent(
Icons.settings_outlined,
appLocalization(context).profile_setting,
),
SizedBox(height: context.lowValue),
cardContent(
Icons.logout_outlined,
appLocalization(context).log_out,
),
],
);
}),
); );
} }
cardContent(IconData icon, String content, User user) { cardContent(IconData icon, String content) {
return GestureDetector( return GestureDetector(
onTap: () async { onTap: () async {
if (icon == Icons.account_circle_rounded) { if (icon == Icons.account_circle_rounded) {
@@ -118,10 +120,7 @@ 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 if(icon == Icons.sim_card){ } else {
context.push(ApplicationConstants.SIM_DATA_SETTINGS);
}
else {
await apiServices.logOut(context); await apiServices.logOut(context);
} }
}, },
@@ -133,7 +132,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
leading: IconConstants.instance.getMaterialIcon(icon), leading: IconConstants.instance.getMaterialIcon(icon),
title: Text( title: Text(
content, content,
style: context.responsiveBodyMediumWithBold, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
), ),
trailing: const Icon( trailing: const Icon(
Icons.arrow_forward_ios_outlined, Icons.arrow_forward_ios_outlined,
@@ -143,6 +142,12 @@ 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

@@ -1,170 +0,0 @@
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

@@ -1,55 +0,0 @@
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,12 +1,12 @@
import 'package:alarm/alarm.dart'; import 'dart:developer';
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/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart' import 'package:flutter_gen/gen_l10n/app_localizations.dart';
show PersistentTabController; import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:sfm_app/firebase_options.dart';
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';
@@ -15,17 +15,90 @@ 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); @pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await setupFlutterNotifications();
showFlutterNotification(message);
// If you're going to use other Firebase services in the background, such as Firestore,
// make sure you call `initializeApp` before using other Firebase services.
print('Handling a background message ${message.messageId}');
}
/// Create a [AndroidNotificationChannel] for heads up notifications
late AndroidNotificationChannel channel;
bool isFlutterLocalNotificationsInitialized = false;
Future<void> setupFlutterNotifications() async {
if (isFlutterLocalNotificationsInitialized) {
return;
}
channel = const AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
description:
'This channel is used for important notifications.', // description
importance: Importance.high,
);
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
/// Create an Android Notification Channel.
///
/// We use this channel in the `AndroidManifest.xml` file to override the
/// default FCM channel to enable heads up notifications.
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
/// Update the iOS foreground notification presentation options to allow
/// heads up notifications.
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
isFlutterLocalNotificationsInitialized = true;
}
void showFlutterNotification(RemoteMessage message) {
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification?.android;
if (notification != null && android != null && !kIsWeb) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channelDescription: channel.description,
// TODO add a proper drawable resource to android, for now using
// one that already exists in example app.
icon: 'launch_background',
),
),
);
}
}
/// Initialize the [FlutterLocalNotificationsPlugin] package.
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp( await Firebase.initializeApp();
options: DefaultFirebaseOptions.currentPlatform, // Set the background messaging handler early on, as a named top-level function
name: "sfm-notification"); FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
FirebaseMessaging.onBackgroundMessage(
NotificationServices.firebaseMessagingBackgroundHandler); if (!kIsWeb) {
await Alarm.init(); await setupFlutterNotifications();
await Alarm.stopAll(); }
runApp( runApp(
BlocProvider( BlocProvider(
child: const MyApp(), child: const MyApp(),
@@ -34,6 +107,7 @@ void main() async {
); );
} }
class MyApp extends StatefulWidget { class MyApp extends StatefulWidget {
const MyApp({super.key}); const MyApp({super.key});
@@ -56,7 +130,7 @@ class _MyAppState extends State<MyApp> {
late MainBloc mainBloc; late MainBloc mainBloc;
LanguageServices languageServices = LanguageServices(); LanguageServices languageServices = LanguageServices();
ThemeServices themeServices = ThemeServices(); ThemeServices themeServices = ThemeServices();
final NotificationServices notificationServices = NotificationServices(); // final NotificationServices notificationServices = NotificationServices();
APIServices apiServices = APIServices(); APIServices apiServices = APIServices();
setLocale(Locale locale) { setLocale(Locale locale) {
_locale = locale; _locale = locale;
@@ -72,11 +146,20 @@ class _MyAppState extends State<MyApp> {
void initState() { void initState() {
super.initState(); super.initState();
mainBloc = BlocProvider.of(context); mainBloc = BlocProvider.of(context);
notificationServices.initialize(); // // notificationServices.initLocalNotifications();
notificationServices.firebaseInit(context); // // notificationServices.firebaseInit(context);
notificationServices.setupInteractMessage(controller); // // notificationServices.setupInteractMessage();
// 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
void didChangeDependencies() { void didChangeDependencies() {
languageServices.getLocale().then((locale) => {setLocale(locale)}); languageServices.getLocale().then((locale) => {setLocale(locale)});
@@ -92,18 +175,19 @@ class _MyAppState extends State<MyApp> {
initialData: _locale, initialData: _locale,
builder: (context, languageSnapshot) { builder: (context, languageSnapshot) {
return StreamBuilder<ThemeData?>( return StreamBuilder<ThemeData?>(
stream: mainBloc.streamTheme, stream: mainBloc.streamTheme,
initialData: _themeData, initialData: _themeData,
builder: (context, themeSnapshot) { builder: (context, themeSnapshot) {
return MaterialApp.router( return MaterialApp.router(
theme: themeSnapshot.data, theme: themeSnapshot.data,
routerConfig: router, routerConfig: router,
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
localizationsDelegates: AppLocalizations.localizationsDelegates, localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales, supportedLocales: AppLocalizations.supportedLocales,
locale: languageSnapshot.data, locale: languageSnapshot.data,
); );
}); }
);
}, },
); );
} }

View File

@@ -30,7 +30,8 @@ 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: context.responsiveBodyLargeWithBold, style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 18),
), ),
), ),
Divider(height: dialogContext.lowValue), Divider(height: dialogContext.lowValue),

View File

@@ -1,4 +1,4 @@
// ignore_for_file: constant_identifier_names, non_constant_identifier_names // ignore_for_file: constant_identifier_names
class ApplicationConstants { class ApplicationConstants {
static const APP_NAME = "Smatec SFM"; static const APP_NAME = "Smatec SFM";
@@ -21,10 +21,8 @@ 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,5 +13,4 @@ enum AppRoutes {
HISTORY, HISTORY,
GROUPS, GROUPS,
GROUP_DETAIL, GROUP_DETAIL,
SIM_DATA_SETTING,
} }

View File

@@ -1,7 +1,4 @@
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';
@@ -154,16 +151,6 @@ 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),
),
], ],
); );
} }

View File

@@ -1,8 +1,7 @@
import 'dart:math'; import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils/app_logger_utils.dart';
import '../utils/responsive_text_utils.dart';
import '../theme/app_theme_light.dart'; import '../theme/app_theme_light.dart';
// MEDIA // MEDIA
@@ -10,105 +9,55 @@ extension ContextExtension on BuildContext {
MediaQueryData get mediaQuery => MediaQuery.of(this); MediaQueryData get mediaQuery => MediaQuery.of(this);
} }
extension ResponsiveTextStyle on BuildContext {
TextStyle get h1 => TextStyle(
fontSize: ResponsiveText.getSize(this, 32), fontWeight: FontWeight.bold);
TextStyle get h2 => TextStyle(
fontSize: ResponsiveText.getSize(this, 24), fontWeight: FontWeight.bold);
TextStyle get h3 => TextStyle(
fontSize: ResponsiveText.getSize(this, 20), fontWeight: FontWeight.bold);
TextStyle get responsiveBodyLarge =>
TextStyle(fontSize: ResponsiveText.getSize(this, 18));
TextStyle get responsiveBodyLargeWithBold => TextStyle(
fontSize: ResponsiveText.getSize(this, 18), fontWeight: FontWeight.bold);
TextStyle get responsiveBodyMedium =>
TextStyle(fontSize: ResponsiveText.getSize(this, 16));
TextStyle get responsiveBodyMediumWithBold => TextStyle(
fontSize: ResponsiveText.getSize(this, 16), fontWeight: FontWeight.bold);
TextStyle get responsiveBodySmall =>
TextStyle(fontSize: ResponsiveText.getSize(this, 14));
TextStyle get responsiveBodySmallWithBold => TextStyle(
fontSize: ResponsiveText.getSize(this, 14), fontWeight: FontWeight.bold);
TextStyle dynamicResponsiveSize(double val) =>
TextStyle(fontSize: ResponsiveText.getSize(this, val));
TextStyle dynamicResponsiveSizeWithBold(double val) => TextStyle(
fontSize: ResponsiveText.getSize(this, val), fontWeight: FontWeight.bold);
}
// VALUES // VALUES
extension MediaQueryExtension on BuildContext { extension MediaQueryExtension on BuildContext {
double get height => mediaQuery.size.height; double get height => mediaQuery.size.height;
double get width => mediaQuery.size.width; double get width => mediaQuery.size.width;
double get lowValue => height * 0.01; double get lowValue => height * 0.01;
double get normalValue => height * 0.02; double get normalValue => height * 0.02;
double get mediumValue => height * 0.04; double get mediumValue => height * 0.04;
double get highValue => height * 0.1; double get highValue => height * 0.1;
double dynamicWidth(double val) => width * val; double dynamicWidth(double val) => width * val;
double dynamicHeight(double val) => height * val; double dynamicHeight(double val) => height * val;
} }
// THEME // THEME
extension ThemeExtension on BuildContext { extension ThemeExtension on BuildContext {
ThemeData get theme => Theme.of(this); ThemeData get theme => Theme.of(this);
TextTheme get textTheme => theme.textTheme; TextTheme get textTheme => theme.textTheme;
ColorScheme get colors => AppThemeLight.instance.theme.colorScheme; ColorScheme get colors => AppThemeLight.instance.theme.colorScheme;
} }
// PADDING ALLL // PADDING ALLL
extension PaddingExtensionAll on BuildContext { extension PaddingExtensionAll on BuildContext {
EdgeInsets get paddingLow => EdgeInsets.all(lowValue); EdgeInsets get paddingLow => EdgeInsets.all(lowValue);
EdgeInsets get paddingNormal => EdgeInsets.all(normalValue); EdgeInsets get paddingNormal => EdgeInsets.all(normalValue);
EdgeInsets get paddingMedium => EdgeInsets.all(mediumValue); EdgeInsets get paddingMedium => EdgeInsets.all(mediumValue);
EdgeInsets get paddingHigh => EdgeInsets.all(highValue); EdgeInsets get paddingHigh => EdgeInsets.all(highValue);
EdgeInsets dynamicPadding(double val) => EdgeInsets.all(val); EdgeInsets dynamicPadding(double val) => EdgeInsets.all(val);
// double dynamicPadding(double val) => height * val; // double dynamicPadding(double val) => height * val;
} }
// PADDING SYMETRIC // PADDING SYMETRIC
extension PaddingExtensionSymetric on BuildContext { extension PaddingExtensionSymetric on BuildContext {
// VERTICAL PADDİNG // VERTICAL PADDİNG
EdgeInsets get paddingLowVertical => EdgeInsets.symmetric(vertical: lowValue); EdgeInsets get paddingLowVertical => EdgeInsets.symmetric(vertical: lowValue);
EdgeInsets get paddingNormalVertical => EdgeInsets get paddingNormalVertical =>
EdgeInsets.symmetric(vertical: normalValue); EdgeInsets.symmetric(vertical: normalValue);
EdgeInsets get paddingMediumVertical => EdgeInsets get paddingMediumVertical =>
EdgeInsets.symmetric(vertical: mediumValue); EdgeInsets.symmetric(vertical: mediumValue);
EdgeInsets get paddingHighVertical => EdgeInsets get paddingHighVertical =>
EdgeInsets.symmetric(vertical: highValue); EdgeInsets.symmetric(vertical: highValue);
// HORIZONTAL PADDİNG // HORIZONTAL PADDİNG
EdgeInsets get paddingLowHorizontal => EdgeInsets get paddingLowHorizontal =>
EdgeInsets.symmetric(horizontal: lowValue); EdgeInsets.symmetric(horizontal: lowValue);
EdgeInsets get paddingNormalHorizontal => EdgeInsets get paddingNormalHorizontal =>
EdgeInsets.symmetric(horizontal: normalValue); EdgeInsets.symmetric(horizontal: normalValue);
EdgeInsets get paddingMediumHorizontal => EdgeInsets get paddingMediumHorizontal =>
EdgeInsets.symmetric(horizontal: mediumValue); EdgeInsets.symmetric(horizontal: mediumValue);
EdgeInsets get paddingHighHorizontal => EdgeInsets get paddingHighHorizontal =>
EdgeInsets.symmetric(horizontal: highValue); EdgeInsets.symmetric(horizontal: highValue);
} }
@@ -121,63 +70,34 @@ extension PageExtension on BuildContext {
// DURATION // DURATION
extension DurationExtension on BuildContext { extension DurationExtension on BuildContext {
Duration get lowDuration => const Duration(milliseconds: 150); Duration get lowDuration => const Duration(milliseconds: 150);
Duration get normalDuration => const Duration(milliseconds: 500); Duration get normalDuration => const Duration(milliseconds: 500);
Duration dynamicMilliSecondDuration(int milliseconds) => Duration dynamicMilliSecondDuration(int milliseconds) =>
Duration(milliseconds: milliseconds); Duration(milliseconds: milliseconds);
Duration dynamicMinutesDuration(int minutes) => Duration(minutes: minutes); Duration dynamicMinutesDuration(int minutes) => Duration(minutes: minutes);
} }
// RADIUS // RADIUS
extension RadiusExtension on BuildContext { extension RadiusExtension on BuildContext {
Radius get lowRadius => Radius.circular(width * 0.02); Radius get lowRadius => Radius.circular(width * 0.02);
Radius get normalRadius => Radius.circular(width * 0.05); Radius get normalRadius => Radius.circular(width * 0.05);
Radius get highRadius => Radius.circular(width * 0.1); Radius get highRadius => Radius.circular(width * 0.1);
Radius dynamicRadius(double radius) => Radius.circular(radius); Radius dynamicRadius(double radius) => Radius.circular(radius);
} }
extension TextStyleExtention on BuildContext { extension TextStyleExtention on BuildContext {
TextStyle get labelSmallTextStyle => Theme.of(this).textTheme.labelSmall!; TextStyle get labelSmallTextStyle => Theme.of(this).textTheme.labelSmall!;
TextStyle get labelMediumTextStyle => Theme.of(this).textTheme.labelMedium!; TextStyle get labelMediumTextStyle => Theme.of(this).textTheme.labelMedium!;
TextStyle get labelLargeTextStyle => Theme.of(this).textTheme.labelLarge!; TextStyle get labelLargeTextStyle => Theme.of(this).textTheme.labelLarge!;
TextStyle get bodySmallTextStyle => Theme.of(this).textTheme.bodySmall!; TextStyle get bodySmallTextStyle => Theme.of(this).textTheme.bodySmall!;
TextStyle get bodyMediumTextStyle => Theme.of(this).textTheme.bodyMedium!; TextStyle get bodyMediumTextStyle => Theme.of(this).textTheme.bodyMedium!;
TextStyle get bodyLargeTextStyle => Theme.of(this).textTheme.bodyLarge!; TextStyle get bodyLargeTextStyle => Theme.of(this).textTheme.bodyLarge!;
TextStyle get titleSmallTextStyle => Theme.of(this).textTheme.titleSmall!; TextStyle get titleSmallTextStyle => Theme.of(this).textTheme.titleSmall!;
TextStyle get titleMediumTextStyle => Theme.of(this).textTheme.titleMedium!; TextStyle get titleMediumTextStyle => Theme.of(this).textTheme.titleMedium!;
TextStyle get titleLargeTextStyle => Theme.of(this).textTheme.titleLarge!; TextStyle get titleLargeTextStyle => Theme.of(this).textTheme.titleLarge!;
TextStyle get headlineSmallTextStyle => TextStyle get headlineSmallTextStyle =>
Theme.of(this).textTheme.headlineSmall!; Theme.of(this).textTheme.headlineSmall!;
TextStyle get headlineMediumTextStyle => TextStyle get headlineMediumTextStyle =>
Theme.of(this).textTheme.headlineMedium!; Theme.of(this).textTheme.headlineMedium!;
TextStyle get headlineLargeTextStyle => TextStyle get headlineLargeTextStyle =>
Theme.of(this).textTheme.headlineLarge!; Theme.of(this).textTheme.headlineLarge!;
} }
extension FutureExtension<T> on Future<T> {
Future<T> handleApiError() async {
try {
return await this;
} catch (e) {
AppLoggerUtils.error(e.toString());
return Future.error(e);
}
}
}

View File

@@ -22,8 +22,6 @@
"let_PCCC_handle_message": "Let the Fire Prevention and Fighting Team handle it!", "let_PCCC_handle_message": "Let the Fire Prevention and Fighting Team handle it!",
"overview_message": "Overview", "overview_message": "Overview",
"total_nof_devices_message": "Total number of devices", "total_nof_devices_message": "Total number of devices",
"over_view_owner_devices":"Owner Devices",
"over_view_joined_devices":"Joined Devices",
"active_devices_message": "Active", "active_devices_message": "Active",
"inactive_devices_message": "Inactive", "inactive_devices_message": "Inactive",
"warning_devices_message": "Warning", "warning_devices_message": "Warning",
@@ -111,10 +109,7 @@
"profile_page_title": "Settings Page", "profile_page_title": "Settings Page",
"profile_change_info": "Change information", "profile_change_info": "Change information",
"profile_change_pass": "Change password", "profile_change_pass": "Change password",
"profile_sim_data": "Device SIM information",
"profile_setting": "Notification Setting", "profile_setting": "Notification Setting",
"sim_data_month_left_message": "months left",
"time_title": "Time",
"change_profile_title": "Personal information", "change_profile_title": "Personal information",
"change_profile_username": "Username: ", "change_profile_username": "Username: ",
"change_profile_username_hint": "Enter username ", "change_profile_username_hint": "Enter username ",

File diff suppressed because it is too large Load Diff

View File

@@ -1,792 +0,0 @@
// ignore: unused_import
import 'package:intl/intl.dart' as intl;
import 'app_localizations.dart';
// ignore_for_file: type=lint
/// The translations for English (`en`).
class AppLocalizationsEn extends AppLocalizations {
AppLocalizationsEn([String locale = 'en']) : super(locale);
@override
String get description_NOTUSE => 'This is english language in HomePage';
@override
String get home_page_name => 'Home Page';
@override
String get vietnam_language => 'Vietnamese';
@override
String get english_language => 'English';
@override
String get notification => 'Notifications:';
@override
String get profile_icon_title => 'Settings';
@override
String get log_out => 'Log out';
@override
String get log_out_content => 'Are you sure you want to log out?';
@override
String get notification_description => 'All devices are operating normally';
@override
String get button_fake_fire_message => 'False fire alarm';
@override
String get in_progress_message => 'In progress';
@override
String get smoke_detecting_message => 'Smoke detecting!';
@override
String get low_battery_message => 'Low Battery!';
@override
String get smoke_detecting_message_lowercase => 'smoke detecting!';
@override
String get disconnect_message_uppercase => 'Disconnected';
@override
String get disconnect_message_lowercase => 'disconnected';
@override
String get location_message => 'Address: ';
@override
String get confirm_fake_fire_message =>
'Are you sure the fire is a false alarm?';
@override
String get confirm_fake_fire_body =>
'Please check carefully to ensure that this is just a normal incident. The fire department will confirm that this is a false alarm!';
@override
String get confirm_fake_fire_sure_message => 'I\'\'m sure';
@override
String get let_PCCC_handle_message =>
'Let the Fire Prevention and Fighting Team handle it!';
@override
String get overview_message => 'Overview';
@override
String get total_nof_devices_message => 'Total number of devices';
@override
String get over_view_owner_devices => 'Owner Devices';
@override
String get over_view_joined_devices => 'Joined Devices';
@override
String get active_devices_message => 'Active';
@override
String get inactive_devices_message => 'Inactive';
@override
String get warning_devices_message => 'Warning';
@override
String get unused_devices_message => 'Unused';
@override
String get description_NOTUSE1 =>
'This is english language in DeviceManagerPage';
@override
String get device_manager_page_name => 'Devices Manager';
@override
String get add_device_title => 'Add new device';
@override
String get input_extID_device_input => 'Devcice ID';
@override
String get input_extID_device_hintText => 'Enter the device ID';
@override
String get input_name_device_device => 'Device Name';
@override
String get input_name_device_hintText => 'Enter the device Name';
@override
String get paginated_data_table_title => 'List of devices';
@override
String get paginated_data_table_column_action => 'Action';
@override
String get paginated_data_table_column_deviceName => 'Device name';
@override
String get paginated_data_table_column_deviceStatus => 'Status';
@override
String get paginated_data_table_column_deviceBaterry => 'Battery';
@override
String get paginated_data_table_column_deviceSignal => 'Signal';
@override
String get paginated_data_table_column_deviceTemperature => 'Temperature';
@override
String get paginated_data_table_column_deviceHump => 'Humidity';
@override
String get paginated_data_table_column_devicePower => 'Power';
@override
String get delete_device_dialog_title => 'Remove device';
@override
String get delete_device_dialog_content =>
'Are you sure you want to delete this device?';
@override
String get update_device_dialog_title => 'Update device';
@override
String get update_device_dialog_location_title => 'Device location';
@override
String get update_device_dialog_location_longitude => 'Longitude';
@override
String get update_device_dialog_location_latitude => 'Latitude';
@override
String get update_device_dialog_location_longitude_hintText =>
'Enter longitude';
@override
String get update_device_dialog_location_latitude_hintText =>
'Enter latitude';
@override
String get update_device_dialog_location_province_hintText =>
'Select Province/City';
@override
String get update_device_dialog_location_province_searchHint =>
'Find Province/City';
@override
String get update_device_dialog_location_district_hintText =>
'Select District';
@override
String get update_device_dialog_location_district_searchHint =>
'Find district';
@override
String get update_device_dialog_location_ward_hintText =>
'Select Ward/Commune';
@override
String get update_device_dialog_location_ward_searchHint =>
'Find Ward/Commune';
@override
String get update_device_dialog_maps_dialog_title => 'Update location';
@override
String get update_device_dialog_search_location_hint => 'Search Location';
@override
String get description_NOTUSE8 =>
'This is english language in MapPositionPage';
@override
String get map_your_location => 'Your Location';
@override
String get map_show_direction => 'Give directions';
@override
String get map_nearby_hospital => 'Nearby hospital';
@override
String get map_nearest_hospital => 'Nearest hospital';
@override
String get map_nearby_firestation => 'Nearby fire station';
@override
String get map_nearest_firestation => 'Nearest fire station';
@override
String get map_result => 'Result';
@override
String get map_always_opened => 'Always open';
@override
String get map_openning => 'Openning';
@override
String get map_closed => 'Closed';
@override
String get map_no_results => 'No results found';
@override
String get map_start => 'Start';
@override
String get map_destination => 'Destination';
@override
String get map_stream => 'Stream';
@override
String get description_NOTUSE2 => 'This is english language in DeviceLogPage';
@override
String get device_log_page_name => 'Devices Log';
@override
String get choose_device_dropdownButton => 'Select device';
@override
String get choose_date_start_datePicker => 'Start from';
@override
String get choose_date_end_datePicker => 'End';
@override
String get main_no_data => 'No data yet.';
@override
String get event_tag_title => 'Event';
@override
String get description_NOTUSE3 => 'This is english language in InterFamily';
@override
String get interfamily_page_name => 'InterFamily';
@override
String get my_group_title => 'My group';
@override
String get invite_group => 'Joined group';
@override
String get add_new_group => 'Add new group';
@override
String get join_group => 'Join group';
@override
String get group_name_title => 'Group Name';
@override
String get group_id_title => 'Group ID';
@override
String get add_new_user_title => 'Add user';
@override
String get share_group_title => 'Share group';
@override
String get change_group_infomation_title => 'Change Infomation';
@override
String get change_group_infomation_content => 'Change group infomation';
@override
String get delete_group_title => 'Delete group';
@override
String get delete_group_content =>
'Are you sure you want to delete this group?';
@override
String get leave_group_content =>
'Are you sure you want to leave this group?';
@override
String get dont_have_group => 'No group yet';
@override
String get dont_join_group => 'You haven\'\'t joined any groups yet.';
@override
String get description_group => 'Description';
@override
String get add_new_device_title => 'Add new device';
@override
String get approve_user => 'Approve members';
@override
String get devices_title => 'Devices';
@override
String get device_title => 'Device';
@override
String get member_title => 'Members';
@override
String get leave_group_title => 'Leave group';
@override
String get dont_have_device => 'No device yet';
@override
String get description_NOTUSE4 => 'This is english language in ProfilePage';
@override
String get profile_page_title => 'Settings Page';
@override
String get profile_change_info => 'Change information';
@override
String get profile_change_pass => 'Change password';
@override
String get profile_sim_data => 'Device SIM information';
@override
String get profile_setting => 'Notification Setting';
@override
String get sim_data_month_left_message => 'months left';
@override
String get time_title => 'Time';
@override
String get change_profile_title => 'Personal information';
@override
String get change_profile_username => 'Username: ';
@override
String get change_profile_username_hint => 'Enter username ';
@override
String get change_profile_email => 'Email: ';
@override
String get change_profile_email_hint => 'Enter email ';
@override
String get change_profile_email_not_empty => 'Email cannot be empty';
@override
String get change_profile_tel => 'Phone number: ';
@override
String get change_profile_tel_hint => 'Enter phone number';
@override
String get change_profile_tel_not_empty => 'Phone number cannot be empty';
@override
String get change_profile_address => 'Address: ';
@override
String get change_profile_address_hint => 'Enter address';
@override
String get change_profile_old_pass => 'Password: ';
@override
String get change_profile_old_pass_hint => 'Enter password';
@override
String get change_profile_old_pass_not_empty =>
'Old password cannot be empty';
@override
String get change_profile_new_pass => 'New password: ';
@override
String get change_profile_new_pass_hint => 'Enter new password';
@override
String get change_profile_new_pass_not_empty =>
'New password cannot be empty';
@override
String get change_profile_device_notification_select_all => 'Select all';
@override
String get change_profile_device_notification_deselect_all => 'Deselect all';
@override
String get description_NOTUSE5 => 'This is english language in BellPage';
@override
String get bell_page_title => 'Notifications';
@override
String get bell_page_no_items_body => 'No notifications yet';
@override
String get bell_user_uppercase => 'User';
@override
String get bell_battery_device => 'Device Battery';
@override
String get bell_user_joined_group => 'joined group';
@override
String get bell_leave_group => 'left group';
@override
String get bell_user_added_group => 'added to the group';
@override
String get bell_user_kick_group => 'removed from the group';
@override
String get bell_operate_normal => 'operating normally';
@override
String get bell_invalid_code => 'Invalid event code';
@override
String get bell_days_ago => 'days ago';
@override
String get bell_hours_ago => 'hours ago';
@override
String get bell_minutes_ago => 'minutes ago';
@override
String get bell_just_now => 'just now';
@override
String get bell_read_all => 'You have read all the notifications';
@override
String get description_NOTUSE6 =>
'This is english language in GlobalFunction';
@override
String get gf_newly_create_message => 'Newly created';
@override
String get gf_disconnect_message => 'Disconnected';
@override
String get gf_smoke_detected_message => 'Smoke detected';
@override
String get gf_no_signal_message => 'No Signal';
@override
String get gf_weak_signal_message => 'Weak Signal';
@override
String get gf_moderate_signal_message => 'Moderate signal';
@override
String get gf_good_signal_message => 'Good signal';
@override
String get gf_volt_detect_message => 'Voltage detected';
@override
String get gf_temp_detect_message => 'Temperature detected';
@override
String get gf_hum_detect_message => 'Humidity detected';
@override
String get gf_battery_detect_message => 'Battery detected';
@override
String get gf_offline_message => 'Offline';
@override
String get gf_in_firefighting_message => 'In firefighting';
@override
String get gf_device_error_message => 'Device error';
@override
String get gf_not_move_message => 'Not moved';
@override
String get gf_moving_message => 'Moved';
@override
String get gf_remove_from_base_message => 'Removed from the base';
@override
String get gf_connected_lowercase => 'connected';
@override
String get description_NOTUSE7 => 'This is english language in LoginPage';
@override
String get login_account_not_empty => 'Account cannot be empty';
@override
String get login_account_hint => 'Account';
@override
String get login_password_not_empty => 'Password cannot be empty';
@override
String get login_password_hint => 'Password';
@override
String get login_success_message => 'Login successful';
@override
String get login_incorrect_usernameOrPass => 'Incorrect account or password';
@override
String get login_button_content => 'Login';
@override
String get description_NOTUSE9 =>
'This is english language in DeviceUpdatePage';
@override
String get device_update_title => 'Update Device';
@override
String get device_update_location => 'Device Location';
@override
String get device_update_province => 'Province/City';
@override
String get device_update_district => 'District';
@override
String get device_update_ward => 'Ward/Commune';
@override
String get description_NOTUSE10 =>
'This is english language in DetailDevicePage';
@override
String get detail_device_dont_has_location_message =>
'No location information available yet';
@override
String get detail_device_volt_message => 'Measured voltage (V)';
@override
String get no_data_message => 'No data yet';
@override
String get normal_message => 'Normal';
@override
String get warning_status_message => 'Warning';
@override
String get undefine_message => 'Undefined';
@override
String get low_message_uppercase => 'Low';
@override
String get moderate_message_uppercase => 'Moderate';
@override
String get good_message_uppercase => 'Good';
@override
String get low_message_lowercase => 'low';
@override
String get moderate_message_lowercase => 'moderate';
@override
String get good_message_lowercase => 'good';
@override
String get error_message_uppercase => 'Error';
@override
String get error_message_lowercase => 'error';
@override
String get warning_message => 'Warning: ';
@override
String get loading_message => 'Loading...';
@override
String get detail_message => 'Detail';
@override
String get permission_deny_message => 'Permission Denied';
@override
String get decline_message => 'Decline';
@override
String get allow_message => 'Allow';
@override
String get add_button_content => 'Add';
@override
String get update_button_content => 'Update';
@override
String get change_button_content => 'Change';
@override
String get confirm_button_content => 'Confirm';
@override
String get delete_button_content => 'Delete';
@override
String get cancel_button_content => 'Cancel';
@override
String get find_button_content => 'Find';
@override
String get home_page_destination => 'Home';
@override
String get manager_page_destination => 'Manager';
@override
String get map_page_destination => 'Map';
@override
String get history_page_destination => 'History';
@override
String get history_page_destination_tooltip => 'Device history';
@override
String get group_page_destination => 'Group';
@override
String get group_page_destination_tooltip => 'Exchange device notifications';
@override
String get notification_enter_all_inf =>
'Please enter all the required information';
@override
String get notification_update_device_success => 'Device update successfully';
@override
String get notification_update_device_failed => 'Device update failed';
@override
String get notification_update_device_error => 'Device update Error';
@override
String get notification_cannot_find_address_from_location =>
'Can\'\'t find the location';
@override
String get notification_add_device_success => 'Device added successfully';
@override
String get notification_add_device_failed => 'Failed to add device';
@override
String get notification_create_device_success =>
'Device created successfully';
@override
String get notification_create_device_failed => 'Failed to create device';
@override
String get notification_delete_device_success =>
'Device deleted successfully';
@override
String get notification_delete_device_failed => 'Failed to delete device';
@override
String get notification_device_not_exist => 'The device does not exist';
@override
String get notification_add_group_success => 'Group created successfully';
@override
String get notification_add_group_failed => 'Failed to create group';
@override
String get notification_update_group_success => 'Group updated successfully';
@override
String get notification_update_group_failed => 'Failed to updated group';
@override
String get notification_delete_group_success => 'Group deleted successfully';
@override
String get notification_delete_group_failed => 'Failed to delete group';
@override
String get notification_leave_group_success => 'Leave group successfully';
@override
String get notification_leave_group_failed => 'Failed to leave group';
@override
String get notification_join_request_group_success =>
'Group join request successful!';
@override
String get notification_join_request_group_failed =>
'Group join request failed!';
@override
String get notification_update_profile_success =>
'Update profile successfully';
@override
String get notification_update_profile_failed => 'Failed to update profile';
@override
String get notification_update_password_success =>
'Change password successfully';
@override
String get notification_update_password_failed =>
'The old password does not match';
@override
String get notification_update_device_settings_success =>
'Device notification updated successfully';
@override
String get notification_update_device_settings_failed =>
'Failed to update device notification';
@override
String get notification_confirm_fake_fire_success =>
'Information has been updated to the Fire Station';
@override
String get notification_confirm_fake_fire_failed =>
'Failed to update confirm fake fire';
}

View File

@@ -1,786 +0,0 @@
// ignore: unused_import
import 'package:intl/intl.dart' as intl;
import 'app_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Vietnamese (`vi`).
class AppLocalizationsVi extends AppLocalizations {
AppLocalizationsVi([String locale = 'vi']) : super(locale);
@override
String get description_NOTUSE => 'This is VietNam language in HomePage';
@override
String get home_page_name => 'Trang chủ';
@override
String get vietnam_language => 'Tiếng Việt';
@override
String get english_language => 'Tiếng Anh';
@override
String get notification => 'Thông báo:';
@override
String get profile_icon_title => 'Cài đặt';
@override
String get log_out => 'Đăng xuất';
@override
String get log_out_content => 'Bạn chắc chắn muốn đăng xuất?';
@override
String get notification_description =>
'Tất cả thiết bị hoạt động bình thường';
@override
String get button_fake_fire_message => 'Cháy giả?';
@override
String get in_progress_message => 'Đang xử lý';
@override
String get smoke_detecting_message => 'Phát hiện khói!';
@override
String get low_battery_message => 'Cảnh báo pin yếu!';
@override
String get smoke_detecting_message_lowercase => 'Phát hiện khói!';
@override
String get disconnect_message_uppercase => 'Mất kết nối';
@override
String get disconnect_message_lowercase => 'mất kết nối';
@override
String get location_message => 'Địa chỉ: ';
@override
String get confirm_fake_fire_message => 'Bạn chắc chắn đám cháy là cháy giả?';
@override
String get confirm_fake_fire_body =>
'Bạn hãy kiểm tra thật kỹ để chắc chắn rằng đây chỉ là sự cố bình thường. Đội PCCC sẽ xác nhận đây là đám cháy giả!';
@override
String get confirm_fake_fire_sure_message => 'Tôi chắc chắn';
@override
String get let_PCCC_handle_message => 'Hãy để Đội PCCC xử lý!';
@override
String get overview_message => 'Tổng quan';
@override
String get total_nof_devices_message => 'Tổng số';
@override
String get over_view_owner_devices => 'Thiết bị sở hữu';
@override
String get over_view_joined_devices => 'Thiết bị tham gia';
@override
String get active_devices_message => 'Bình thường';
@override
String get inactive_devices_message => 'Đang tắt';
@override
String get warning_devices_message => 'Cảnh báo';
@override
String get unused_devices_message => 'Không sử dụng';
@override
String get description_NOTUSE1 =>
'This is vietnamese language in DeviceManagerPage';
@override
String get device_manager_page_name => 'Quản lý thiết bị';
@override
String get add_device_title => 'Thêm thiết bị';
@override
String get input_extID_device_input => 'Mã thiết bị';
@override
String get input_extID_device_hintText => 'Nhập mã thiết bị';
@override
String get input_name_device_device => 'Tên thiết bị';
@override
String get input_name_device_hintText => 'Nhập tên thiết bị';
@override
String get paginated_data_table_title => 'Danh sách thiết bị';
@override
String get paginated_data_table_column_action => 'Thao tác';
@override
String get paginated_data_table_column_deviceName => 'Tên thiết bị';
@override
String get paginated_data_table_column_deviceStatus => 'Tình trạng';
@override
String get paginated_data_table_column_deviceBaterry => 'Mức pin';
@override
String get paginated_data_table_column_deviceSignal => 'Mức sóng';
@override
String get paginated_data_table_column_deviceTemperature => 'Nhiệt độ';
@override
String get paginated_data_table_column_deviceHump => 'Độ ẩm';
@override
String get paginated_data_table_column_devicePower => 'Nguồn';
@override
String get delete_device_dialog_title => 'Xóa thiết bị';
@override
String get delete_device_dialog_content =>
'Bạn có chắc chắn muốn xóa thiết bị này?';
@override
String get update_device_dialog_title => 'Sửa thiết bị';
@override
String get update_device_dialog_location_title => 'Ví trí thiết bị';
@override
String get update_device_dialog_location_longitude => 'Kinh độ';
@override
String get update_device_dialog_location_latitude => 'Vĩ độ';
@override
String get update_device_dialog_location_longitude_hintText => 'Nhập kinh độ';
@override
String get update_device_dialog_location_latitude_hintText => 'Nhập vĩ độ';
@override
String get update_device_dialog_location_province_hintText =>
'Chọn Tỉnh/Thành phố';
@override
String get update_device_dialog_location_province_searchHint =>
'Tìm Tỉnh/Thành phố';
@override
String get update_device_dialog_location_district_hintText =>
'Chọn Quận/Huyện';
@override
String get update_device_dialog_location_district_searchHint =>
'Tìm Quận/Huyện';
@override
String get update_device_dialog_location_ward_hintText => 'Chọn Phường/Xã';
@override
String get update_device_dialog_location_ward_searchHint => 'Tìm Phường/Xã';
@override
String get update_device_dialog_maps_dialog_title => 'Cập nhật vị trí';
@override
String get update_device_dialog_search_location_hint => 'Tìm kiếm địa chỉ';
@override
String get description_NOTUSE8 =>
'This is vietnamese language in MapPositionPage';
@override
String get map_your_location => 'Vị trí của bạn';
@override
String get map_show_direction => 'Chỉ đường';
@override
String get map_nearby_hospital => 'Bệnh viện gần đó';
@override
String get map_nearest_hospital => 'Bệnh viện gần nhất';
@override
String get map_nearby_firestation => 'Trạm cứu hỏa gần đó';
@override
String get map_nearest_firestation => 'Trạm cứu hỏa gần nhất';
@override
String get map_result => 'Kết quả';
@override
String get map_always_opened => 'Luôn mở cửa';
@override
String get map_openning => 'Đang mở cửa';
@override
String get map_closed => 'Đóng cửa';
@override
String get map_no_results => 'Không tìm thấy kết quả';
@override
String get map_start => 'Xuất phát';
@override
String get map_destination => 'Đích đến';
@override
String get map_stream => 'Trực tiếp';
@override
String get description_NOTUSE2 =>
'This is vietnamese language in DeviceLogPage';
@override
String get device_log_page_name => 'Lịch sử thiết bị';
@override
String get choose_device_dropdownButton => 'Chọn thiết bị';
@override
String get choose_date_start_datePicker => 'Bắt đầu từ';
@override
String get choose_date_end_datePicker => 'Kết thúc';
@override
String get main_no_data => 'Chưa có dữ liệu.';
@override
String get event_tag_title => 'Sự kiện';
@override
String get description_NOTUSE3 =>
'This is vietnamese language in InterFamily';
@override
String get interfamily_page_name => 'Liên gia';
@override
String get my_group_title => 'Nhóm của tôi';
@override
String get invite_group => 'Nhóm tham gia';
@override
String get add_new_group => 'Thêm nhóm mới';
@override
String get join_group => 'Tham gia nhóm';
@override
String get group_name_title => 'Tên nhóm';
@override
String get group_id_title => 'Mã nhóm';
@override
String get add_new_user_title => 'Thêm người dùng';
@override
String get share_group_title => 'Chia sẻ nhóm';
@override
String get change_group_infomation_title => 'Đổi thông tin';
@override
String get change_group_infomation_content => 'Chỉnh sửa thông tin nhóm';
@override
String get delete_group_title => 'Xóa nhóm';
@override
String get delete_group_content => 'Bạn chắc chắn muốn xóa nhóm này?';
@override
String get leave_group_content => 'Bạn chắc chắn muốn rời nhóm?';
@override
String get dont_have_group => 'Chưa có nhóm';
@override
String get dont_join_group => 'Bạn chưa tham gia nhóm nào';
@override
String get description_group => 'Mô tả';
@override
String get add_new_device_title => 'Thêm thiết bị mới';
@override
String get approve_user => 'Duyệt thành viên';
@override
String get devices_title => 'Thiết bị';
@override
String get device_title => 'Thiết bị';
@override
String get member_title => 'Thành viên';
@override
String get leave_group_title => 'Rời nhóm';
@override
String get dont_have_device => 'Chưa có thiết bị';
@override
String get description_NOTUSE4 =>
'This is vietnamese language in ProfilePage';
@override
String get profile_page_title => 'Cài đặt';
@override
String get profile_change_info => 'Đổi thông tin cá nhân';
@override
String get profile_change_pass => 'Đổi mật khẩu';
@override
String get profile_sim_data => 'Thông tin sim thiết bị';
@override
String get profile_setting => 'Cài đặt thông báo';
@override
String get sim_data_month_left_message => 'tháng còn lại';
@override
String get time_title => 'Thời gian';
@override
String get change_profile_title => 'Thông tin người dùng';
@override
String get change_profile_username => 'Tên người dùng: ';
@override
String get change_profile_username_hint => 'Nhập tên ';
@override
String get change_profile_email => 'Email: ';
@override
String get change_profile_email_hint => 'Nhập email ';
@override
String get change_profile_email_not_empty => 'Email không được để trống';
@override
String get change_profile_tel => 'Số điện thoại: ';
@override
String get change_profile_tel_hint => 'Nhập số điện thoại';
@override
String get change_profile_tel_not_empty =>
'Số điện thoại không được để trống';
@override
String get change_profile_address => 'Địa chỉ: ';
@override
String get change_profile_address_hint => 'Nhập địa chỉ';
@override
String get change_profile_old_pass => 'Mật khẩu cũ: ';
@override
String get change_profile_old_pass_hint => 'Nhập mật khẩu cũ';
@override
String get change_profile_old_pass_not_empty =>
'Mật khẩu không được để trống';
@override
String get change_profile_new_pass => 'Mật khẩu mới: ';
@override
String get change_profile_new_pass_hint => 'Nhập mật khẩu mới';
@override
String get change_profile_new_pass_not_empty =>
'Mật khẩu không được để trống';
@override
String get change_profile_device_notification_select_all => 'Chọn tất cả';
@override
String get change_profile_device_notification_deselect_all =>
'Bỏ chọn tất cả';
@override
String get description_NOTUSE5 => 'This is vietnamese language in BellPage';
@override
String get bell_page_title => 'Thông báo';
@override
String get bell_page_no_items_body => 'Chưa có thông báo';
@override
String get bell_user_uppercase => 'Người dùng';
@override
String get bell_battery_device => 'Pin thiết bị';
@override
String get bell_user_joined_group => 'đã tham gia nhóm';
@override
String get bell_leave_group => 'đã rời nhóm';
@override
String get bell_user_added_group => 'đã được thêm vào nhóm';
@override
String get bell_user_kick_group => 'đã bị xóa khỏi nhóm';
@override
String get bell_operate_normal => 'hoạt động bình thường';
@override
String get bell_invalid_code => 'Mã sự kiện không hợp lệ';
@override
String get bell_days_ago => 'ngày trước';
@override
String get bell_hours_ago => 'giờ trước';
@override
String get bell_minutes_ago => 'phút trước';
@override
String get bell_just_now => 'Vừa xong';
@override
String get bell_read_all => 'Bạn đã xem hết thông báo';
@override
String get description_NOTUSE6 =>
'This is vietnamese language in GlobalFunction';
@override
String get gf_newly_create_message => 'Mới tạo';
@override
String get gf_disconnect_message => 'Mất kết nối';
@override
String get gf_smoke_detected_message => 'Đang hoạt động';
@override
String get gf_no_signal_message => 'Không có sóng';
@override
String get gf_weak_signal_message => 'Mức sóng yếu';
@override
String get gf_moderate_signal_message => 'Mức sóng khá';
@override
String get gf_good_signal_message => 'Mức sóng tốt';
@override
String get gf_volt_detect_message => 'Có điện thế';
@override
String get gf_temp_detect_message => 'Có nhiệt độ';
@override
String get gf_hum_detect_message => 'Có độ ẩm';
@override
String get gf_battery_detect_message => 'Có mức pin';
@override
String get gf_offline_message => 'Không hoạt động';
@override
String get gf_in_firefighting_message => 'Đang chữa cháy';
@override
String get gf_device_error_message => 'Thiết bị lỗi';
@override
String get gf_not_move_message => 'Chưa di chuyển';
@override
String get gf_moving_message => 'Đã di chuyển';
@override
String get gf_remove_from_base_message => 'Bị tháo khỏi đế';
@override
String get gf_connected_lowercase => 'đã kết nối';
@override
String get description_NOTUSE7 => 'This is vietnamese language in LoginPage';
@override
String get login_account_not_empty => 'Tài khoản không được để trống';
@override
String get login_account_hint => 'Tài khoản';
@override
String get login_password_not_empty => 'Mật khẩu không được để trống';
@override
String get login_password_hint => 'Mật khẩu';
@override
String get login_success_message => 'Đăng nhập thành công';
@override
String get login_incorrect_usernameOrPass =>
'Tài khoản hoặc mật khẩu không đúng';
@override
String get login_button_content => 'Đăng nhập';
@override
String get description_NOTUSE9 =>
'This is vietnamese language in DeviceUpdatePage';
@override
String get device_update_title => 'Chỉnh sửa chi tiết thiết bị';
@override
String get device_update_location => 'Vị trí thiết bị';
@override
String get device_update_province => 'Tỉnh/Thành phố';
@override
String get device_update_district => 'Quận/Huyện';
@override
String get device_update_ward => 'Phường/Xã';
@override
String get description_NOTUSE10 =>
'This is vietnamese language in DetailDevicePage';
@override
String get detail_device_dont_has_location_message =>
'Chưa có thông tin về vị trí';
@override
String get detail_device_volt_message => 'Nguồn điện đo được (V)';
@override
String get no_data_message => 'Chưa có';
@override
String get normal_message => 'Bình thường';
@override
String get warning_status_message => 'Cảnh báo';
@override
String get undefine_message => 'Không xác định';
@override
String get low_message_uppercase => 'Yếu';
@override
String get moderate_message_uppercase => 'Khá';
@override
String get good_message_uppercase => 'Tốt';
@override
String get low_message_lowercase => 'yếu';
@override
String get moderate_message_lowercase => 'khá';
@override
String get good_message_lowercase => 'tốt';
@override
String get error_message_uppercase => 'Lỗi';
@override
String get error_message_lowercase => 'lỗi';
@override
String get warning_message => 'Cảnh báo:';
@override
String get loading_message => 'Đang tải...';
@override
String get detail_message => 'Chi tiết';
@override
String get permission_deny_message => 'Quyền bị từ chối';
@override
String get decline_message => 'TỪ CHỐI';
@override
String get allow_message => 'CHO PHÉP';
@override
String get add_button_content => 'Thêm';
@override
String get update_button_content => 'Cập nhật';
@override
String get change_button_content => 'Chỉnh sửa';
@override
String get confirm_button_content => 'Xác nhận';
@override
String get delete_button_content => 'Xóa';
@override
String get cancel_button_content => 'Hủy';
@override
String get find_button_content => 'Tìm';
@override
String get home_page_destination => 'Trang chủ';
@override
String get manager_page_destination => 'Quản lý';
@override
String get map_page_destination => 'Bản đồ';
@override
String get history_page_destination => 'Lịch sử';
@override
String get history_page_destination_tooltip => 'Lịch sử thiết bị';
@override
String get group_page_destination => 'Nhóm';
@override
String get group_page_destination_tooltip => 'Trao đổi thông báo thiết bị';
@override
String get notification_enter_all_inf => 'Vui lòng điền đầy đủ thông tin';
@override
String get notification_update_device_success =>
'Cập nhật thiết bị thành công';
@override
String get notification_update_device_failed => 'Cập nhật thiết bị thất bại';
@override
String get notification_update_device_error => 'Cập nhật lỗi';
@override
String get notification_cannot_find_address_from_location =>
'Không tìm được vị trí';
@override
String get notification_add_device_success => 'Thêm thiết bị thành công';
@override
String get notification_add_device_failed => 'Thêm thiết bị thất bại';
@override
String get notification_create_device_success => 'Tạo thiết bị thành công';
@override
String get notification_create_device_failed => 'Tạo thiết bị thất bại';
@override
String get notification_delete_device_success => 'Xóa thiết bị thành công';
@override
String get notification_delete_device_failed => 'Xóa thiết bị thất bại';
@override
String get notification_device_not_exist => 'Thiết bị không tồn tại';
@override
String get notification_add_group_success => 'Tạo nhóm thành công';
@override
String get notification_add_group_failed => 'Tạo nhóm thất bại';
@override
String get notification_update_group_success => 'Sửa nhóm thành công';
@override
String get notification_update_group_failed => 'Sửa nhóm thất bại';
@override
String get notification_delete_group_success => 'Xóa nhóm thành công';
@override
String get notification_delete_group_failed => 'Xóa nhóm thất bại';
@override
String get notification_leave_group_success => 'Rời nhóm thành công';
@override
String get notification_leave_group_failed => 'Rời nhóm thất bại';
@override
String get notification_join_request_group_success =>
'Yêu cầu tham gia nhóm thành công!';
@override
String get notification_join_request_group_failed =>
'Yêu cầu tham gia nhóm thất bại!';
@override
String get notification_update_profile_success => 'Sửa thông tin thành công';
@override
String get notification_update_profile_failed => 'Sửa thông tin thất bại';
@override
String get notification_update_password_success => 'Đổi mật khẩu thành công';
@override
String get notification_update_password_failed => 'Mật khẩu cũ không khớp';
@override
String get notification_update_device_settings_success =>
'Cập nhật thông báo cho thiết bị thành công';
@override
String get notification_update_device_settings_failed =>
'Cập nhật thông báo cho thiết bị thất bại';
@override
String get notification_confirm_fake_fire_success =>
'Đã cập nhật thông tin đến đội PCCC';
@override
String get notification_confirm_fake_fire_failed =>
'Cập nhật cháy giả thất bại';
}

View File

@@ -21,8 +21,6 @@
"confirm_fake_fire_sure_message": "Tôi chắc chắn", "confirm_fake_fire_sure_message": "Tôi chắc chắn",
"let_PCCC_handle_message": "Hãy để Đội PCCC xử lý!", "let_PCCC_handle_message": "Hãy để Đội PCCC xử lý!",
"overview_message": "Tổng quan", "overview_message": "Tổng quan",
"over_view_owner_devices":"Thiết bị sở hữu",
"over_view_joined_devices":"Thiết bị tham gia",
"total_nof_devices_message": "Tổng số", "total_nof_devices_message": "Tổng số",
"active_devices_message": "Bình thường", "active_devices_message": "Bình thường",
"inactive_devices_message": "Đang tắt", "inactive_devices_message": "Đang tắt",
@@ -112,9 +110,6 @@
"profile_change_info": "Đổi thông tin cá nhân", "profile_change_info": "Đổi thông tin cá nhân",
"profile_change_pass": "Đổi mật khẩu", "profile_change_pass": "Đổi mật khẩu",
"profile_setting": "Cài đặt thông báo", "profile_setting": "Cài đặt thông báo",
"profile_sim_data": "Thông tin sim thiết bị",
"sim_data_month_left_message": "tháng còn lại",
"time_title": "Thời gian",
"change_profile_title": "Thông tin người dùng", "change_profile_title": "Thông tin người dùng",
"change_profile_username": "Tên người dùng: ", "change_profile_username": "Tên người dùng: ",
"change_profile_username_hint": "Nhập tên ", "change_profile_username_hint": "Nhập tên ",

View File

@@ -1,5 +1,5 @@
import '../constant/icon/icon_constants.dart'; import 'package:sfm_app/product/constant/icon/icon_constants.dart';
import '../constant/lang/language_constants.dart'; import 'package:sfm_app/product/constant/lang/language_constants.dart';
class Language { class Language {
final int id; final int id;

View File

@@ -1,9 +1,6 @@
import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import '../utils/app_logger_utils.dart';
import '../constant/status_code/status_code_constants.dart'; import '../constant/status_code/status_code_constants.dart';
import '../cache/local_manager.dart'; import '../cache/local_manager.dart';
@@ -16,7 +13,6 @@ class NetworkManager {
static NetworkManager? _instance; static NetworkManager? _instance;
static NetworkManager? get instance => _instance ??= NetworkManager._init(); static NetworkManager? get instance => _instance ??= NetworkManager._init();
Future<Map<String, String>> getHeaders() async { Future<Map<String, String>> getHeaders() async {
String? token = String? token =
LocaleManager.instance.getStringValue(PreferencesKeys.TOKEN); LocaleManager.instance.getStringValue(PreferencesKeys.TOKEN);
@@ -39,26 +35,15 @@ class NetworkManager {
/// [String] if the request is successful (status code 200), or an empty /// [String] if the request is successful (status code 200), or an empty
/// string if the request fails /// string if the request fails
Future<String> getDataFromServer(String path) async { Future<String> getDataFromServer(String path) async {
try { final url = Uri.https(ApplicationConstants.DOMAIN, path);
final url = Uri.https(ApplicationConstants.DOMAIN, path); log("[${DateTime.now().toLocal().toString().split(' ')[1]}] GET url: $url");
AppLoggerUtils.info("GET url: $url"); final headers = await getHeaders();
// log("[${DateTime.now().toLocal().toString().split(' ')[1]}] GET url: $url"); final response = await http.get(url, headers: headers);
final headers = await getHeaders(); if (response.statusCode == StatusCodeConstants.OK ||
final response = await http.get(url, headers: headers).timeout( response.statusCode == StatusCodeConstants.CREATED) {
Duration(seconds: ApplicationConstants.CALL_API_TIMEOUT), return response.body;
onTimeout: () => } else {
throw TimeoutException('Yêu cầu GET hết thời gian'), return "";
);
if (response.statusCode == StatusCodeConstants.OK ||
response.statusCode == StatusCodeConstants.CREATED) {
return response.body;
} else {
throw Exception('Lỗi server: ${response.statusCode}');
}
} catch (e) {
// AppLoggerUtils.error(message)
// log('Lỗi khi lấy dữ liệu: $e, StackTrace: $stackTrace');
throw Exception('Lỗi khi lấy dữ liệu: $e');
} }
} }
@@ -75,36 +60,16 @@ class NetworkManager {
/// Returns a [Future<String>] containing the server response body. /// Returns a [Future<String>] containing the server response body.
Future<String> getDataFromServerWithParams( Future<String> getDataFromServerWithParams(
String path, Map<String, dynamic> params) async { String path, Map<String, dynamic> params) async {
try { final url = Uri.https(ApplicationConstants.DOMAIN, path, params);
final url = Uri.https(ApplicationConstants.DOMAIN, path, params); log("[${DateTime.now().toLocal().toString().split(' ')[1]}] GET Params url: $url");
AppLoggerUtils.info("GET Params url: $url"); final headers = await getHeaders();
// log("[${DateTime.now().toLocal().toString().split(' ')[1]}] GET Params url: $url"); final response = await http.get(url, headers: headers);
final headers = await getHeaders(); if (response.statusCode == StatusCodeConstants.CREATED ||
final response = await http.get(url, headers: headers).timeout( response.statusCode == StatusCodeConstants.OK) {
Duration(seconds: ApplicationConstants.CALL_API_TIMEOUT), return response.body;
onTimeout: () => } else {
throw TimeoutException('Yêu cầu GET+PARAM hết thời gian'), return "";
);
if (response.statusCode == StatusCodeConstants.OK ||
response.statusCode == StatusCodeConstants.CREATED) {
return response.body;
} else {
throw Exception('Lỗi server: ${response.statusCode}');
}
} catch (e, stackTrace) {
log('Lỗi khi lấy dữ liệu: $e, StackTrace: $stackTrace');
throw Exception('Lỗi khi lấy dữ liệu: $e');
} }
// final url = Uri.https(ApplicationConstants.DOMAIN, path, params);
// log("[${DateTime.now().toLocal().toString().split(' ')[1]}] GET Params url: $url");
// final headers = await getHeaders();
// final response = await http.get(url, headers: headers);
// if (response.statusCode == StatusCodeConstants.CREATED ||
// response.statusCode == StatusCodeConstants.OK) {
// return response.body;
// } else {
// return "";
// }
} }
/// Creates new data on the server using a POST request. /// Creates new data on the server using a POST request.
@@ -112,28 +77,12 @@ class NetworkManager {
/// [path] is the endpoint for the request, and [body] contains the data /// [path] is the endpoint for the request, and [body] contains the data
/// to be sent. Returns the HTTP status code of the response. /// to be sent. Returns the HTTP status code of the response.
Future<int> createDataInServer(String path, Map<String, dynamic> body) async { Future<int> createDataInServer(String path, Map<String, dynamic> body) async {
try { final url = Uri.https(ApplicationConstants.DOMAIN, path);
final url = Uri.https(ApplicationConstants.DOMAIN, path); log("[${DateTime.now().toLocal().toString().split(' ')[1]}] POST url: $url");
AppLoggerUtils.info("POST url: $url"); final headers = await getHeaders();
// log("[${DateTime.now().toLocal().toString().split(' ')[1]}] POST url: $url"); final response =
final headers = await getHeaders(); await http.post(url, headers: headers, body: jsonEncode(body));
final response = await http return response.statusCode;
.post(url, headers: headers, body: jsonEncode(body))
.timeout(
Duration(seconds: ApplicationConstants.CALL_API_TIMEOUT),
onTimeout: () =>
throw TimeoutException('Yêu cầu POST hết thời gian'),
);
if (response.statusCode == StatusCodeConstants.OK ||
response.statusCode == StatusCodeConstants.CREATED) {
return response.statusCode;
} else {
throw Exception('Lỗi server: ${response.statusCode}');
}
} catch (e, stackTrace) {
log('Lỗi khi lấy dữ liệu: $e, StackTrace: $stackTrace');
throw Exception('Lỗi khi lấy dữ liệu: $e');
}
} }
/// Updates existing data on the server using a PUT request. /// Updates existing data on the server using a PUT request.
@@ -141,27 +90,12 @@ class NetworkManager {
/// [path] is the endpoint for the request, and [body] contains the data /// [path] is the endpoint for the request, and [body] contains the data
/// to be updated. Returns the HTTP status code of the response. /// to be updated. Returns the HTTP status code of the response.
Future<int> updateDataInServer(String path, Map<String, dynamic> body) async { Future<int> updateDataInServer(String path, Map<String, dynamic> body) async {
try { final url = Uri.https(ApplicationConstants.DOMAIN, path);
final url = Uri.https(ApplicationConstants.DOMAIN, path); log("[${DateTime.now().toLocal().toString().split(' ')[1]}] PUT url: $url");
AppLoggerUtils.info("PUT url: $url"); final headers = await getHeaders();
// log("[${DateTime.now().toLocal().toString().split(' ')[1]}] PUT url: $url"); final response =
final headers = await getHeaders(); await http.put(url, headers: headers, body: jsonEncode(body));
final response = return response.statusCode;
await http.put(url, headers: headers, body: jsonEncode(body)).timeout(
Duration(seconds: ApplicationConstants.CALL_API_TIMEOUT),
onTimeout: () =>
throw TimeoutException('Yêu cầu PUT hết thời gian'),
);
if (response.statusCode == StatusCodeConstants.OK ||
response.statusCode == StatusCodeConstants.CREATED) {
return response.statusCode;
} else {
throw Exception('Lỗi server: ${response.statusCode}');
}
} catch (e, stackTrace) {
log('Lỗi khi lấy dữ liệu: $e, StackTrace: $stackTrace');
throw Exception('Lỗi khi lấy dữ liệu: $e');
}
} }
/// Deletes data from the server using a DELETE request. /// Deletes data from the server using a DELETE request.
@@ -171,25 +105,10 @@ class NetworkManager {
/// A status code of 200 indicates success, while other codes indicate /// A status code of 200 indicates success, while other codes indicate
/// failure or an error. /// failure or an error.
Future<int> deleteDataInServer(String path) async { Future<int> deleteDataInServer(String path) async {
try { final url = Uri.https(ApplicationConstants.DOMAIN, path);
final url = Uri.https(ApplicationConstants.DOMAIN, path); log("[${DateTime.now().toLocal().toString().split(' ')[1]}] DELETE url: $url");
// log("[${DateTime.now().toLocal().toString().split(' ')[1]}] DELETE url: $url"); final headers = await getHeaders();
AppLoggerUtils.info("DELETE url: $url"); final response = await http.delete(url, headers: headers);
final headers = await getHeaders(); return response.statusCode;
final response = await http.delete(url, headers: headers).timeout(
Duration(seconds: ApplicationConstants.CALL_API_TIMEOUT),
onTimeout: () =>
throw TimeoutException('Yêu cầu DELETE hết thời gian'),
);
if (response.statusCode == StatusCodeConstants.OK ||
response.statusCode == StatusCodeConstants.CREATED) {
return response.statusCode;
} else {
throw Exception('Lỗi server: ${response.statusCode}');
}
} catch (e, stackTrace) {
log('Lỗi khi lấy dữ liệu: $e, StackTrace: $stackTrace');
throw Exception('Lỗi khi lấy dữ liệu: $e');
}
} }
} }

View File

@@ -1,10 +1,10 @@
import 'dart:developer'; import 'dart:developer';
import 'dart:io'; import 'dart:io';
import 'package:app_settings/app_settings.dart'; import 'package:app_settings/app_settings.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:sfm_app/product/base/widget/dialog/request_permission_dialog.dart';
import '../base/widget/dialog/request_permission_dialog.dart';
class LocationPermissionRequest { class LocationPermissionRequest {
LocationPermissionRequest._init(); LocationPermissionRequest._init();

View File

@@ -1,10 +1,8 @@
import 'dart:developer'; import 'dart:developer';
import 'dart:io';
import 'package:app_settings/app_settings.dart'; import 'package:app_settings/app_settings.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_local_notifications/flutter_local_notifications.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import '../base/widget/dialog/request_permission_dialog.dart'; import '../base/widget/dialog/request_permission_dialog.dart';
@@ -13,7 +11,7 @@ class NotificationPermission {
static NotificationPermission? _instance; static NotificationPermission? _instance;
static NotificationPermission get instance => static NotificationPermission get instance =>
_instance ??= NotificationPermission._init(); _instance ??= NotificationPermission._init();
static final FlutterLocalNotificationsPlugin _notificationsPlugin = FlutterLocalNotificationsPlugin();
Future<bool> checkNotificationPermission(context) async { Future<bool> checkNotificationPermission(context) async {
var status = await Permission.notification.status; var status = await Permission.notification.status;
log("Status: $status"); log("Status: $status");
@@ -46,21 +44,4 @@ class NotificationPermission {
Icons.location_on_outlined, "ABCDE", AppSettingsType.notification); Icons.location_on_outlined, "ABCDE", AppSettingsType.notification);
} }
} }
Future<bool?> requestNotificationPermission() async {
try {
if (Platform.isAndroid) {
return await _notificationsPlugin
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
?.requestNotificationsPermission();
} else if (Platform.isIOS) {
return await _notificationsPlugin
.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(alert: true, sound: true, badge: true);
}
return null;
} catch (e) {
log("Error requesting notification permission: $e");
return null;
}
}
} }

View File

@@ -1,35 +0,0 @@
import 'dart:io';
import 'package:alarm/alarm.dart';
@pragma('vm:entry-point')
class AlarmServices {
Future<void> showAlarm(String title, String body) async {
final DateTime now = DateTime.now();
final AlarmSettings alarmSettings = AlarmSettings(
id: 42,
dateTime: now,
assetAudioPath: 'assets/sounds/warning_alarm.mp3',
loopAudio: true,
vibrate: true,
warningNotificationOnKill: Platform.isIOS,
androidFullScreenIntent: true,
allowAlarmOverlap: true,
volumeSettings: VolumeSettings.fade(
volume: 1.0,
fadeDuration: const Duration(seconds: 3),
volumeEnforced: true,
),
notificationSettings: NotificationSettings(
title: title,
body: body,
stopButton: 'Dừng cảnh báo',
icon: 'ic_launcher',
),
);
await Alarm.set(alarmSettings: alarmSettings);
}
void cancelAlarm({int id = 42}) async {
await Alarm.stop(id);
}
}

View File

@@ -1,24 +1,10 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'package:connectivity_plus/connectivity_plus.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:http/http.dart' as http; import 'package:http/http.dart' as http;
import '../shared/model/province_model.dart';
import '../utils/app_logger_utils.dart';
import '../../feature/device_log/device_logs_model.dart';
import '../../feature/devices/device_model.dart';
import '../../feature/home/device_alias_model.dart';
import '../../feature/inter_family/group_detail/group_detail_model.dart';
import '../../feature/inter_family/groups/groups_model.dart';
import '../../feature/settings/device_notification_settings/device_notification_settings_model.dart';
import '../../feature/settings/profile/profile_model.dart';
import '../constant/app/api_path_constant.dart'; import '../constant/app/api_path_constant.dart';
import '../shared/model/district_model.dart';
import '../shared/model/ward_model.dart';
import '../shared/shared_snack_bar.dart'; import '../shared/shared_snack_bar.dart';
import '../constant/enums/app_route_enums.dart'; import '../constant/enums/app_route_enums.dart';
import 'language_services.dart'; import 'language_services.dart';
@@ -55,86 +41,6 @@ class APIServices {
return headers; return headers;
} }
Future<T> executeApiCall<T>(Future<dynamic> Function() apiCall,
{T Function(dynamic)? parser,
String errorMessage = 'Lỗi khi gọi API',
T Function(int)? statusCodeHandler}) async {
try {
final response = await apiCall().timeout(
Duration(seconds: ApplicationConstants.CALL_API_TIMEOUT),
onTimeout: () => throw TimeoutException('Yêu cầu hết thời gian'),
);
if (statusCodeHandler != null && response is int) {
return statusCodeHandler(response);
}
if (response is String && response != "") {
if (parser != null) {
try {
return parser(jsonDecode(response));
} catch (e) {
throw Exception('Lỗi parsing dữ liệu: $e');
}
}
return response as T;
} else {
throw Exception('Dữ liệu trả về rỗng');
}
} catch (e, stackTrace) {
AppLoggerUtils.error("Lỗi gọi API", e, stackTrace);
throw Exception('$errorMessage: $e');
}
}
/// Most Used Function
// Future<T> execute<T>(Future<T> Function() apiCall) async {
// try {
// return await apiCall();
// } catch (e) {
// AppLoggerUtils.error(e.toString());
// return Future.error(e);
// }
// }
Future<T> execute<T>(
BuildContext context,
Future<T> Function() apiCall, {
bool checkMounted = true,
}) async {
try {
// Quick connectivity check before attempting network calls.
final conn = await Connectivity().checkConnectivity();
if (conn == ConnectivityResult.none) {
AppLoggerUtils.warning('No network connectivity');
if (checkMounted && context.mounted) {
showErrorTopSnackBarCustom(context, "Không có kết nối mạng");
}
return Future.error(const SocketException('No network connectivity'));
}
return await apiCall();
} on SocketException catch (e, stackTrace) {
// Network-related errors (DNS, timeout, host lookup...)
AppLoggerUtils.warning('Network error when calling API: $e');
if (checkMounted && context.mounted) {
AppLoggerUtils.error("Không có kết nối mạng");
}
return Future.error(e);
} catch (e, stackTrace) {
// If widget was unmounted (e.g. background isolate), preserve previous behavior
if (checkMounted && !context.mounted) {
return Future.error('Widget not mounted');
}
AppLoggerUtils.error("Lỗi hệ thống khi gọi API", e, stackTrace);
if (checkMounted && context.mounted) {
showErrorTopSnackBarCustom(context, "Lỗi hệ thống");
}
return Future.error(e);
}
}
Future<String> login(String path, Map<String, dynamic> loginRequest) async { Future<String> login(String path, Map<String, dynamic> loginRequest) async {
final url = Uri.https(ApplicationConstants.DOMAIN, path); final url = Uri.https(ApplicationConstants.DOMAIN, path);
final headers = await getHeaders(); final headers = await getHeaders();
@@ -143,11 +49,14 @@ class APIServices {
return response.body; return response.body;
} }
Future<int> sendNotificationToken(String token) async { Future<int> sendNotificationToken(String token) async{
String uid = await getUID(); String uid = await getUID();
Map<String, dynamic> body = {"user_id": uid, "app_token": token}; Map<String,dynamic> body = {
int statusCode = await NetworkManager.instance! "user_id": uid,
.updateDataInServer(APIPathConstants.NOTIFICATION_TOKEN_PATH, body); "app_token": token
};
int statusCode = await NetworkManager.instance!.updateDataInServer(
APIPathConstants.NOTIFICATION_TOKEN_PATH, body);
return statusCode; return statusCode;
} }
@@ -160,7 +69,7 @@ class APIServices {
actions: [ actions: [
TextButton( TextButton(
onPressed: () async { onPressed: () async {
var url = Uri.https(ApplicationConstants.DOMAIN, var url = Uri.http(ApplicationConstants.DOMAIN,
APIPathConstants.LOGOUT_PATH); APIPathConstants.LOGOUT_PATH);
final headers = await NetworkManager.instance!.getHeaders(); final headers = await NetworkManager.instance!.getHeaders();
final response = await http.post(url, headers: headers); final response = await http.post(url, headers: headers);
@@ -213,89 +122,67 @@ class APIServices {
return language; return language;
} }
Future<Bell> getBellNotifications(String offset, String pageSize) async { Future<Bell> getBellNotifications(String offset, String pagesize) async {
final params = {"offset": offset, "page_size": pageSize}; Bell bell = Bell();
return executeApiCall( final params = {"offset": offset, "page_size": pagesize};
() => NetworkManager.instance!.getDataFromServerWithParams( final data = await NetworkManager.instance!.getDataFromServerWithParams(
APIPathConstants.BELL_NOTIFICATIONS_PATH, params), APIPathConstants.BELL_NOTIFICATIONS_PATH, params);
parser: (json) => Bell.fromJson(json), if (data != "") {
errorMessage: 'Lỗi khi GET /${APIPathConstants.BELL_NOTIFICATIONS_PATH}', bell = Bell.fromJson(jsonDecode(data));
); return bell;
} else {
return bell;
}
} }
Future<int> updateStatusOfNotification(List<String> notificationID) async { Future<int> updateStatusOfNotification(List<String> notificationID) async {
Map<String, dynamic> body = { Map<String, dynamic> body = {
"event_ids": notificationID, "event_ids": notificationID,
}; };
return executeApiCall( int statusCode = await NetworkManager.instance!.updateDataInServer(
() => NetworkManager.instance!.updateDataInServer( APIPathConstants.BELL_UPDATE_READ_NOTIFICATIONS_PATH, body);
APIPathConstants.BELL_UPDATE_READ_NOTIFICATIONS_PATH, body), return statusCode;
statusCodeHandler: (statusCode) => statusCode,
errorMessage: 'Lỗi khi PUT /${APIPathConstants.BELL_NOTIFICATIONS_PATH}',
);
} }
Future<User> getUserDetail() async { Future<String> getUserDetail() async {
String uid = await getUID(); String uid = await getUID();
return executeApiCall( String? response = await NetworkManager.instance!
() => NetworkManager.instance! .getDataFromServer('${APIPathConstants.USER_PATH}/$uid');
.getDataFromServer('${APIPathConstants.USER_PATH}/$uid'), return response;
parser: (json) => User.fromJson(json),
errorMessage: 'Lỗi khi GET /${APIPathConstants.USER_PATH}',
);
} }
Future<int> updateUserProfile(Map<String, dynamic> body) async { Future<int> updateUserProfile(Map<String, dynamic> body) async {
String uid = await getUID(); String uid = await getUID();
return executeApiCall( int statusCode = await NetworkManager.instance!
() => NetworkManager.instance!.updateDataInServer( .updateDataInServer("${APIPathConstants.USER_PROFILE_PATH}/$uid", body);
"${APIPathConstants.USER_PROFILE_PATH}/$uid", body), return statusCode;
statusCodeHandler: (statusCode) => statusCode,
errorMessage: 'Lỗi khi PUT /${APIPathConstants.USER_PROFILE_PATH}',
);
} }
Future<int> updateUserPassword(Map<String, dynamic> body) async { Future<int> updateUserPassword(Map<String, dynamic> body) async {
String uid = await getUID(); String uid = await getUID();
// int statusCode = await NetworkManager.instance!.updateDataInServer( int statusCode = await NetworkManager.instance!.updateDataInServer(
// "${APIPathConstants.USER_PATH}/$uid/password", body); "${APIPathConstants.USER_PATH}/$uid/password", body);
return executeApiCall( return statusCode;
() => NetworkManager.instance!.updateDataInServer(
"${APIPathConstants.USER_PATH}/$uid/password", body),
statusCodeHandler: (statusCode) => statusCode,
errorMessage: 'Lỗi khi PUT /${APIPathConstants.USER_PATH}');
} }
Future<List<DeviceNotificationSettings>> Future<String> getAllSettingsNotificationOfDevices() async {
getAllSettingsNotificationOfDevices() async { String? data = await NetworkManager.instance!
return executeApiCall( .getDataFromServer(APIPathConstants.DEVICE_NOTIFICATION_SETTINGS);
() => NetworkManager.instance! return data;
.getDataFromServer(APIPathConstants.DEVICE_NOTIFICATION_SETTINGS),
parser: (json) =>
DeviceNotificationSettings.mapFromJson(json['data']).values.toList(),
errorMessage:
'Lỗi khi GET /${APIPathConstants.DEVICE_NOTIFICATION_SETTINGS}',
);
} }
Future<int> updateDeviceNotificationSettings( Future<int> updateDeviceNotificationSettings(
String thingID, Map<String, int> data) async { String thingID, Map<String, int> data) async {
Map<String, dynamic> body = {"thing_id": thingID, "notifi_settings": data}; Map<String, dynamic> body = {"thing_id": thingID, "notifi_settings": data};
return executeApiCall( int statusCode = await NetworkManager.instance!.updateDataInServer(
() => NetworkManager.instance!.updateDataInServer( APIPathConstants.DEVICE_NOTIFICATION_SETTINGS, body);
APIPathConstants.DEVICE_NOTIFICATION_SETTINGS, body), return statusCode;
statusCodeHandler: (statusCode) => statusCode,
errorMessage:
'Lỗi khi PUT /${APIPathConstants.DEVICE_NOTIFICATION_SETTINGS}');
} }
Future<List<DeviceWithAlias>> getDashBoardDevices() async { Future<String> getDashBoardDevices() async {
return executeApiCall( String? data = await NetworkManager.instance!
() => NetworkManager.instance! .getDataFromServer(APIPathConstants.DASHBOARD_DEVICES);
.getDataFromServer(APIPathConstants.DASHBOARD_DEVICES), return data;
parser: (json) => DeviceWithAlias.fromJsonDynamicList(json['items']),
errorMessage: 'Lỗi khi GET /${APIPathConstants.DASHBOARD_DEVICES}',
);
} }
Future<int> setupDeviceNotification(String thingID, String deviceName) async { Future<int> setupDeviceNotification(String thingID, String deviceName) async {
@@ -315,103 +202,68 @@ class APIServices {
"104": 1, "104": 1,
} }
}; };
return executeApiCall( int statusCode = await NetworkManager.instance!.updateDataInServer(
() => NetworkManager.instance!.updateDataInServer( APIPathConstants.DEVICE_NOTIFICATION_SETTINGS, body);
APIPathConstants.DEVICE_NOTIFICATION_SETTINGS, body), return statusCode;
statusCodeHandler: (statusCode) => statusCode,
errorMessage:
'Lỗi khi PUT /${APIPathConstants.DEVICE_NOTIFICATION_SETTINGS}');
} }
Future<List<Province>> getAllProvinces() async { Future<String> getAllProvinces() async {
return executeApiCall( String? data = await NetworkManager.instance!
() => NetworkManager.instance! .getDataFromServer(APIPathConstants.PROVINCES_PATH);
.getDataFromServer(APIPathConstants.PROVINCES_PATH), return data;
parser: (json) => Province.fromJsonDynamicList(json['items']),
errorMessage: 'Lỗi khi GET /${APIPathConstants.PROVINCES_PATH}');
} }
Future<List<Province>> getProvincesByName(String name) async { Future<String> getProvincesByName(String name) async {
final params = {'name': name}; final params = {'name': name};
return executeApiCall( String? data = await NetworkManager.instance!
() => NetworkManager.instance! .getDataFromServerWithParams(APIPathConstants.PROVINCES_PATH, params);
.getDataFromServerWithParams(APIPathConstants.PROVINCES_PATH, params), return data;
parser: (json) => Province.fromJsonDynamicList(json['items']),
errorMessage: 'Lỗi khi GET /${APIPathConstants.PROVINCES_PATH}/$name',
);
} }
Future<Province> getProvinceByID(String provinceID) async { Future<String> getProvinceByID(String provinceID) async {
return executeApiCall( String data = await NetworkManager.instance!
() => NetworkManager.instance! .getDataFromServer("${APIPathConstants.PROVINCES_PATH}/$provinceID");
.getDataFromServer("${APIPathConstants.PROVINCES_PATH}/$provinceID"), return data;
parser: (json) => Province.fromJson(json['data']),
errorMessage:
'Lỗi khi GET /${APIPathConstants.PROVINCES_PATH}/$provinceID}',
);
} }
Future<List<District>> getAllDistricts(String provinceID) async { Future<String> getAllDistricts(String provinceID) async {
final params = {"parent": provinceID}; final params = {"parent": provinceID};
return executeApiCall( String? data = await NetworkManager.instance!
() => NetworkManager.instance! .getDataFromServerWithParams(APIPathConstants.DISTRICTS_PATH, params);
.getDataFromServerWithParams(APIPathConstants.DISTRICTS_PATH, params), return data;
parser: (json) => District.fromJsonDynamicList(json['items']),
errorMessage:
'Lỗi khi GET /${APIPathConstants.DISTRICTS_PATH} by parentCode $provinceID',
);
} }
Future<List<District>> getDistrictsByName(String districtName) async { Future<String> getDistrictsByName(String districtName) async {
final params = {"name": districtName}; final params = {"name": districtName};
return executeApiCall( String? data = await NetworkManager.instance!
() => NetworkManager.instance! .getDataFromServerWithParams(APIPathConstants.DISTRICTS_PATH, params);
.getDataFromServerWithParams(APIPathConstants.DISTRICTS_PATH, params), return data;
parser: (json) => District.fromJsonDynamicList(json['items']),
errorMessage:
'Lỗi khi GET /${APIPathConstants.DISTRICTS_PATH} by name $districtName',
);
} }
Future<District> getDistrictByID(String districtID) async { Future<String> getDistrictByID(String districtID) async {
return executeApiCall( String? data = await NetworkManager.instance!
() => NetworkManager.instance! .getDataFromServer("${APIPathConstants.DISTRICTS_PATH}/$districtID");
.getDataFromServer("${APIPathConstants.DISTRICTS_PATH}/$districtID"), return data;
parser: (json) => District.fromJson(json['data']),
errorMessage:
'Lỗi khi GET /${APIPathConstants.DISTRICTS_PATH}/$districtID',
);
} }
Future<List<Ward>> getAllWards(String districtID) async { Future<String> getAllWards(String districtID) async {
final params = {'parent': districtID}; final params = {'parent': districtID};
return executeApiCall( String? data = await NetworkManager.instance!
() => NetworkManager.instance! .getDataFromServerWithParams(APIPathConstants.WARDS_PATH, params);
.getDataFromServerWithParams(APIPathConstants.WARDS_PATH, params), return data;
parser: (json) => Ward.fromJsonDynamicList(json['items']),
errorMessage:
'Lỗi khi GET /${APIPathConstants.WARDS_PATH} by parent $districtID',
);
} }
Future<List<Ward>> getWardsByName(String wardName) async { Future<String> getWarsdByName(String wardName) async {
final params = {"name": wardName}; final params = {"name": wardName};
return executeApiCall( String? data = await NetworkManager.instance!
() => NetworkManager.instance! .getDataFromServerWithParams(APIPathConstants.WARDS_PATH, params);
.getDataFromServerWithParams(APIPathConstants.WARDS_PATH, params), return data;
parser: (json) => Ward.fromJsonDynamicList(json['items']),
errorMessage:
'Lỗi khi GET /${APIPathConstants.WARDS_PATH} by name $wardName',
);
} }
Future<Ward> getWardByID(String wardID) async { Future<String> getWardByID(String wardID) async {
return executeApiCall( String? data = await NetworkManager.instance!
() => NetworkManager.instance! .getDataFromServer("${APIPathConstants.WARDS_PATH}/$wardID");
.getDataFromServer("${APIPathConstants.WARDS_PATH}/$wardID"), return data;
parser: (json) => Ward.fromJson(json['data']),
errorMessage: 'Lỗi khi GET /${APIPathConstants.WARDS_PATH}/$wardID',
);
} }
Future<int> confirmFakeFireByUser(String thingID) async { Future<int> confirmFakeFireByUser(String thingID) async {
@@ -419,187 +271,131 @@ class APIServices {
"state": 3, "state": 3,
"note": "Người dùng xác nhận cháy giả!" "note": "Người dùng xác nhận cháy giả!"
}; };
return executeApiCall( int statusCode = await NetworkManager.instance!
() => NetworkManager.instance!.updateDataInServer( .updateDataInServer("${APIPathConstants.DEVICE_PATH}/$thingID", body);
"${APIPathConstants.DEVICE_PATH}/$thingID", body), return statusCode;
statusCodeHandler: (statusCode) => statusCode,
errorMessage: 'Lỗi khi PUT /${APIPathConstants.DEVICE_PATH}/$thingID');
} }
Future<List<Device>> getOwnerDevices() async { Future<String> getOwnerDevices() async {
return executeApiCall( String? data = await NetworkManager.instance!
() => NetworkManager.instance! .getDataFromServer(APIPathConstants.DEVICE_PATH);
.getDataFromServer(APIPathConstants.DEVICE_PATH), return data;
parser: (json) => Device.fromJsonDynamicList(json['items']),
errorMessage: 'Lỗi khi GET /${APIPathConstants.DEVICE_PATH}',
);
} }
Future<List<Device>> getOwnerDeviceByState( Future<String> getOwnerDeviceByState(Map<String, dynamic> params) async {
Map<String, dynamic> params) async { String? data = await NetworkManager.instance!
return executeApiCall( .getDataFromServerWithParams(APIPathConstants.DEVICE_PATH, params);
() => NetworkManager.instance! return data;
.getDataFromServerWithParams(APIPathConstants.DEVICE_PATH, params),
parser: (json) => Device.fromJsonDynamicList(json['items']),
errorMessage: 'Lỗi khi GET /${APIPathConstants.DEVICE_PATH}',
);
} }
Future<int> createDeviceByAdmin(Map<String, dynamic> body) async { Future<int> createDeviceByAdmin(Map<String, dynamic> body) async {
return executeApiCall( int? statusCode = await NetworkManager.instance!
() => NetworkManager.instance! .createDataInServer(APIPathConstants.DEVICE_PATH, body);
.createDataInServer(APIPathConstants.DEVICE_PATH, body), return statusCode;
statusCodeHandler: (statusCode) => statusCode,
errorMessage: 'Lỗi khi POST /${APIPathConstants.DEVICE_PATH}');
} }
Future<int> registerDevice(Map<String, dynamic> body) async { Future<int> registerDevice(Map<String, dynamic> body) async {
return executeApiCall( int? statusCode = await NetworkManager.instance!
() => NetworkManager.instance! .createDataInServer(APIPathConstants.DEVICE_REGISTER_PATH, body);
.createDataInServer(APIPathConstants.DEVICE_REGISTER_PATH, body), return statusCode;
statusCodeHandler: (statusCode) => statusCode,
errorMessage: 'Lỗi khi POST /${APIPathConstants.DEVICE_REGISTER_PATH}');
} }
Future<int> deleteDeviceByAdmin(String thingID) async { Future<int> deleteDeviceByAdmin(String thingID) async {
return executeApiCall( int statusCode = await NetworkManager.instance!
() => NetworkManager.instance! .deleteDataInServer("${APIPathConstants.DEVICE_PATH}/$thingID");
.deleteDataInServer("${APIPathConstants.DEVICE_PATH}/$thingID"), return statusCode;
statusCodeHandler: (statusCode) => statusCode,
errorMessage:
'Lỗi khi DELETE /${APIPathConstants.DEVICE_PATH}/$thingID');
} }
Future<int> unregisterDevice(Map<String, dynamic> body) async { Future<int> unregisterDevice(Map<String, dynamic> body) async {
return executeApiCall( int statusCode = await NetworkManager.instance!
() => NetworkManager.instance! .createDataInServer(APIPathConstants.DEVICE_UNREGISTER_PATH, body);
.createDataInServer(APIPathConstants.DEVICE_UNREGISTER_PATH, body), return statusCode;
statusCodeHandler: (statusCode) => statusCode,
errorMessage:
'Lỗi khi DELETE /${APIPathConstants.DEVICE_UNREGISTER_PATH} by USER');
} }
Future<Device> getDeviceInformation(String thingID) async { Future<String> getDeviceInfomation(String thingID) async {
return executeApiCall( String? response = await NetworkManager.instance!
() => NetworkManager.instance! .getDataFromServer("${APIPathConstants.DEVICE_PATH}/$thingID");
.getDataFromServer("${APIPathConstants.DEVICE_PATH}/$thingID"), return response;
parser: (json) => Device.fromJson(json),
errorMessage: 'Lỗi khi GET /${APIPathConstants.DEVICE_PATH}/$thingID',
);
} }
Future<List<Group>> getAllGroups() async { Future<String> getAllGroups() async {
return executeApiCall( String? body = await NetworkManager.instance!
() => NetworkManager.instance! .getDataFromServer(APIPathConstants.ALL_GROUPS_PATH);
.getDataFromServer(APIPathConstants.ALL_GROUPS_PATH), return body;
parser: (json) => Group.fromJsonDynamicList(json['items']),
errorMessage: 'Lỗi khi GET /${APIPathConstants.USER_PATH}',
);
} }
Future<int> createGroup(Map<String, dynamic> body) async { Future<int> createGroup(Map<String, dynamic> body) async {
return executeApiCall( int? statusCode = await NetworkManager.instance!
() => NetworkManager.instance! .createDataInServer(APIPathConstants.GROUPS_PATH, body);
.createDataInServer(APIPathConstants.GROUPS_PATH, body), return statusCode;
statusCodeHandler: (statusCode) => statusCode,
errorMessage: 'Lỗi khi POST /${APIPathConstants.GROUPS_PATH}');
} }
Future<int> updateGroup(Map<String, dynamic> body, String groupID) async { Future<int> updateGroup(Map<String, dynamic> body, String groupID) async {
return executeApiCall( int? statusCode = await NetworkManager.instance!
() => NetworkManager.instance!.updateDataInServer( .updateDataInServer("${APIPathConstants.GROUPS_PATH}/$groupID", body);
"${APIPathConstants.GROUPS_PATH}/$groupID", body), return statusCode;
statusCodeHandler: (statusCode) => statusCode,
errorMessage: 'Lỗi khi PUT /${APIPathConstants.GROUPS_PATH}/$groupID');
} }
Future<int> joinGroup(String groupID, Map<String, dynamic> body) async { Future<int> joinGroup(String groupID, Map<String, dynamic> body) async {
return executeApiCall( int? statusCode = await NetworkManager.instance!
() => NetworkManager.instance! .createDataInServer(APIPathConstants.JOIN_GROUP_PATH, body);
.createDataInServer(APIPathConstants.JOIN_GROUP_PATH, body), return statusCode;
statusCodeHandler: (statusCode) => statusCode,
errorMessage: 'Lỗi khi POST /${APIPathConstants.JOIN_GROUP_PATH}');
} }
Future<int> deleteGroup(String groupID) async { Future<int> deleteGroup(String groupID) async {
return executeApiCall( int? statusCode = await NetworkManager.instance!
() => NetworkManager.instance! .deleteDataInServer("${APIPathConstants.GROUPS_PATH}/$groupID");
.deleteDataInServer("${APIPathConstants.GROUPS_PATH}/$groupID"), return statusCode;
statusCodeHandler: (statusCode) => statusCode,
errorMessage:
'Lỗi khi DELETE /${APIPathConstants.GROUPS_PATH}/$groupID');
} }
Future<GroupDetail> getGroupDetail(String groupID) async { Future<String> getGroupDetail(String groupID) async {
return executeApiCall( String? body = await NetworkManager.instance!
() => NetworkManager.instance! .getDataFromServer("${APIPathConstants.GROUPS_PATH}/$groupID");
.getDataFromServer("${APIPathConstants.GROUPS_PATH}/$groupID"), return body;
parser: (json) => GroupDetail.fromJson(json),
errorMessage: 'Lỗi khi GET /${APIPathConstants.GROUPS_PATH}/$groupID',
);
} }
Future<int> approveGroup(Map<String, dynamic> body) async { Future<int> approveGroup(Map<String, dynamic> body) async {
return executeApiCall( int statusCode = await NetworkManager.instance!
() => NetworkManager.instance! .createDataInServer(APIPathConstants.APPROVE_GROUP_PATH, body);
.createDataInServer(APIPathConstants.APPROVE_GROUP_PATH, body), return statusCode;
statusCodeHandler: (statusCode) => statusCode,
errorMessage: 'Lỗi khi POST /${APIPathConstants.APPROVE_GROUP_PATH}');
} }
Future<int> deleteUserInGroup(String groupID, String userID) async { Future<int> deleteUserInGroup(String groupID, String userID) async {
return executeApiCall( int? statusCode = await NetworkManager.instance!.deleteDataInServer(
() => NetworkManager.instance!.deleteDataInServer( "${APIPathConstants.GROUPS_PATH}/$groupID/users/$userID");
"${APIPathConstants.GROUPS_PATH}/$groupID/users/$userID"), return statusCode;
statusCodeHandler: (statusCode) => statusCode,
errorMessage:
'Lỗi khi DELETE /${APIPathConstants.GROUPS_PATH}/$groupID/users/$userID');
} }
Future<int> deleteDeviceInGroup(String groupID, String thingID) async { Future<int> deleteDeviceInGroup(String groupID, String thingID) async {
return executeApiCall( int? statusCode = await NetworkManager.instance!.deleteDataInServer(
() => NetworkManager.instance!.deleteDataInServer( "${APIPathConstants.GROUPS_PATH}/$groupID/devices/$thingID");
"${APIPathConstants.GROUPS_PATH}/$groupID/devices/$thingID"), return statusCode;
statusCodeHandler: (statusCode) => statusCode,
errorMessage:
'Lỗi khi DELETE /${APIPathConstants.GROUPS_PATH}/$groupID/devices/$thingID');
} }
Future<int> updateDeviceAlias(Map<String, dynamic> body) async { Future<int> updateDeviceAlias(Map<String, dynamic> body) async {
return executeApiCall( int? statusCode = await NetworkManager.instance!.updateDataInServer(
() => NetworkManager.instance!.updateDataInServer( APIPathConstants.DEVICE_NOTIFICATION_SETTINGS, body);
APIPathConstants.DEVICE_NOTIFICATION_SETTINGS, body), return statusCode;
statusCodeHandler: (statusCode) => statusCode,
errorMessage:
'Lỗi khi PUT /${APIPathConstants.DEVICE_NOTIFICATION_SETTINGS}');
} }
Future<int> addDeviceToGroup( Future<int> addDeviceToGroup(
String groupID, Map<String, dynamic> body) async { String groupID, Map<String, dynamic> body) async {
return executeApiCall( int? statusCode = await NetworkManager.instance!.createDataInServer(
() => NetworkManager.instance!.createDataInServer( "${APIPathConstants.GROUPS_PATH}/$groupID/things", body);
"${APIPathConstants.GROUPS_PATH}/$groupID/things", body), return statusCode;
statusCodeHandler: (statusCode) => statusCode,
errorMessage:
'Lỗi khi PUT /${APIPathConstants.GROUPS_PATH}/$groupID/things');
} }
Future<int> updateOwnerDevice( Future<int> updateOwnerDevice(
String thingID, Map<String, dynamic> body) async { String thingID, Map<String, dynamic> body) async {
return executeApiCall( int? statusCode = await NetworkManager.instance!
() => NetworkManager.instance!.updateDataInServer( .updateDataInServer("${APIPathConstants.DEVICE_PATH}/$thingID", body);
"${APIPathConstants.DEVICE_PATH}/$thingID", body), return statusCode;
statusCodeHandler: (statusCode) => statusCode,
errorMessage: 'Lỗi khi PUT /${APIPathConstants.DEVICE_PATH}/$thingID');
} }
Future<DeviceLog> getLogsOfDevice( Future<String> getLogsOfDevice(
String thingID, Map<String, dynamic> params) async { String thingID, Map<String, dynamic> params) async {
return executeApiCall( String? body = await NetworkManager.instance!
() => NetworkManager.instance!.getDataFromServerWithParams( .getDataFromServerWithParams(APIPathConstants.DEVICE_LOGS_PATH, params);
APIPathConstants.DEVICE_LOGS_PATH, params), return body;
parser: (json) => DeviceLog.fromJson(json),
errorMessage: 'Lỗi khi GET /${APIPathConstants.DEVICE_LOGS_PATH}',
);
} }
} }

View File

@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import '../cache/local_manager.dart'; import '../cache/local_manager.dart';
import '../constant/enums/local_keys_enums.dart'; import '../constant/enums/local_keys_enums.dart';
import '../constant/lang/language_constants.dart'; import '../constant/lang/language_constants.dart';
import '../lang/l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class LanguageServices { class LanguageServices {
Future<Locale> setLocale(String languageCode) async { Future<Locale> setLocale(String languageCode) async {

View File

@@ -10,8 +10,9 @@ import '../shared/find_location_maps/model/prediction_model.dart';
import '../shared/model/near_by_search_model.dart'; import '../shared/model/near_by_search_model.dart';
class MapServices { class MapServices {
Future<List<PlaceDetails>> getNearbyPlaces(
double latitude, double longitude, String searchKey, int radius, String type) async { Future<List<PlaceDetails>> getNearbyPlaces(double latitude, double longitude,
String searchKey, int radius, String type) async {
List<PlaceDetails> result = []; List<PlaceDetails> result = [];
var url = Uri.parse( var url = Uri.parse(
'https://maps.googleapis.com/maps/api/place/autocomplete/json?input=$searchKey&language=vi&location=$latitude%2C$longitude&radius=$radius&strictbounds=true&type=$type&key=${ApplicationConstants.MAP_KEY}'); 'https://maps.googleapis.com/maps/api/place/autocomplete/json?input=$searchKey&language=vi&location=$latitude%2C$longitude&radius=$radius&strictbounds=true&type=$type&key=${ApplicationConstants.MAP_KEY}');
@@ -45,11 +46,12 @@ class MapServices {
PolylineResult result = await polylinePoints.getRouteBetweenCoordinates( PolylineResult result = await polylinePoints.getRouteBetweenCoordinates(
googleApiKey: ApplicationConstants.MAP_KEY, googleApiKey: ApplicationConstants.MAP_KEY,
request: PolylineRequest( request: PolylineRequest(
origin: PointLatLng(origin.latitude, origin.longitude),
origin: PointLatLng(origin.latitude, origin.longitude), destination: PointLatLng(destination.latitude, destination.longitude),
destination: PointLatLng(destination.latitude, destination.longitude), mode: TravelMode.driving,
mode: TravelMode.driving, optimizeWaypoints: true
optimizeWaypoints: true)); )
);
if (result.points.isNotEmpty) { if (result.points.isNotEmpty) {
for (var point in result.points) { for (var point in result.points) {
polylineCoordinates.add(LatLng(point.latitude, point.longitude)); polylineCoordinates.add(LatLng(point.latitude, point.longitude));
@@ -60,4 +62,5 @@ class MapServices {
return []; return [];
} }
} }
} }

View File

@@ -1,262 +1,121 @@
import 'dart:developer' as dev; import 'dart:developer' as dev;
import 'dart:io';
import 'dart:math' as math; import 'dart:math' as math;
import 'package:alarm/alarm.dart'; import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart'
as firebase_messaging;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:persistent_bottom_nav_bar/persistent_bottom_nav_bar.dart';
import '../utils/app_logger_utils.dart';
import '../../firebase_options.dart';
import 'alarm_services.dart';
@pragma('vm:entry-point')
class NotificationServices { class NotificationServices {
static final FlutterLocalNotificationsPlugin _notificationsPlugin = FirebaseMessaging messaging = FirebaseMessaging.instance;
FlutterLocalNotificationsPlugin(); final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
final firebase_messaging.FirebaseMessaging _messaging =
firebase_messaging.FirebaseMessaging.instance;
AlarmServices alarmServices = AlarmServices();
Future<void> initialize() async { Future<void> initLocalNotifications(PersistentTabController controller) async {
await initializeLocalNotifications(); const AndroidInitializationSettings androidInitializationSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
dev.log("NotificationService initialized"); const DarwinInitializationSettings iosInitializationSettings = DarwinInitializationSettings();
} const InitializationSettings initializationSettings = InitializationSettings(
android: androidInitializationSettings,
iOS: iosInitializationSettings,
);
@pragma('vm:entry-point') await _flutterLocalNotificationsPlugin.initialize(
Future<void> initializeLocalNotifications() async { initializationSettings,
try { onDidReceiveNotificationResponse: (NotificationResponse response) {
const androidInitSettings = dev.log("Người dùng click thông báo ở foreground với payload: ${response.payload}");
AndroidInitializationSettings('@mipmap/ic_launcher'); handleMessage(response.payload,controller);
final darwinInitSettings = DarwinInitializationSettings( },
onDidReceiveLocalNotification: _onDidReceiveLocalNotification, );
); dev.log("Local notifications initialized");
final initSettings = InitializationSettings(
android: androidInitSettings,
iOS: darwinInitSettings,
);
await _notificationsPlugin.initialize(
initSettings,
onDidReceiveNotificationResponse: _onDidReceiveNotificationResponse,
);
dev.log("Local notifications initialized");
} catch (e) {
AppLoggerUtils.error("Failed to initialize local notifications", e);
}
}
Future<void> _onDidReceiveLocalNotification(
int id, String? title, String? body, String? payload) async {
// Handle local notification tap when app is foreground (iOS)
dev.log("Local notification tapped (foreground): payload=$payload");
}
Future<void> _onDidReceiveNotificationResponse(
NotificationResponse response) async {
// Handle local notification tap (both foreground & background)
final payload = response.payload;
dev.log("Notification tapped: payload=$payload");
if (payload != null && payload.isNotEmpty) {
final controller = PersistentTabController(initialIndex: 0);
handleMessage(payload, controller);
}
} }
void firebaseInit(BuildContext context) { void firebaseInit(BuildContext context) {
firebase_messaging.FirebaseMessaging.onMessage.listen((message) { FirebaseMessaging.onMessage.listen((message) {
_handleForegroundMessage(message); dev.log("Foreground message payload: ${message.toMap()}");
if (WidgetsBinding.instance != null) {
showNotification(message);
} else {
dev.log("App is in background, skipping foreground notification");
}
}); });
} }
Future<String> getDeviceToken() async {
print("GET FB TOKEN");
String? token = await messaging.getAPNSToken();
print("GET FB: ${token}");
return token!;
}
void isTokenRefresh() { void isTokenRefresh() {
_messaging.onTokenRefresh.listen((newToken) { messaging.onTokenRefresh.listen((newToken) {
dev.log("Refresh Firebase Messaging Token: $newToken"); dev.log("Refresh Firebase Messaging Token: $newToken");
}); });
} }
static Future<bool?> requestNotificationPermission() async { Future<void> showNotification(RemoteMessage message) async {
try { dev.log(message.toString());
final messaging = firebase_messaging.FirebaseMessaging.instance; dev.log(message.data.toString());
final settings = await messaging.requestPermission( dev.log(message.data["notification"].toString());
alert: true, String? title = message.data['title'];
announcement: false, String? body = message.data['body'];
badge: true, String type = message.data['type'] ?? "normal";
provisional: false,
sound: true, if (title == null || body == null) {
); dev.log("Skipping notification due to missing title or body");
return settings.authorizationStatus == return;
firebase_messaging.AuthorizationStatus.authorized;
} catch (e) {
AppLoggerUtils.error("Failed to request notification permission", e);
return null;
} }
}
/// Xử lý message khi app foreground AndroidNotificationChannel androidNotificationChannel = AndroidNotificationChannel(
Future<void> _handleForegroundMessage( math.Random.secure().nextInt(1000000).toString(),
firebase_messaging.RemoteMessage message) async { 'High Importance Notification',
try { importance: Importance.max,
final type = message.data['type'] as String? ?? 'normal'; );
final title =
message.notification?.title ?? message.data['title'] ?? 'SFM';
final body = message.notification?.body ?? message.data['body'] ?? '';
dev.log('Foreground message: type=$type, title=$title, body=$body'); final androidPlugin = _flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>();
await androidPlugin?.deleteNotificationChannel(androidNotificationChannel.id);
if (type == 'smoke_warning') { AndroidNotificationDetails androidNotificationDetails = AndroidNotificationDetails(
// Hiển thị alarm, không hiển thị notification androidNotificationChannel.id,
await _showAlarm(title, body); androidNotificationChannel.name,
} else { channelDescription: "Channel description",
// Hiển thị notification local sound: getSound(type),
await _showForegroundNotification(title, body, type); importance: androidNotificationChannel.importance,
} priority: Priority.high,
} catch (e, stackTrace) { ticker: 'ticker',
AppLoggerUtils.error('Error handling foreground message', e, stackTrace); actions: type == "smoke_warning"
} ? [
} const AndroidNotificationAction(
"id1",
"Hogg xóa được",
// true thì khi nhấn vào button sẽ mở giao diện ra
showsUserInterface: true,
cancelNotification: false,
)
]
: null
);
/// Hiển thị alarm (smoke warning) const DarwinNotificationDetails darwinNotificationDetails = DarwinNotificationDetails(
Future<void> _showAlarm(String title, String body) async { presentAlert: true,
try { presentBadge: true,
await Alarm.init(); presentBanner: true,
presentSound: true,
);
final alarmSettings = AlarmSettings( NotificationDetails notificationDetails = NotificationDetails(
id: 42, android: androidNotificationDetails,
dateTime: DateTime.now(), iOS: darwinNotificationDetails,
assetAudioPath: 'assets/sounds/warning_alarm.mp3', );
loopAudio: true,
vibrate: true,
warningNotificationOnKill: Platform.isIOS,
androidFullScreenIntent: true,
volumeSettings: VolumeSettings.fade(
volume: 0.8,
fadeDuration: const Duration(seconds: 5),
volumeEnforced: true,
),
notificationSettings: NotificationSettings(
title: title,
body: body,
stopButton: 'Dừng thông báo',
icon: "ic_launcher",
),
);
await Alarm.set(alarmSettings: alarmSettings); // Truyền payload vào thông báo
dev.log('Alarm set successfully: $title'); String payload = message.data['type'] ?? "default";
} catch (e, stackTrace) { await _flutterLocalNotificationsPlugin.show(
AppLoggerUtils.error('Failed to set alarm', e, stackTrace); math.Random.secure().nextInt(1000000),
} title,
} body,
notificationDetails,
/// Hiển thị notification local khi app foreground payload: payload,
Future<void> _showForegroundNotification( );
String title, String body, String type) async { dev.log("Displayed notification with title: $title, body: $body, type: $type");
try {
final androidDetails = AndroidNotificationDetails(
'sfm_channel_${math.Random.secure().nextInt(1000000)}',
'SFM Notifications',
channelDescription: 'Notifications from SFM app',
sound: getSound(type),
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker',
);
final iosDetails = DarwinNotificationDetails(
sound: _getSoundNameForIOS(type),
presentAlert: true,
presentBadge: true,
presentBanner: true,
presentSound: true,
);
final notificationDetails = NotificationDetails(
android: androidDetails,
iOS: iosDetails,
);
await _notificationsPlugin.show(
math.Random.secure().nextInt(1000000),
title,
body,
notificationDetails,
payload: type,
);
dev.log('Foreground notification shown: title=$title, type=$type');
} catch (e, stackTrace) {
AppLoggerUtils.error(
'Failed to show foreground notification', e, stackTrace);
}
}
/// Hiển thị notification cho background/terminate
Future<void> showBackgroundOrTerminateNotification(
firebase_messaging.RemoteMessage message) async {
try {
final type = message.data['type'] as String? ?? 'normal';
final title =
message.notification?.title ?? message.data['title'] ?? 'SFM';
final body = message.notification?.body ?? message.data['body'] ?? '';
dev.log('Background/terminate notification: type=$type, title=$title');
// Cho Android: nếu type = smoke_warning, hiển thị alarm
// Cho iOS: chỉ hiển thị notification, APNs payload sẽ phát sound natively
if (type == 'smoke_warning' && Platform.isAndroid) {
dev.log('→ Showing alarm for Android background');
await _showAlarm(title, body);
return;
}
// Hiển thị notification (cho cả iOS lẫn Android, nhưng iOS không có alarm)
final channelId = math.Random.secure().nextInt(1000000).toString();
const channelName = 'SFM Notifications';
const channelDescription = 'Notifications from SFM app';
final androidDetails = AndroidNotificationDetails(
channelId,
channelName,
channelDescription: channelDescription,
sound: getSound(type),
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker',
);
final iosDetails = DarwinNotificationDetails(
sound: _getSoundNameForIOS(type),
presentAlert: true,
presentBadge: true,
presentBanner: true,
presentSound: true,
);
final notificationDetails = NotificationDetails(
android: androidDetails,
iOS: iosDetails,
);
await _notificationsPlugin.show(
int.parse(channelId),
title,
body,
notificationDetails,
payload: type,
);
dev.log(
'Background/terminate notification shown: title=$title, type=$type');
} catch (e, stackTrace) {
AppLoggerUtils.error(
'Failed to show background/terminate notification', e, stackTrace);
}
} }
AndroidNotificationSound getSound(String type) { AndroidNotificationSound getSound(String type) {
@@ -269,110 +128,35 @@ class NotificationServices {
} else if (type == "battery_warning") { } else if (type == "battery_warning") {
return const RawResourceAndroidNotificationSound("new_alarm"); return const RawResourceAndroidNotificationSound("new_alarm");
} else { } else {
return const RawResourceAndroidNotificationSound("warning_alarm"); return const RawResourceAndroidNotificationSound("normal");
} }
} }
String _getSoundNameForIOS(String type) {
// iOS tìm file trong app bundle (không có đuôi)
if (type == "smoke_warning" || type == "battery_warning") {
return "warning_alarm"; // file: warning_alarm.caf trong bundle
}
return "default";
}
void handleMessage(String? payload, PersistentTabController controller) { void handleMessage(String? payload, PersistentTabController controller) {
AppLoggerUtils.info("Handling notification tap with payload: $payload"); dev.log("Handling notification tap with payload: $payload");
controller.jumpToTab(1); controller.jumpToTab(1);
if (payload == "smoke_warning") {
// TODO: Navigate to smoke warning screen nếu cần
// NavigationRouter.navigateToSmokeWarningScreen();
} else if (payload == "battery_warning") {
// TODO: Navigate to battery warning screen nếu cần
// NavigationRouter.navigateToBatteryWarningScreen();
}
} }
Future<void> setupInteractMessage(PersistentTabController controller) async { Future<void> setupInteractMessage(PersistentTabController controller) async {
// Khi app terminated, được mở bằng notification // Khi app terminated
firebase_messaging.RemoteMessage? initialMessage = RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage();
await firebase_messaging.FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null) { if (initialMessage != null) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
final type = initialMessage.data['type'] as String?; try {
handleMessage(type, controller); handleMessage(initialMessage.data['type'],controller);
} catch (e, stack) {
dev.log("Error handling initial message: $e\n$stack");
}
}); });
} }
// Khi app ở background
// Khi app background, user tap notification FirebaseMessaging.onMessageOpenedApp.listen((message) {
firebase_messaging.FirebaseMessaging.onMessageOpenedApp.listen((message) {
try { try {
final type = message.data['type'] as String?; handleMessage(message.data['type'],controller);
handleMessage(type, controller); } catch (e, stack) {
} catch (e, stackTrace) { dev.log("Error in onMessageOpenedApp: $e\n$stack");
AppLoggerUtils.error('Error in onMessageOpenedApp', e, stackTrace);
} }
}); });
} }
@pragma('vm:entry-point')
static Future<void> firebaseMessagingBackgroundHandler(
firebase_messaging.RemoteMessage message) async {
try {
// Log to device storage for debugging background isolate
dev.log('═══ BACKGROUND HANDLER STARTED ═══');
dev.log('Message data: ${message.data}');
dev.log('Notification: ${message.notification}');
await Alarm.init();
dev.log('✓ Alarm initialized');
if (Firebase.apps.isEmpty) {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
name: "sfm-notification",
);
dev.log('✓ Firebase initialized in background');
} else {
dev.log('✓ Firebase already initialized');
}
final type = message.data['type'] as String? ?? 'normal';
final title = message.notification?.title ??
message.data['title'] ??
'Thông báo từ SmartFM';
final body = message.notification?.body ??
message.data['body'] ??
'Bạn có một thông báo mới';
dev.log('Message type: $type');
dev.log('Title: $title');
dev.log('Body: $body');
// Logic: Hiển thị local notification cho background/terminate
// iOS sẽ phát sound natively thông qua APNs payload (aps.sound)
// Android sẽ phát sound thông qua local notification
try {
final notificationService = NotificationServices();
await notificationService.initialize();
dev.log('✓ Notification service initialized');
await notificationService
.showBackgroundOrTerminateNotification(message);
dev.log('✓ Background/terminate notification shown');
} catch (notifError, notifStackTrace) {
dev.log('✗ ERROR showing notification: $notifError');
dev.log('Stack trace: $notifStackTrace');
rethrow;
}
dev.log('═══ BACKGROUND HANDLER COMPLETED ═══');
} catch (e, stackTrace) {
dev.log('✗ BACKGROUND HANDLER FAILED');
dev.log('Error: $e');
dev.log('Stack trace: $stackTrace');
}
}
} }

View File

@@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sfm_app/product/constant/image/image_constants.dart';
import '../constant/image/image_constants.dart';
class SharedBackground extends StatelessWidget { class SharedBackground extends StatelessWidget {
final Widget child; final Widget child;

View File

@@ -1,23 +0,0 @@
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
class SharedComponentLoadingAnimation extends StatefulWidget {
const SharedComponentLoadingAnimation({super.key});
@override
State<SharedComponentLoadingAnimation> createState() => _SharedComponentLoadingAnimationState();
}
class _SharedComponentLoadingAnimationState extends State<SharedComponentLoadingAnimation> {
@override
Widget build(BuildContext context) {
return Center(
child: LottieBuilder.asset(
'assets/animations/component_loading.json',
width: 80,
height: 80,
fit: BoxFit.fill,
),
);
}
}

View File

@@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sfm_app/product/constant/icon/icon_constants.dart';
import '../constant/icon/icon_constants.dart';
const int _kDuration = 300; const int _kDuration = 300;
const double _kWidth = 60; const double _kWidth = 60;

View File

@@ -1,7 +1,7 @@
import 'package:fl_chart/fl_chart.dart'; import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../feature/device_log/device_logs_model.dart'; import 'package:sfm_app/feature/device_log/device_logs_model.dart';
import '../utils/date_time_utils.dart'; import 'package:sfm_app/product/utils/date_time_utils.dart';
Widget sharedLineChart(String chartName, List<SensorLogs> sensors) { Widget sharedLineChart(String chartName, List<SensorLogs> sensors) {
return LineChart( return LineChart(

View File

@@ -1,23 +0,0 @@
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
class SharedLoadingAnimation extends StatefulWidget {
const SharedLoadingAnimation({super.key});
@override
State<SharedLoadingAnimation> createState() => _SharedLoadingAnimationState();
}
class _SharedLoadingAnimationState extends State<SharedLoadingAnimation> {
@override
Widget build(BuildContext context) {
return Center(
child: LottieBuilder.asset(
'assets/animations/loading.json',
width: 100,
height: 100,
fit: BoxFit.fill,
),
);
}
}

View File

@@ -182,23 +182,23 @@ class _SharedPieChartState extends State<SharedPieChart> {
switch (originalIndex) { switch (originalIndex) {
case 0: // OFFLINE_STATE case 0: // OFFLINE_STATE
log("Touched Index device state = -1"); log("Touched Index device state = -1");
widget.devicesManagerBloc.getDeviceByState(context,-1); widget.devicesManagerBloc.getDeviceByState(-1);
break; break;
case 1: // NORMAL_STATE case 1: // NORMAL_STATE
log("Touched Index Get device state = 0"); log("Touched Index Get device state = 0");
widget.devicesManagerBloc.getDeviceByState(context,0); widget.devicesManagerBloc.getDeviceByState(0);
break; break;
case 2: // WARNING_STATE case 2: // WARNING_STATE
log("Touched Index Get device state = 1"); log("Touched Index Get device state = 1");
widget.devicesManagerBloc.getDeviceByState(context,1); widget.devicesManagerBloc.getDeviceByState(1);
break; break;
case 3: // INPROGRESS_STATE case 3: // INPROGRESS_STATE
log("Touched Index Get device state = 2"); log("Touched Index Get device state = 2");
widget.devicesManagerBloc.getDeviceByState(context,2); widget.devicesManagerBloc.getDeviceByState(2);
break; break;
case 4: // ERROR_STATE case 4: // ERROR_STATE
log("Touched Index Get device state = 3"); log("Touched Index Get device state = 3");
widget.devicesManagerBloc.getDeviceByState(context,3); widget.devicesManagerBloc.getDeviceByState(3);
break; break;
} }
} }

View File

@@ -1,26 +0,0 @@
import 'package:flutter/material.dart';
class SharedRocketContainer extends CustomClipper<Path> {
@override
Path getClip(Size size) {
final double width = size.width;
final double height = size.height;
const double pointyWidth = 20.0;
Path path = Path();
path.moveTo(0, 0);
path.lineTo(width - pointyWidth, 0);
path.lineTo(width, height / 2);
path.lineTo(width - pointyWidth, height);
path.lineTo(0, height);
path.close();
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) {
return false;
}
}

View File

@@ -1,9 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sfm_app/product/extension/context_extension.dart';
import 'package:top_snackbar_flutter/custom_snack_bar.dart'; import 'package:top_snackbar_flutter/custom_snack_bar.dart';
import 'package:top_snackbar_flutter/top_snack_bar.dart'; import 'package:top_snackbar_flutter/top_snack_bar.dart';
import '../extension/context_extension.dart';
void showNoIconTopSnackBar(BuildContext context, String message, void showNoIconTopSnackBar(BuildContext context, String message,
Color backgroundColor, Color textColor) { Color backgroundColor, Color textColor) {
if (!context.mounted) return; if (!context.mounted) return;

View File

@@ -1,7 +1,7 @@
import 'package:flex_color_scheme/flex_color_scheme.dart'; import 'package:flex_color_scheme/flex_color_scheme.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sfm_app/product/theme/app_theme.dart';
import 'app_theme.dart';
class AppThemeDark extends AppTheme { class AppThemeDark extends AppTheme {
static AppThemeDark? _instance; static AppThemeDark? _instance;
static AppThemeDark get instance { static AppThemeDark get instance {
@@ -13,7 +13,6 @@ class AppThemeDark extends AppTheme {
@override @override
ThemeData get theme => FlexThemeData.dark( ThemeData get theme => FlexThemeData.dark(
scaffoldBackground: Colors.black,
useMaterial3: true, useMaterial3: true,
scheme: FlexScheme.flutterDash, scheme: FlexScheme.flutterDash,
subThemesData: const FlexSubThemesData( subThemesData: const FlexSubThemesData(

View File

@@ -13,7 +13,6 @@ class AppThemeLight extends AppTheme {
@override @override
ThemeData get theme => FlexThemeData.light( ThemeData get theme => FlexThemeData.light(
scaffoldBackground: Colors.white,
useMaterial3: true, useMaterial3: true,
scheme: FlexScheme.flutterDash, scheme: FlexScheme.flutterDash,
bottomAppBarElevation: 20.0, bottomAppBarElevation: 20.0,

View File

@@ -1,39 +0,0 @@
import 'package:logger/logger.dart';
class AppLoggerUtils{
static final Logger _logger = Logger(
printer: PrettyPrinter(
methodCount: 2,
errorMethodCount: 8,
lineLength: 120,
colors: true,
printEmojis: true,
dateTimeFormat: (DateTime dateTime) {
// Tùy chỉnh định dạng thời gian
return '[${DateTime.now().toLocal().toString().split(' ')[1]}]';
},
// dateTimeFormat: DateTimeFormat.dateAndTime
),
level: Level.debug, // Cấp độ log tối thiểu (có thể thay đổi trong môi trường production)
);
static void debug(String message, [dynamic error, StackTrace? stackTrace]) {
_logger.d(message);
}
static void info(String message, [dynamic error, StackTrace? stackTrace]) {
_logger.i(message);
}
static void warning(String message, [dynamic error, StackTrace? stackTrace]) {
_logger.w(message, error: error, stackTrace: stackTrace);
}
static void error(String message, [dynamic error, StackTrace? stackTrace]) {
_logger.e(message, error: error, stackTrace: stackTrace);
}
static void trace(String message, [dynamic error, StackTrace? stackTrace]) {
_logger.t(message, error: error, stackTrace: stackTrace);
}
}

View File

@@ -1,10 +1,12 @@
import 'package:flutter/material.dart'; import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:sfm_app/feature/device_log/device_logs_model.dart';
import 'package:sfm_app/product/services/api_services.dart';
import 'package:sfm_app/product/services/language_services.dart';
import 'package:sfm_app/product/shared/model/district_model.dart';
import 'package:sfm_app/product/shared/model/province_model.dart';
import '../../feature/device_log/device_logs_model.dart';
import '../services/api_services.dart';
import '../services/language_services.dart';
import '../shared/model/district_model.dart';
import '../shared/model/province_model.dart';
import '../../feature/devices/device_model.dart'; import '../../feature/devices/device_model.dart';
import '../constant/icon/icon_constants.dart'; import '../constant/icon/icon_constants.dart';
import '../shared/model/ward_model.dart'; import '../shared/model/ward_model.dart';
@@ -91,34 +93,30 @@ class DeviceUtils {
return map; return map;
} }
Future<String> getFullDeviceLocation( Future<String> getFullDeviceLocation(
BuildContext context, String areaPath, String? deviceName) async { BuildContext context, String areaPath) async {
if (areaPath.isNotEmpty) { if (areaPath != "") {
List<String> parts = areaPath.split('_'); List<String> parts = areaPath.split('_');
if (parts.length < 3 || parts[0].isEmpty || parts[1].isEmpty || parts[2].isEmpty) {
if (deviceName != null && deviceName.isNotEmpty) {
return deviceName;
} else {
return appLocalization(context).no_data_message;
}
}
String provinceID = parts[0]; String provinceID = parts[0];
String districtID = parts[1]; String districtID = parts[1];
String wardID = parts[2]; String wardID = parts[2];
Province province = await apiServices.getProvinceByID(provinceID); String provinceBody = await apiServices.getProvinceByID(provinceID);
District district = await apiServices.getDistrictByID(districtID); final provinceItem = jsonDecode(provinceBody);
Ward ward = await apiServices.getWardByID(wardID); Province province = Province.fromJson(provinceItem['data']);
String districtBody = await apiServices.getDistrictByID(districtID);
final districtItem = jsonDecode(districtBody);
District district = District.fromJson(districtItem['data']);
String wardBody = await apiServices.getWardByID(wardID);
final wardItem = jsonDecode(wardBody);
Ward ward = Ward.fromJson(wardItem['data']);
return "${ward.fullName}, ${district.fullName}, ${province.fullName}"; return "${ward.fullName}, ${district.fullName}, ${province.fullName}";
} }
return appLocalization(context).no_data_message; return appLocalization(context).no_data_message;
} }
String checkStateDevice(BuildContext context, int state) { String checkStateDevice(BuildContext context, int state) {
String message = appLocalization(context).no_data_message; String message = appLocalization(context).no_data_message;
if (state == 1) { if (state == 1) {
@@ -159,11 +157,11 @@ class DeviceUtils {
} else if (state == 0) { } else if (state == 0) {
return const Color(0xFF9EF16D); return const Color(0xFF9EF16D);
} else if (state == 2) { } else if (state == 2) {
return const Color(0xFFF5EF44); return const Color(0xFFF5EF44);;
} else if (state == -1) { } else if (state == -1) {
return const Color(0xFFBBBAC2); return const Color(0xFFBBBAC2);;
} else { } else {
return const Color(0xFFF5EF44); return const Color(0xFFF5EF44);;
} }
} }

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