feat(api_service): Update try-catch funtion and handle exception
update(loading_animation): Update loading animation using Lottie
This commit is contained in:
anhtunz
2025-06-09 14:29:43 +07:00
parent 477646ab9d
commit 3a8fa3633c
44 changed files with 1659 additions and 1065 deletions

View File

@@ -1,4 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<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.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE" />
@@ -24,7 +26,7 @@
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/launcher_icon"> android:icon="@mipmap/launcher_icon">
<meta-data android:name="com.google.android.geo.API_KEY" <meta-data android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyA9C7Pmxw6Gw3H2mM4WA_XGngRIIr2VS7k"/> android:value="AIzaSyDI8b-PUgKUgj5rHdtgEHCwWjUXYJrqYhE"/>
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:launchMode="singleTop" android:launchMode="singleTop"

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -9,6 +9,7 @@ import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:sfm_app/product/services/api_services.dart'; import 'package:sfm_app/product/services/api_services.dart';
import 'package:sfm_app/product/utils/device_utils.dart'; import 'package:sfm_app/product/utils/device_utils.dart';
import '../product/shared/shared_snack_bar.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';
@@ -42,14 +43,12 @@ class DetailDeviceBloc extends BlocBase {
String thingID, String thingID,
Completer<GoogleMapController> controller, Completer<GoogleMapController> controller,
) async { ) async {
String body = await apiServices.getDeviceInfomation(thingID); try {
if (body != "") { Device device = await apiServices.getDeviceInformation(thingID);
final data = jsonDecode(body);
Device device = Device.fromJson(data);
sinkDeviceInfo.add(device); sinkDeviceInfo.add(device);
if (device.areaPath != null) { if (device.areaPath != null) {
String fullLocation = await DeviceUtils.instance String fullLocation = await DeviceUtils.instance
.getFullDeviceLocation(context, device.areaPath!); .getFullDeviceLocation(context, device.areaPath!, "");
log("Location: $fullLocation"); log("Location: $fullLocation");
sinkDeviceLocation.add(fullLocation); sinkDeviceLocation.add(fullLocation);
} }
@@ -74,40 +73,46 @@ class DetailDeviceBloc extends BlocBase {
mapController mapController
.animateCamera(CameraUpdate.newCameraPosition(cameraPosition)); .animateCamera(CameraUpdate.newCameraPosition(cameraPosition));
} }
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(context, e.toString());
} }
} }
void findLocation(BuildContext context, String areaPath) async { void findLocation(BuildContext context, String areaPath) async {
String fullLocation = String fullLocation =
await DeviceUtils.instance.getFullDeviceLocation(context, areaPath); await DeviceUtils.instance.getFullDeviceLocation(context, areaPath, "");
sinkDeviceLocation.add(fullLocation); sinkDeviceLocation.add(fullLocation);
} }
void getNearerSensorValue(String thingID) async { void getNearerSensorValue(BuildContext context,String thingID) async {
List<SensorLogs> sensorTemps = []; try {
DateTime twoDaysAgo = DateTime.now().subtract(const Duration(days: 2)); List<SensorLogs> sensorTemps = [];
String from = DateTimeUtils.instance.formatDateTimeToString(twoDaysAgo); DateTime twoDaysAgo = DateTime.now().subtract(const Duration(days: 2));
String now = DateTimeUtils.instance.formatDateTimeToString(DateTime.now()); String from = DateTimeUtils.instance.formatDateTimeToString(twoDaysAgo);
Map<String, dynamic> params = { String now = 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 = await apiServices.getLogsOfDevice(thingID, params);
final data = jsonDecode(body);
DeviceLog devicesListLog = DeviceLog.fromJson(data);
if (devicesListLog.sensors!.isNotEmpty) { if (devicesListLog.sensors!.isNotEmpty) {
for (var sensor in devicesListLog.sensors!) { for (var sensor in devicesListLog.sensors!) {
sensorTemps.add(sensor); sensorTemps.add(sensor);
} }
sensorTemps = sensorTemps.reversed.toList(); sensorTemps = sensorTemps.reversed.toList();
sinkSensorTemps.add(sensorTemps); sinkSensorTemps.add(sensorTemps);
} else{ } else {
sinkSensorTemps.add([]); sinkSensorTemps.add([]);
} }
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
sinkSensorTemps.add([]);
} }
} }
} }

View File

@@ -1,10 +1,13 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/material.dart';
import '../feature/devices/device_model.dart'; import '../feature/devices/device_model.dart';
import '../product/base/bloc/base_bloc.dart'; import '../product/base/bloc/base_bloc.dart';
import '../product/constant/app/app_constants.dart'; import '../product/constant/app/app_constants.dart';
import '../product/services/api_services.dart'; import '../product/services/api_services.dart';
import '../product/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';
@@ -35,40 +38,41 @@ class DeviceLogsBloc extends BlocBase {
@override @override
void dispose() {} void dispose() {}
void getAllDevices() async { void getAllDevices(BuildContext context) async {
String body = await apiServices.getOwnerDevices(); try {
if (body != "") { List<Device> originalDevices = await apiServices.getOwnerDevices();
final data = jsonDecode(body);
List<dynamic> items = data['items'];
List<Device> originalDevices = Device.fromJsonDynamicList(items);
List<Device> devices = List<Device> devices =
DeviceUtils.instance.sortDeviceByState(originalDevices); DeviceUtils.instance.sortDeviceByState(originalDevices);
sinkAllDevices.add(devices); sinkAllDevices.add(devices);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(context, e.toString());
} }
} }
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 {
sinkmessage.add(ApplicationConstants.LOADING); try {
String fromDateString = sinkmessage.add(ApplicationConstants.LOADING);
DateTimeUtils.instance.formatDateTimeToString(fromDate); String fromDateString =
String now = DateTimeUtils.instance.formatDateTimeToString(DateTime.now()); DateTimeUtils.instance.formatDateTimeToString(fromDate);
Map<String, dynamic> params = { String now =
'thing_id': thingID, DateTimeUtils.instance.formatDateTimeToString(DateTime.now());
'from': fromDateString, Map<String, dynamic> params = {
'to': now, 'thing_id': thingID,
'limit': '30', 'from': fromDateString,
"offset": offset.toString(), 'to': now,
"asc": "true" 'limit': '30',
}; "offset": offset.toString(),
final body = await apiServices.getLogsOfDevice(thingID, params); "asc": "true"
if (body != "") { };
final data = jsonDecode(body); DeviceLog devicesListLog =
DeviceLog devicesListLog = DeviceLog.fromJson(data); await apiServices.getLogsOfDevice(thingID, params);
if (devicesListLog.sensors!.isEmpty) { if (devicesListLog.sensors!.isEmpty) {
bool hasMore = false; bool hasMore = false;
sinkHasMore.add(hasMore); sinkHasMore.add(hasMore);
@@ -81,6 +85,9 @@ class DeviceLogsBloc extends BlocBase {
sinkmessage.add(ApplicationConstants.NO_DATA); sinkmessage.add(ApplicationConstants.NO_DATA);
} }
sinkSensors.add(sensors); sinkSensors.add(sensors);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(context, e.toString());
} }
} }
} }

View File

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

@@ -8,6 +8,7 @@ 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/shared/shared_snack_bar.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';
@@ -29,77 +30,114 @@ class DetailGroupBloc extends BlocBase {
@override @override
void dispose() {} void dispose() {}
Future<void> getGroupDetail(String groupID) async { Future<void> getGroupDetail(BuildContext context,String groupID) async {
final body = await apiServices.getGroupDetail(groupID); try {
final data = jsonDecode(body); List<DeviceOfGroup> warningDevices = [];
List<DeviceOfGroup> warningDevices = []; GroupDetail group = await apiServices.getGroupDetail(groupID);
if (data != null) { sinkDetailGroup.add(group);
GroupDetail group = GroupDetail.fromJson(data); if (group.devices != null) {
sinkDetailGroup.add(group); for (var device in group.devices!) {
if (group.devices != null) { if (device.state == 1) {
for (var device in group.devices!) { warningDevices.add(device);
if (device.state == 1) { }
warningDevices.add(device);
} }
sinkWarningDevice.add(warningDevices);
} }
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 {
Map<String, dynamic> body = {"group_id": groupID, "user_id": userID}; try {
int statusCode = await apiServices.approveGroup(body); Map<String, dynamic> body = {"group_id": groupID, "user_id": userID};
showSnackBarResponseByStatusCode(context, statusCode, int statusCode = await apiServices.approveGroup(body);
"Đã duyệt $userName vào nhóm!", "Duyệt $userName thất bại!"); 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 {
int statusCode = await apiServices.deleteUserInGroup(groupID, userID); try {
showSnackBarResponseByStatusCode(context, statusCode, int statusCode = await apiServices.deleteUserInGroup(groupID, userID);
"Đã xóa người dùng $userName", "Xóa người dùng $userName thất bại"); 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 {
int statusCode = await apiServices.deleteDeviceInGroup(groupID, thingID); try {
showSnackBarResponseByStatusCode(context, statusCode, int statusCode = await apiServices.deleteDeviceInGroup(groupID, thingID);
"Đã xóa thiết bị $deviceName", "Xóa thiết bị $deviceName thất bại"); 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 {
int statusCode = await apiServices.deleteUserInGroup(groupID, userID); try {
showSnackBarResponseByStatusCode( int statusCode = await apiServices.deleteUserInGroup(groupID, userID);
context, showSnackBarResponseByStatusCode(
statusCode, context,
appLocalization(context).notification_leave_group_success, statusCode,
appLocalization(context).notification_leave_group_failed); 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 {
Map<String, dynamic> body = {"thing_id": thingID, "alias": newAlias}; try {
int statusCode = await apiServices.updateDeviceAlias(body); Map<String, dynamic> body = {"thing_id": thingID, "alias": newAlias};
showSnackBarResponseByStatusCode( int statusCode = await apiServices.updateDeviceAlias(body);
context, showSnackBarResponseByStatusCode(
statusCode, context,
appLocalization(context).notification_update_device_success, statusCode,
appLocalization(context).notification_update_device_failed, appLocalization(context).notification_update_device_success,
); appLocalization(context).notification_update_device_failed,
);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
} }
Future<List<Device>> getOwnerDevices() async { Future<List<Device>> getOwnerDevices(BuildContext context) async {
List<Device> allDevices = []; List<Device> allDevices = [];
String body = await apiServices.getOwnerDevices(); try {
if (body != "") { final originalDevices = await apiServices.getOwnerDevices();
final data = jsonDecode(body); allDevices = originalDevices.where((device) => device.visibility == "PUBLIC").toList();
List<dynamic> items = data['items'];
allDevices = Device.fromJsonDynamicList(items); return allDevices;
} catch (e) {
if (context.mounted) {
showErrorTopSnackBarCustom(context, e.toString());
}
return allDevices;
} }
return allDevices;
} }
Future<void> addDeviceToGroup( Future<void> addDeviceToGroup(
@@ -107,12 +145,18 @@ class DetailGroupBloc extends BlocBase {
Map<String, dynamic> body = { Map<String, dynamic> body = {
"thing_id": thingID, "thing_id": thingID,
}; };
int statusCode = await apiServices.addDeviceToGroup(groupID, body); try {
showSnackBarResponseByStatusCode( int statusCode = await apiServices.addDeviceToGroup(groupID, body);
context, showSnackBarResponseByStatusCode(
statusCode, context,
appLocalization(context).notification_add_device_success, statusCode,
appLocalization(context).notification_add_device_failed, appLocalization(context).notification_add_device_success,
); appLocalization(context).notification_add_device_failed,
);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
} }
} }

View File

@@ -10,6 +10,7 @@ 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';
import '../product/shared/shared_snack_bar.dart';
import '../product/utils/response_status_utils.dart'; import '../product/utils/response_status_utils.dart';
import '../feature/inter_family/groups/groups_model.dart'; import '../feature/inter_family/groups/groups_model.dart';
@@ -38,46 +39,27 @@ class InterFamilyBloc extends BlocBase {
void getAllGroup(String role) async { void getAllGroup(String role) async {
List<Group> groups = []; List<Group> groups = [];
sinkCurrentGroups.add(groups); sinkCurrentGroups.add(groups);
try {
groups = await apiServices.getAllGroups();
groups = sortGroupByName(groups);
final body = await apiServices.getAllGroups(); List<Group> currentGroups = groups.where(
(group) {
bool isPublic = group.visibility == "PUBLIC";
if (role == ApplicationConstants.OWNER_GROUP) {
return group.isOwner == true && isPublic;
}
if (role == ApplicationConstants.PARTICIPANT_GROUP) {
return group.isOwner == null && isPublic;
}
return false;
},
).toList();
if (body.isNotEmpty) { sinkCurrentGroups.add(currentGroups);
try { } catch (e) {
final data = jsonDecode(body); // Xử lý lỗi khi jsonDecode thất bại
log("Error decoding JSON: $e");
if (data is Map && data.containsKey("items") && data["items"] is List) {
List<dynamic> items = data["items"];
groups = Group.fromJsonDynamicList(items);
groups = sortGroupByName(groups);
List<Group> currentGroups = groups.where(
(group) {
bool isPublic = group.visibility == "PUBLIC";
if (role == ApplicationConstants.OWNER_GROUP) {
return group.isOwner == true && isPublic;
}
if (role == ApplicationConstants.PARTICIPANT_GROUP) {
return group.isOwner == null && isPublic;
}
return false;
},
).toList();
sinkCurrentGroups.add(currentGroups);
} else {
log("No items found in API response or empty JSON object");
sinkCurrentGroups.add([]);
}
} catch (e) {
// Xử lý lỗi khi jsonDecode thất bại
log("Error decoding JSON: $e");
sinkCurrentGroups.add([]);
}
} else {
log("Get groups from API failed: Empty response");
sinkCurrentGroups.add([]); sinkCurrentGroups.add([]);
} }
log("Inter Family Role: $role"); log("Inter Family Role: $role");
@@ -85,46 +67,70 @@ class InterFamilyBloc extends BlocBase {
Future<void> createGroup( Future<void> createGroup(
BuildContext context, String name, String description) async { BuildContext context, String name, String description) async {
APIServices apiServices = APIServices(); try {
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);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
} }
Future<void> changeGroupInfomation(BuildContext context, String groupID, Future<void> changeGroupInformation(BuildContext context, String groupID,
String name, String description) async { String name, String description) async {
Map<String, dynamic> body = {"name": name, "description": description}; try {
int statusCode = await apiServices.updateGroup(body, groupID); Map<String, dynamic> body = {"name": name, "description": description};
showSnackBarResponseByStatusCode( int statusCode = await apiServices.updateGroup(body, groupID);
context, showSnackBarResponseByStatusCode(
statusCode, context,
appLocalization(context).notification_update_group_success, statusCode,
appLocalization(context).notification_update_group_failed); appLocalization(context).notification_update_group_success,
appLocalization(context).notification_update_group_failed);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
} }
Future<void> joinGroup(BuildContext context, String groupID) async { Future<void> joinGroup(BuildContext context, String groupID) async {
Map<String, dynamic> body = { try {
"group_id": groupID, Map<String, dynamic> body = {
}; "group_id": groupID,
int statusCode = await apiServices.joinGroup(groupID, body); };
showSnackBarResponseByStatusCode( int statusCode = await apiServices.joinGroup(groupID, body);
context, showSnackBarResponseByStatusCode(
statusCode, context,
appLocalization(context).notification_join_request_group_success, statusCode,
appLocalization(context).notification_join_request_group_failed); appLocalization(context).notification_join_request_group_success,
appLocalization(context).notification_join_request_group_failed);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
} }
Future<void> deleteGroup(BuildContext context, String groupID) async { Future<void> deleteGroup(BuildContext context, String groupID) async {
int statusCode = await apiServices.deleteGroup(groupID); try {
showSnackBarResponseByStatusCode( int statusCode = await apiServices.deleteGroup(groupID);
context, showSnackBarResponseByStatusCode(
statusCode, context,
appLocalization(context).notification_delete_group_success, statusCode,
appLocalization(context).notification_delete_group_failed); appLocalization(context).notification_delete_group_success,
appLocalization(context).notification_delete_group_failed);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
} }
List<Group> sortGroupByName(List<Group> groups) { List<Group> sortGroupByName(List<Group> groups) {

View File

@@ -12,6 +12,7 @@ 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';
import '../feature/settings/profile/profile_model.dart'; import '../feature/settings/profile/profile_model.dart';
import '../product/shared/shared_snack_bar.dart';
class MainBloc extends BlocBase { class MainBloc extends BlocBase {
APIServices apiServices = APIServices(); APIServices apiServices = APIServices();
@@ -42,10 +43,16 @@ class MainBloc extends BlocBase {
@override @override
void dispose() {} void dispose() {}
void getUserProfile() async { void getUserProfile(BuildContext context) async {
String data = await apiServices.getUserDetail(); try {
User user = User.fromJson(jsonDecode(data)); User user = await apiServices.getUserDetail();
sinkUserProfile.add(user); sinkUserProfile.add(user);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
} }
getFCMTokenAndPresentations() async { getFCMTokenAndPresentations() async {

View File

@@ -1,10 +1,15 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:sfm_app/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';
import '../product/shared/shared_snack_bar.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;
@@ -18,6 +23,17 @@ class SettingsBloc extends BlocBase {
isChangeProfileInfomation.stream; isChangeProfileInfomation.stream;
void getUserProfile(BuildContext context) async {
try {
User user = await apiServices.getUserDetail();
sinkUserProfile.add(user);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
}
@override @override
void dispose() { void dispose() {

View File

@@ -1,11 +1,14 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sfm_app/product/shared/shared_snack_bar.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/bell_bloc.dart'; import '../../bloc/bell_bloc.dart';
import '../../product/base/bloc/base_bloc.dart'; import '../../product/base/bloc/base_bloc.dart';
import '../../product/services/api_services.dart'; import '../../product/services/api_services.dart';
import '../../product/shared/shared_component_loading_animation.dart';
import '../../product/shared/shared_loading_animation.dart';
import 'bell_model.dart'; import 'bell_model.dart';
class BellScreen extends StatefulWidget { class BellScreen extends StatefulWidget {
@@ -56,11 +59,7 @@ class _BellScreenState extends State<BellScreen> {
initialData: items, initialData: items,
builder: (context, bellSnapshot) { builder: (context, bellSnapshot) {
return check return check
? Center( ? const SharedLoadingAnimation()
child: CircularProgressIndicator(
value: context.highValue,
),
)
: bellSnapshot.data?.isEmpty ?? true : bellSnapshot.data?.isEmpty ?? true
? Center( ? Center(
child: Text( child: Text(
@@ -78,16 +77,7 @@ class _BellScreenState extends State<BellScreen> {
if (index < bellSnapshot.data!.length) { if (index < bellSnapshot.data!.length) {
return GestureDetector( return GestureDetector(
onTap: () async { onTap: () async {
List<String> read = []; readNotification(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: [
@@ -143,7 +133,7 @@ class _BellScreenState extends State<BellScreen> {
builder: (context, hasMoreSnapshot) { builder: (context, hasMoreSnapshot) {
return Center( return Center(
child: hasMoreSnapshot.data ?? hasMore child: hasMoreSnapshot.data ?? hasMore
? const CircularProgressIndicator() ? const SharedComponentLoadingAnimation()
: Text( : Text(
appLocalization(context) appLocalization(context)
.bell_read_all, .bell_read_all,
@@ -173,20 +163,40 @@ class _BellScreenState extends State<BellScreen> {
getBellNotification(offset); getBellNotification(offset);
} }
Future<void> getBellNotification(int offset) async { void readNotification(String id) async{
bell = await apiServices.getBellNotifications( try {
offset.toString(), (offset + 20).toString()); List<String> read = [];
read.add(id);
if (bell.items!.isEmpty) { await apiServices
hasMore = false; .updateStatusOfNotification(read);
bellBloc.sinkHasMore.add(hasMore); read.clear();
} else { } catch (e) {
for (var item in bell.items!) { if (mounted){
items.add(item); showErrorTopSnackBarCustom(
context, e.toString());
} }
} }
bellBloc.bellItems.add(items); refresh();
check = false; }
Future<void> getBellNotification(int offset) async {
try {
bell = await apiServices.getBellNotifications(
offset.toString(), (offset + 20).toString());
if (bell.items!.isEmpty) {
hasMore = false;
bellBloc.sinkHasMore.add(hasMore);
} else {
for (var item in bell.items!) {
items.add(item);
}
}
bellBloc.bellItems.add(items);
check = false;
} catch (e) {
if (!mounted) return;
showErrorTopSnackBarCustom(context, e.toString());
}
} }
String timeAgo(BuildContext context, DateTime dateTime) { String timeAgo(BuildContext context, DateTime dateTime) {

View File

@@ -2,6 +2,8 @@
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';
@@ -42,7 +44,7 @@ class _DeviceLogsScreenState extends State<DeviceLogsScreen> {
() { () {
if (controller.position.maxScrollExtent == controller.offset) { if (controller.position.maxScrollExtent == controller.offset) {
offset += 30; offset += 30;
deviceLogsBloc.getDeviceLogByThingID( deviceLogsBloc.getDeviceLogByThingID(context,
offset, thingID, dateTime!, sensors); offset, thingID, dateTime!, sensors);
} }
}, },
@@ -64,10 +66,8 @@ class _DeviceLogsScreenState extends State<DeviceLogsScreen> {
stream: deviceLogsBloc.streamAllDevices, stream: deviceLogsBloc.streamAllDevices,
builder: (context, allDevicesSnapshot) { builder: (context, allDevicesSnapshot) {
if (allDevicesSnapshot.data == null) { if (allDevicesSnapshot.data == null) {
deviceLogsBloc.getAllDevices(); deviceLogsBloc.getAllDevices(context);
return const Center( return const SharedLoadingAnimation();
child: CircularProgressIndicator(),
);
} else { } else {
return StreamBuilder<List<SensorLogs>>( return StreamBuilder<List<SensorLogs>>(
stream: deviceLogsBloc.streamSensors, stream: deviceLogsBloc.streamSensors,
@@ -188,6 +188,7 @@ class _DeviceLogsScreenState extends State<DeviceLogsScreen> {
); );
} else { } else {
deviceLogsBloc.getDeviceLogByThingID( deviceLogsBloc.getDeviceLogByThingID(
context,
offset, offset,
thingID, thingID,
dateTime!, dateTime!,
@@ -246,7 +247,7 @@ class _DeviceLogsScreenState extends State<DeviceLogsScreen> {
return Center( return Center(
child: hasMoreSnapshot.data ?? child: hasMoreSnapshot.data ??
hasMore hasMore
? const CircularProgressIndicator() ? const SharedComponentLoadingAnimation()
: Text( : Text(
appLocalization(context) appLocalization(context)
.main_no_data), .main_no_data),
@@ -310,7 +311,7 @@ class _DeviceLogsScreenState extends State<DeviceLogsScreen> {
deviceLogsBloc.sensors.add(sensors); deviceLogsBloc.sensors.add(sensors);
hasMore = true; hasMore = true;
deviceLogsBloc.sinkHasMore.add(hasMore); deviceLogsBloc.sinkHasMore.add(hasMore);
deviceLogsBloc.getDeviceLogByThingID(offset, thingID, dateTime!, sensors); deviceLogsBloc.getDeviceLogByThingID(context,offset, thingID, dateTime!, sensors);
} }
Widget leadingList(SensorLogs sensor) { Widget leadingList(SensorLogs sensor) {

View File

@@ -95,23 +95,36 @@ void addDevices(
APIServices apiServices = APIServices(); APIServices apiServices = APIServices();
Map<String, dynamic> body = {}; Map<String, dynamic> body = {};
if (role == RoleEnums.ADMIN.name) { if (role == RoleEnums.ADMIN.name) {
body = {"ext_id": extID, "name": deviceName}; try {
int statusCode = await apiServices.createDeviceByAdmin(body); body = {"ext_id": extID, "name": deviceName};
showSnackBarResponseByStatusCode( int statusCode = await apiServices.createDeviceByAdmin(body);
context, showSnackBarResponseByStatusCode(
statusCode, context,
appLocalization(context).notification_create_device_success, statusCode,
appLocalization(context).notification_create_device_failed); appLocalization(context).notification_create_device_success,
deviceManagerBloc.getDeviceByState(-2); appLocalization(context).notification_create_device_failed);
deviceManagerBloc.getDeviceByState(context,-2);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
} else { } else {
body = {"ext_id": extID}; try {
int statusCode = await apiServices.registerDevice(body); body = {"ext_id": extID};
showSnackBarResponseByStatusCode( int statusCode = await apiServices.registerDevice(body);
context, showSnackBarResponseByStatusCode(
statusCode, context,
appLocalization(context).notification_add_device_success, statusCode,
appLocalization(context).notification_device_not_exist); appLocalization(context).notification_add_device_success,
deviceManagerBloc.getDeviceByState(-2); appLocalization(context).notification_device_not_exist);
deviceManagerBloc.getDeviceByState(context,-2);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
} }
} }

View File

@@ -5,6 +5,7 @@ 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';
import '../../product/services/language_services.dart'; import '../../product/services/language_services.dart';
import '../../product/shared/shared_snack_bar.dart';
import '../../product/utils/response_status_utils.dart'; import '../../product/utils/response_status_utils.dart';
handleDeleteDevice(BuildContext context, DevicesManagerBloc devicesManagerBloc, handleDeleteDevice(BuildContext context, DevicesManagerBloc devicesManagerBloc,
@@ -53,14 +54,21 @@ deleteOrUnregisterDevice(BuildContext context, DevicesManagerBloc devicesBloc,
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 {
int statusCode = await apiServices.deleteDeviceByAdmin(extID); try {
showSnackBarResponseByStatusCode( int statusCode = await apiServices.deleteDeviceByAdmin(extID);
context, showSnackBarResponseByStatusCode(
statusCode, context,
appLocalization(context).notification_delete_device_success, statusCode,
appLocalization(context).notification_delete_device_failed); appLocalization(context).notification_delete_device_success,
devicesBloc.getDeviceByState(-2); appLocalization(context).notification_delete_device_failed);
devicesBloc.getDeviceByState(context,-2);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
} }
} }

View File

@@ -2,11 +2,13 @@ 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:sfm_app/product/shared/shared_component_loading_animation.dart';
import 'package:simple_ripple_animation/simple_ripple_animation.dart'; import 'package:simple_ripple_animation/simple_ripple_animation.dart';
import '../../../product/constant/image/image_constants.dart'; import '../../../product/constant/image/image_constants.dart';
import '../../../product/shared/shared_line_chart.dart'; import '../../../product/shared/shared_line_chart.dart';
import '../../../product/shared/shared_curve.dart'; import '../../../product/shared/shared_curve.dart';
import '../../../product/shared/shared_loading_animation.dart';
import '../../device_log/device_logs_model.dart'; import '../../device_log/device_logs_model.dart';
import '../device_model.dart'; import '../device_model.dart';
import '../../../product/base/bloc/base_bloc.dart'; import '../../../product/base/bloc/base_bloc.dart';
@@ -92,8 +94,11 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
widget.thingID, widget.thingID,
controller, controller,
); );
return const Center( return Scaffold(
child: CircularProgressIndicator(), appBar: AppBar(),
body: Center(
child: Text(appLocalization(context).no_data_message),
),
); );
} else { } else {
return StreamBuilder<Map<String, dynamic>>( return StreamBuilder<Map<String, dynamic>>(
@@ -125,28 +130,6 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
fit: BoxFit.fill, fit: BoxFit.fill,
), ),
), ),
// Center(
// child: Container(
// height: 50,
// width: 400,
// // color: Colors.blueAccent,
// alignment: Alignment.centerRight,
// margin: const EdgeInsets.fromLTRB(0, 0, 0, 50),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// const SizedBox(),
// Text(
// deviceSnapshot.data?.name ?? "",
// style: const TextStyle(
// fontSize: 25,
// fontWeight: FontWeight.w600,
// ),
// ),
// ],
// ),
// ),
// ),
], ],
), ),
), ),
@@ -541,12 +524,10 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
builder: (context, sensorTempsSnapshot) { builder: (context, sensorTempsSnapshot) {
if (sensorTempsSnapshot.data == null) { if (sensorTempsSnapshot.data == null) {
detailDeviceBloc detailDeviceBloc
.getNearerSensorValue(widget.thingID); .getNearerSensorValue(context,widget.thingID);
return const AspectRatio( return const AspectRatio(
aspectRatio: 3, aspectRatio: 3,
child: Center( child: SharedComponentLoadingAnimation(),
child: CircularProgressIndicator(),
),
); );
} else if (sensorTempsSnapshot.data!.isEmpty) { } else if (sensorTempsSnapshot.data!.isEmpty) {
return Center( return Center(
@@ -657,12 +638,7 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
), ),
); );
} else { } else {
return Scaffold( return const SharedLoadingAnimation();
appBar: AppBar(),
body: const Center(
child: CircularProgressIndicator(),
),
);
} }
}, },
); );

View File

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

View File

@@ -64,7 +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(latitude, longitude, deviceUpdateBloc); getDataFromApi(context,latitude, longitude, deviceUpdateBloc);
latitudeController.text = latitudeController.text =
mapDialogLatitudeController.text; mapDialogLatitudeController.text;
longitudeController.text = longitudeController.text =
@@ -184,7 +184,7 @@ addMarker(
updateCameraPosition(position, 14, mapController); updateCameraPosition(position, 14, mapController);
} }
void getDataFromApi(String latitude, String longitude, void getDataFromApi(BuildContext context,String latitude, String longitude,
DeviceUpdateBloc deviceUpdateBloc) async { DeviceUpdateBloc deviceUpdateBloc) async {
String path = String path =
"maps/api/geocode/json?latlng=$latitude,$longitude&language=vi&result_type=political&key=${ApplicationConstants.MAP_KEY}"; "maps/api/geocode/json?latlng=$latitude,$longitude&language=vi&result_type=political&key=${ApplicationConstants.MAP_KEY}";
@@ -215,7 +215,7 @@ void getDataFromApi(String latitude, String longitude,
log("$key: $value"); log("$key: $value");
}); });
await _processLocations(locations, deviceUpdateBloc); await _processLocations(context,locations, deviceUpdateBloc);
} }
Map<String, String> _extractLocationComponents( Map<String, String> _extractLocationComponents(
@@ -241,31 +241,31 @@ Map<String, String> _extractLocationComponents(
return locations; return locations;
} }
Future<void> _processLocations( Future<void> _processLocations(BuildContext context,
Map<String, String> locations, DeviceUpdateBloc deviceUpdateBloc) async { Map<String, String> locations, DeviceUpdateBloc deviceUpdateBloc) async {
String provinceNameFromAPI = locations['provincekey'] ?? ""; String provinceNameFromAPI = locations['provincekey'] ?? "";
String districtNameFromAPI = locations['districtkey'] ?? ""; String districtNameFromAPI = locations['districtkey'] ?? "";
String wardNameFromAPI = locations['wardkey'] ?? ""; String wardNameFromAPI = locations['wardkey'] ?? "";
final province = final province =
await deviceUpdateBloc.getProvinceByName(provinceNameFromAPI); await deviceUpdateBloc.getProvinceByName(context,provinceNameFromAPI);
if (province.name != "null") { if (province.name != "null") {
log("Province: ${province.fullName}, ProvinceCode: ${province.code}"); log("Province: ${province.fullName}, ProvinceCode: ${province.code}");
deviceUpdateBloc.sinkProvinceData deviceUpdateBloc.sinkProvinceData
.add({"code": province.code!, "name": province.fullName!}); .add({"code": province.code!, "name": province.fullName!});
deviceUpdateBloc.getAllProvinces(); deviceUpdateBloc.getAllProvinces(context);
final district = await deviceUpdateBloc.getDistrictByName( final district = await deviceUpdateBloc.getDistrictByName(context,
districtNameFromAPI, province.code!); districtNameFromAPI, province.code!);
log("Districtname: ${district.fullName}, districtCode: ${district.code}"); log("Districtname: ${district.fullName}, districtCode: ${district.code}");
deviceUpdateBloc.getAllDistricts(province.code!); deviceUpdateBloc.getAllDistricts(context,province.code!);
if (district.name != "null") { if (district.name != "null") {
deviceUpdateBloc.sinkDistrictData deviceUpdateBloc.sinkDistrictData
.add({"code": district.code!, "name": district.fullName!}); .add({"code": district.code!, "name": district.fullName!});
final ward = final ward =
await deviceUpdateBloc.getWardByName(wardNameFromAPI, district.code!); await deviceUpdateBloc.getWardByName(context,wardNameFromAPI, district.code!);
log("Wardname: ${ward.fullName}, WardCode: ${ward.code}"); log("Wardname: ${ward.fullName}, WardCode: ${ward.code}");
deviceUpdateBloc.getAllWards(district.code!); deviceUpdateBloc.getAllWards(context,district.code!);
if (ward.name != "null") { if (ward.name != "null") {
log("Xac dinh duoc het thong tin tu toa do"); log("Xac dinh duoc het thong tin tu toa do");
deviceUpdateBloc.sinkWardData deviceUpdateBloc.sinkWardData

View File

@@ -4,6 +4,8 @@ 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';
@@ -41,7 +43,7 @@ class _DevicesManagerScreenState extends State<DevicesManagerScreen> {
const duration = Duration(seconds: 10); const duration = Duration(seconds: 10);
getAllDevicesTimer = Timer.periodic( getAllDevicesTimer = Timer.periodic(
duration, duration,
(Timer t) => devicesManagerBloc.getDeviceByState(tagIndex), (Timer t) => devicesManagerBloc.getDeviceByState(context,tagIndex),
); );
} }
@@ -68,8 +70,8 @@ class _DevicesManagerScreenState extends State<DevicesManagerScreen> {
builder: (context, allDeviceSnapshot) { builder: (context, allDeviceSnapshot) {
if(allDeviceSnapshot.data == null){ if(allDeviceSnapshot.data == null){
devicesManagerBloc devicesManagerBloc
.getDeviceByState(tagSnapshot.data?[0] ?? -2); .getDeviceByState(context,tagSnapshot.data?[0] ?? -2);
return const Center(child: CircularProgressIndicator()); return const SharedLoadingAnimation();
} }
if (allDeviceSnapshot.data!.isEmpty) { if (allDeviceSnapshot.data!.isEmpty) {
return Center( return Center(
@@ -219,10 +221,9 @@ class _DevicesManagerScreenState extends State<DevicesManagerScreen> {
stream: devicesManagerBloc.streamDeviceByState, stream: devicesManagerBloc.streamDeviceByState,
builder: (context, devicesByStateSnapshot) { builder: (context, devicesByStateSnapshot) {
if (devicesByStateSnapshot.data == null) { if (devicesByStateSnapshot.data == null) {
devicesManagerBloc.getDeviceByState( devicesManagerBloc.getDeviceByState(context,
tagSnapshot.data?[0] ?? -2); tagSnapshot.data?[0] ?? -2);
return const Center( return const SharedComponentLoadingAnimation();
child: CircularProgressIndicator());
} else { } else {
return SharedPieChart( return SharedPieChart(
deviceByState: deviceByState:
@@ -392,7 +393,7 @@ class TagState extends StatelessWidget {
), ),
GestureDetector( GestureDetector(
onTap: () { onTap: () {
devicesManagerBloc.getDeviceByState(-2); devicesManagerBloc.getDeviceByState(context,-2);
}, },
child: const Icon( child: const Icon(
Icons.close, Icons.close,

View File

@@ -3,6 +3,9 @@ import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../product/shared/shared_component_loading_animation.dart';
import '../../product/shared/shared_loading_animation.dart';
import '../../product/shared/shared_snack_bar.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 '../../product/utils/device_utils.dart';
@@ -206,7 +209,7 @@ class _HomeScreenState extends State<HomeScreen> {
stream: homeBloc.streamAllDevicesAliasJoinedMap, stream: homeBloc.streamAllDevicesAliasJoinedMap,
builder: (context, allDevicesAliasJoinedMapSnapshot) { builder: (context, allDevicesAliasJoinedMapSnapshot) {
if (hasJoinedDeviceSnapshot.data == null) { if (hasJoinedDeviceSnapshot.data == null) {
return const CircularProgressIndicator(); return const SharedComponentLoadingAnimation();
} else { } else {
final data = allDevicesAliasMapSnapshot.data!; final data = allDevicesAliasMapSnapshot.data!;
final dataJoined = final dataJoined =
@@ -219,54 +222,71 @@ class _HomeScreenState extends State<HomeScreen> {
} }
return OverviewCard( return OverviewCard(
isOwner: true, isOwner: true,
total: data['all']?.length ?? 0, total: data['all']?.length ?? 0,
active: data['online']?.length ?? 0, active: data['online']?.length ?? 0,
inactive: data['offline']?.length ?? 0, inactive: data['offline']?.length ?? 0,
warning: data['warn']?.length ?? 0, warning: data['warn']?.length ?? 0,
unused: data['not-use']?.length ?? 0); unused: data['not-use']?.length ?? 0,
showUnused: false,
);
} else { } else {
return DefaultTabController( return DefaultTabController(
length: 2, length: 2,
child: Column( child: Column(
// mainAxisSize: MainAxisSize.min,
// crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
TabBar( TabBar(
tabs: [ tabs: [
Tab(text: appLocalization(context).over_view_owner_devices), Tab(
Tab(text: appLocalization(context).over_view_joined_devices), text: appLocalization(context)
.over_view_owner_devices),
Tab(
text: appLocalization(context)
.over_view_joined_devices),
], ],
labelColor: Colors.blue, labelColor: Colors.blue,
unselectedLabelColor: Colors.grey, unselectedLabelColor: Colors.grey,
indicatorColor: Colors.blue, indicatorColor: Colors.blue,
), ),
SizedBox( ConstrainedBox(
height: context.dynamicHeight(0.6), constraints: BoxConstraints(
maxHeight: context.dynamicHeight(0.5),
minHeight: context.dynamicHeight(0.3),
),
child: TabBarView( child: TabBarView(
children: [ children: [
OverviewCard( OverviewCard(
isOwner: true, isOwner: true,
total: data['all']?.length ?? 0, total: data['all']?.length ?? 0,
active: data['online']?.length ?? 0, active: data['online']?.length ?? 0,
inactive: inactive:
data['offline']?.length ?? 0, data['offline']?.length ?? 0,
warning: data['warn']?.length ?? 0, warning: data['warn']?.length ?? 0,
unused: unused:
data['not-use']?.length ?? 0), data['not-use']?.length ?? 0,
showUnused: false,
),
OverviewCard( OverviewCard(
isOwner: false, isOwner: false,
total: total:
dataJoined['all']?.length ?? 0, dataJoined['all']?.length ?? 0,
active: active:
dataJoined['online']?.length ?? dataJoined['online']?.length ??
0, 0,
inactive: inactive:
dataJoined['offline']?.length ?? dataJoined['offline']?.length ??
0, 0,
warning: warning:
dataJoined['warn']?.length ?? 0, dataJoined['warn']?.length ?? 0,
unused: unused:
dataJoined['not-use']?.length ?? dataJoined['not-use']?.length ??
0,), 0,
showUnused: false,
showActive: false,
showInactive: false,
),
], ],
), ),
), ),
@@ -289,19 +309,21 @@ class _HomeScreenState extends State<HomeScreen> {
} }
void getOwnerAndJoinedDevices() async { void getOwnerAndJoinedDevices() async {
String response = await apiServices.getDashBoardDevices(); try {
final data = jsonDecode(response); devices = await apiServices.getDashBoardDevices();
List<dynamic> result = data["items"]; List<DeviceWithAlias> publicDevices = [];
devices = DeviceWithAlias.fromJsonDynamicList(result); for (var device in devices) {
List<DeviceWithAlias> publicDevices = []; if (device.visibility == "PUBLIC") {
for (var device in devices) { publicDevices.add(device);
if (device.visibility == "PUBLIC") { }
publicDevices.add(device);
} }
getOwnerDeviceState(publicDevices);
checkSettingDevice(publicDevices);
getDeviceStatusAliasMap(publicDevices);
} catch (e) {
if (!mounted) return;
showErrorTopSnackBarCustom(context, e.toString());
} }
getOwnerDeviceState(publicDevices);
checkSettingDevice(publicDevices);
getDeviceStatusAliasMap(publicDevices);
} }
void getOwnerDeviceState(List<DeviceWithAlias> allDevices) async { void getOwnerDeviceState(List<DeviceWithAlias> allDevices) async {
@@ -391,33 +413,36 @@ class _HomeScreenState extends State<HomeScreen> {
} }
void checkSettingDevice(List<DeviceWithAlias> devices) async { void checkSettingDevice(List<DeviceWithAlias> devices) async {
if (isFunctionCall) { try {
log("Ham check setting da duoc goi"); if (isFunctionCall) {
} else { log("Ham check setting da duoc goi");
String? response = } else {
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 =
DeviceNotificationSettings.mapFromJson(result).values.toList(); await apiServices.getAllSettingsNotificationOfDevices();
// 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.setupDeviceNotification( try {
device.thingId!, device.name!); await apiServices.setupDeviceNotification(
device.thingId!, device.name!);
} catch (e) {
if (!mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
} 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;
} catch (e) {
if (!mounted) return;
showErrorTopSnackBarCustom(context, e.toString());
} }
isFunctionCall = true;
} }
} }

View File

@@ -17,7 +17,7 @@ Future<Widget> notificationCard(BuildContext context, String notiticationType,
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(); // DateTime time = DateTime.now();

View File

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

@@ -21,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!) {
@@ -209,22 +209,28 @@ Future<Widget> warningCard(BuildContext context, APIServices apiServices,
actions: [ actions: [
TextButton( TextButton(
onPressed: () async { onPressed: () async {
int statusCode = await apiServices try {
.confirmFakeFireByUser(device.thingId!); int statusCode = await apiServices
if (statusCode == 200) { .confirmFakeFireByUser(device.thingId!);
showNoIconTopSnackBar( if (statusCode == 200) {
context, showNoIconTopSnackBar(
appLocalization(context) context,
.notification_confirm_fake_fire_success, appLocalization(context)
Colors.green, .notification_confirm_fake_fire_success,
Colors.white); Colors.green,
} else { Colors.white);
showNoIconTopSnackBar( } else {
context, showNoIconTopSnackBar(
appLocalization(context) context,
.notification_confirm_fake_fire_failed, appLocalization(context)
Colors.red, .notification_confirm_fake_fire_failed,
Colors.red); Colors.red,
Colors.red);
}
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
} }
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },

View File

@@ -12,7 +12,7 @@ import 'group_detail_model.dart';
addDeviceDialog(BuildContext context, DetailGroupBloc detailGroupBloc, addDeviceDialog(BuildContext context, DetailGroupBloc detailGroupBloc,
String groupID, List<DeviceOfGroup> devices) async { String groupID, List<DeviceOfGroup> devices) async {
List<Device> ownerDevices = await detailGroupBloc.getOwnerDevices(); List<Device> ownerDevices = await detailGroupBloc.getOwnerDevices(context);
List<String> selectedItems = []; List<String> selectedItems = [];
List<String> selectedDevices = []; List<String> selectedDevices = [];
if (devices.isNotEmpty) { if (devices.isNotEmpty) {
@@ -131,7 +131,7 @@ addDeviceDialog(BuildContext context, DetailGroupBloc detailGroupBloc,
for (var device in selectedItems) { for (var device in selectedItems) {
await detailGroupBloc.addDeviceToGroup( await detailGroupBloc.addDeviceToGroup(
context, groupID, device); context, groupID, device);
await detailGroupBloc.getGroupDetail(groupID); await detailGroupBloc.getGroupDetail(context,groupID);
} }
Navigator.of(dialogContext).pop(); Navigator.of(dialogContext).pop();

View File

@@ -4,6 +4,8 @@ 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 '../../../product/shared/shared_snack_bar.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';
@@ -35,15 +37,13 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
const duration = Duration(seconds: 10); const duration = Duration(seconds: 10);
getGroupDetailTimer = Timer.periodic( getGroupDetailTimer = Timer.periodic(
duration, duration,
(Timer t) => detailGroupBloc.getGroupDetail(widget.group), (Timer t) => detailGroupBloc.getGroupDetail(context, widget.group),
); );
} }
@override @override
void dispose() { void dispose() {
getGroupDetailTimer?.cancel(); getGroupDetailTimer?.cancel();
// Thieeus hamf
super.dispose(); super.dispose();
} }
@@ -53,10 +53,8 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
stream: detailGroupBloc.streamDetailGroup, stream: detailGroupBloc.streamDetailGroup,
builder: (context, detailGroupSnapshot) { builder: (context, detailGroupSnapshot) {
if (detailGroupSnapshot.data?.id == null) { if (detailGroupSnapshot.data?.id == null) {
detailGroupBloc.getGroupDetail(widget.group); detailGroupBloc.getGroupDetail(context, widget.group);
return const Center( return const SharedLoadingAnimation();
child: CircularProgressIndicator(),
);
} else { } else {
return Scaffold( return Scaffold(
key: scaffoldKey, key: scaffoldKey,
@@ -142,8 +140,8 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
widget.group, widget.group,
user.id!, user.id!,
user.name!); user.name!);
detailGroupBloc detailGroupBloc.getGroupDetail(
.getGroupDetail(widget.group); context, widget.group);
}, },
icon: const Icon( icon: const Icon(
Icons.check, Icons.check,
@@ -160,8 +158,8 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
widget.group, widget.group,
user.id!, user.id!,
user.name!); user.name!);
await detailGroupBloc await detailGroupBloc.getGroupDetail(
.getGroupDetail(widget.group); context, widget.group);
}, },
icon: const Icon( icon: const Icon(
Icons.close, Icons.close,
@@ -207,8 +205,8 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
widget.group, widget.group,
user.id!, user.id!,
user.name!); user.name!);
await detailGroupBloc await detailGroupBloc.getGroupDetail(
.getGroupDetail(widget.group); context, widget.group);
}, },
value: 2, value: 2,
child: Text(appLocalization(context) child: Text(appLocalization(context)
@@ -241,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: (contex) => [ itemBuilder: (context) => [
PopupMenuItem( PopupMenuItem(
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
@@ -327,15 +325,21 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
Future.delayed(context.lowDuration).then( Future.delayed(context.lowDuration).then(
(value) => Navigator.pop(context), (value) => Navigator.pop(context),
); );
int statusCode = await apiServices try {
.deleteGroup(widget.group); int statusCode = await apiServices
showSnackBarResponseByStatusCode( .deleteGroup(widget.group);
context, showSnackBarResponseByStatusCode(
statusCode, context,
appLocalization(context) statusCode,
.notification_delete_group_success, appLocalization(context)
appLocalization(context) .notification_delete_group_success,
.notification_delete_group_failed); appLocalization(context)
.notification_delete_group_failed);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
}, },
child: Text( child: Text(
appLocalization(context) appLocalization(context)
@@ -463,7 +467,8 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
DeviceUtils.instance.checkStateDevice( DeviceUtils.instance.checkStateDevice(
context, devices[index].state!), context, devices[index].state!),
style: TextStyle( style: TextStyle(
color: DeviceUtils.instance.getTableRowColor(context, color: DeviceUtils.instance.getTableRowColor(
context,
devices[index].state!, devices[index].state!,
), ),
), ),
@@ -525,7 +530,7 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
String alias = aliasController.text; String alias = aliasController.text;
await detailGroupBloc.updateDeviceNameInGroup( await detailGroupBloc.updateDeviceNameInGroup(
context, device.thingId!, alias); context, device.thingId!, alias);
await detailGroupBloc.getGroupDetail(widget.group); await detailGroupBloc.getGroupDetail(context, widget.group);
}, },
child: Text(appLocalization(context).confirm_button_content)), child: Text(appLocalization(context).confirm_button_content)),
) )

View File

@@ -6,6 +6,8 @@ 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 '../../../product/shared/shared_component_loading_animation.dart';
import '../../../product/shared/shared_loading_animation.dart';
import 'groups_model.dart'; import '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';
@@ -54,64 +56,76 @@ class _GroupsScreenState extends State<GroupsScreen> {
return StreamBuilder<List<Group>>( return StreamBuilder<List<Group>>(
stream: interFamilyBloc.streamCurrentGroups, stream: interFamilyBloc.streamCurrentGroups,
builder: (context, groupsSnapshot) { builder: (context, groupsSnapshot) {
if(groupsSnapshot.data == null){ if (groupsSnapshot.data == null) {
interFamilyBloc.getAllGroup(widget.role); interFamilyBloc.getAllGroup(widget.role);
return const Center(child: CircularProgressIndicator(),); return const SharedLoadingAnimation();
}else if(groupsSnapshot.data!.isEmpty){ } else if (groupsSnapshot.data!.isEmpty) {
return Center(child: Text(appLocalization(context).dont_have_group),); return Center(
}else { child: Text(appLocalization(context).dont_have_group),
);
} else {
return Scaffold( return Scaffold(
body: groupsSnapshot.data?.isEmpty ?? true body: groupsSnapshot.data?.isEmpty ?? true
? const Center( ? const SharedComponentLoadingAnimation()
child: CircularProgressIndicator(),
)
: ListView.builder( : ListView.builder(
itemCount: groupsSnapshot.data!.length, itemCount: groupsSnapshot.data!.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return ListTile( return ListTile(
onTap: () { onTap: () {
context.pushNamed(AppRoutes.GROUP_DETAIL.name, context.pushNamed(AppRoutes.GROUP_DETAIL.name,
pathParameters: {"groupId": groupsSnapshot.data![index].id!}, pathParameters: {
extra: widget.role); "groupId": groupsSnapshot.data![index].id!
}, },
leading: IconConstants.instance.getMaterialIcon(Icons.diversity_2), extra: widget.role);
title: Text( },
groupsSnapshot.data![index].name ?? '', leading: IconConstants.instance
style: const TextStyle(fontWeight: FontWeight.bold), .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(),
);
},
), ),
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(),
);
},
),
); );
} }
}, },
@@ -121,15 +135,16 @@ class _GroupsScreenState extends State<GroupsScreen> {
} }
} }
PopupMenuItem _buildPopupMenuItem( PopupMenuItem _buildPopupMenuItem(Group group, BuildContext context,
Group group, BuildContext context, String title, IconData iconData, int position) { String title, IconData iconData, int position) {
return PopupMenuItem( return PopupMenuItem(
onTap: () { onTap: () {
if (title == appLocalization(context).share_group_title) { if (title == appLocalization(context).share_group_title) {
Future.delayed(context.lowDuration, () { 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, () { Future.delayed(context.lowDuration, () {
createOrJoinGroupDialog( createOrJoinGroupDialog(
context, context,

View File

@@ -131,7 +131,7 @@ createOrJoinGroupDialog(
appLocalization(context) appLocalization(context)
.change_group_infomation_content) { .change_group_infomation_content) {
try { try {
await interFamilyBloc.changeGroupInfomation( await interFamilyBloc.changeGroupInformation(
context, groupID, groupName, description); context, groupID, groupName, description);
interFamilyBloc.getAllGroup(role); interFamilyBloc.getAllGroup(role);
Navigator.of(dialogContext).pop(); Navigator.of(dialogContext).pop();

View File

@@ -10,6 +10,7 @@ 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/persistent_bottom_nav_bar.dart'; import 'package:persistent_bottom_nav_bar/persistent_bottom_nav_bar.dart';
import 'package:sfm_app/product/shared/shared_snack_bar.dart';
import 'package:sfm_app/product/utils/permission_handler.dart'; import 'package:sfm_app/product/utils/permission_handler.dart';
import '../../product/permission/notification_permission.dart'; import '../../product/permission/notification_permission.dart';
import '../../product/services/notification_services.dart'; import '../../product/services/notification_services.dart';
@@ -100,7 +101,7 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addObserver(this);
initialCheck(); initialCheck();
getBellNotification(); getBellNotification();
mainBloc.getUserProfile(); mainBloc.getUserProfile(context);
FirebaseMessaging.instance.onTokenRefresh.listen((newToken) { FirebaseMessaging.instance.onTokenRefresh.listen((newToken) {
log("New FCM Token: $newToken"); log("New FCM Token: $newToken");
@@ -412,8 +413,12 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
} }
Future<void> getBellNotification() async { Future<void> getBellNotification() async {
bell = await apiServices.getBellNotifications("0", "20"); try{
mainBloc.bellBloc.add(bell); bell = await apiServices.getBellNotifications("0", "20");
mainBloc.bellBloc.add(bell);
}catch(e){
showErrorTopSnackBarCustom(context, e.toString());
}
} }
bool checkStatus(List<BellItems> bells) { bool checkStatus(List<BellItems> bells) {

View File

@@ -17,6 +17,7 @@ import 'package:sfm_app/product/permission/location_permission.dart';
import 'package:sfm_app/product/services/api_services.dart'; import 'package:sfm_app/product/services/api_services.dart';
import 'package:sfm_app/product/utils/permission_handler.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/shared/shared_snack_bar.dart';
class MapScreen extends StatefulWidget { class MapScreen extends StatefulWidget {
const MapScreen({super.key}); const MapScreen({super.key});
@@ -73,11 +74,11 @@ class _MapScreenState extends State<MapScreen> with WidgetsBindingObserver {
@override @override
void dispose() { void dispose() {
super.dispose();
checkThemeTimer?.cancel(); checkThemeTimer?.cancel();
getMarker?.cancel(); getMarker?.cancel();
_controller = Completer(); _controller = Completer();
streamController.close(); streamController.close();
super.dispose();
} }
void onMapCreated(GoogleMapController controller) { void onMapCreated(GoogleMapController controller) {
@@ -273,18 +274,18 @@ class _MapScreenState extends State<MapScreen> with WidgetsBindingObserver {
} }
void getAllMarkers() async { void getAllMarkers() async {
String response = await apiServices.getOwnerDevices(); try {
if (response != "") { devices.clear();
final data = jsonDecode(response); final devicesList = await apiServices.getOwnerDevices();
List<dynamic> result = data['items']; for (var device in devicesList) {
if (result.isNotEmpty) { devices.add(device);
devices.clear(); }
final devicesList = Device.fromJsonDynamicList(result); } catch (e) {
for (var device in devicesList) { if (!mounted) return;
devices.add(device); showErrorTopSnackBarCustom(
} context, e.toString());
} else {}
} }
} }
// Future<bool> checkLocationPermission(context) async { // Future<bool> checkLocationPermission(context) async {

View File

@@ -79,7 +79,7 @@ onTapMarker(
destination, destination,
); );
String deviceLocations = await DeviceUtils.instance String deviceLocations = await DeviceUtils.instance
.getFullDeviceLocation(context, device.areaPath!); .getFullDeviceLocation(context, device.areaPath!,device.name);
String yourLocation = String yourLocation =
appLocalization(context).map_your_location; appLocalization(context).map_your_location;
showDirections( showDirections(
@@ -88,7 +88,6 @@ onTapMarker(
otherMarkers, otherMarkers,
mapBloc, mapBloc,
yourLocation, yourLocation,
deviceLocations, deviceLocations,
double.parse(device.settings!.latitude!), double.parse(device.settings!.latitude!),
double.parse(device.settings!.longitude!), double.parse(device.settings!.longitude!),

View File

@@ -111,14 +111,15 @@ class _DeviceNotificationSettingsScreenState
} }
void getNotificationSetting() async { void getNotificationSetting() async {
String? response = await apiServices.getAllSettingsNotificationOfDevices(); try {
final data = jsonDecode(response); deviceNotifications =
final result = data['data']; await apiServices.getAllSettingsNotificationOfDevices();
// log("Data ${DeviceNotificationSettings.mapFromJson(jsonDecode(data)).values.toList()}"); deviceNotificationSettingsBloc.sinkListNotifications
deviceNotifications = .add(deviceNotifications);
DeviceNotificationSettings.mapFromJson(result).values.toList(); } catch (e) {
deviceNotificationSettingsBloc.sinkListNotifications if (!mounted) return;
.add(deviceNotifications); showErrorTopSnackBarCustom(context, e.toString());
}
} }
Widget listNotificationSetting( Widget listNotificationSetting(
@@ -292,22 +293,29 @@ class _DeviceNotificationSettingsScreenState
void updateDeviceNotification(String thingID, Map<String, int> notifiSettings, void updateDeviceNotification(String thingID, Map<String, int> notifiSettings,
bool isDataChange) async { bool isDataChange) async {
int statusCode = await apiServices.updateDeviceNotificationSettings( try {
thingID, notifiSettings); int statusCode = await apiServices.updateDeviceNotificationSettings(
if (statusCode == 200) { thingID, notifiSettings);
showNoIconTopSnackBar( if (statusCode == 200) {
context, showNoIconTopSnackBar(
appLocalization(context).notification_update_device_settings_success, context,
Colors.green, appLocalization(context).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);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
} }
isDataChange = false;
deviceNotificationSettingsBloc.sinkIsDataChange.add(isDataChange);
} }
} }

View File

@@ -38,35 +38,42 @@ changeUserInfomation(
? IconButton( ? IconButton(
onPressed: () async { onPressed: () async {
if (formKey.currentState!.validate()) { if (formKey.currentState!.validate()) {
formKey.currentState!.save(); try {
String latitude = user.latitude ?? ""; formKey.currentState!.save();
String longitude = user.longitude ?? ""; String latitude = user.latitude ?? "";
Map<String, dynamic> body = { String longitude = user.longitude ?? "";
"name": username, Map<String, dynamic> body = {
"email": email, "name": username,
"phone": tel, "email": email,
"address": address, "phone": tel,
"latitude": latitude, "address": address,
"longitude": longitude "latitude": latitude,
}; "longitude": longitude
int statusCode = };
await apiServices.updateUserProfile(body); int statusCode =
if (statusCode == 200) { await apiServices.updateUserProfile(body);
showNoIconTopSnackBar( if (statusCode == 200) {
modalBottomSheetContext, showNoIconTopSnackBar(
appLocalization(context) modalBottomSheetContext,
.notification_update_profile_success, appLocalization(context)
Colors.green, .notification_update_profile_success,
Colors.white); Colors.green,
} else { Colors.white);
showNoIconTopSnackBar( } else {
modalBottomSheetContext, showNoIconTopSnackBar(
appLocalization(context) modalBottomSheetContext,
.notification_update_profile_failed, appLocalization(context)
Colors.redAccent, .notification_update_profile_failed,
Colors.white); Colors.redAccent,
Colors.white);
}
settingsBloc.getUserProfile(context);
Navigator.pop(modalBottomSheetContext);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
} }
Navigator.pop(modalBottomSheetContext);
} }
}, },
icon: icon:
@@ -205,35 +212,42 @@ changeUserInfomation(
child: TextButton( child: TextButton(
onPressed: () async { onPressed: () async {
if (formKey.currentState!.validate()) { if (formKey.currentState!.validate()) {
formKey.currentState!.save(); try {
String latitude = user.latitude ?? ""; formKey.currentState!.save();
String longitude = user.longitude ?? ""; String latitude = user.latitude ?? "";
Map<String, dynamic> body = { String longitude = user.longitude ?? "";
"name": username, Map<String, dynamic> body = {
"email": email, "name": username,
"phone": tel, "email": email,
"address": address, "phone": tel,
"latitude": latitude, "address": address,
"longitude": longitude "latitude": latitude,
}; "longitude": longitude
int statusCode = await apiServices };
.updateUserProfile(body); int statusCode =
if (statusCode == 200) { await apiServices.updateUserProfile(body);
showNoIconTopSnackBar( if (statusCode == 200) {
modalBottomSheetContext, showNoIconTopSnackBar(
appLocalization(context) modalBottomSheetContext,
.notification_update_profile_success, appLocalization(context)
Colors.green, .notification_update_profile_success,
Colors.white); Colors.green,
} else { Colors.white);
showNoIconTopSnackBar( } else {
modalBottomSheetContext, showNoIconTopSnackBar(
appLocalization(context) modalBottomSheetContext,
.notification_update_profile_failed, appLocalization(context)
Colors.redAccent, .notification_update_profile_failed,
Colors.white); Colors.redAccent,
Colors.white);
}
settingsBloc.getUserProfile(context);
Navigator.pop(modalBottomSheetContext);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
} }
Navigator.pop(modalBottomSheetContext);
} }
}, },
style: const ButtonStyle( style: const ButtonStyle(
@@ -282,30 +296,36 @@ changeUserPassword(BuildContext context, SettingsBloc settingsBloc) {
isChangeSnapshot.data ?? isChange isChangeSnapshot.data ?? isChange
? IconButton( ? IconButton(
onPressed: () async { onPressed: () async {
if (formKey.currentState!.validate()) { try {
formKey.currentState!.save(); if (formKey.currentState!.validate()) {
Map<String, dynamic> body = { formKey.currentState!.save();
"password_old": oldPass, Map<String, dynamic> body = {
"password_new": newPass, "password_old": oldPass,
}; "password_new": newPass,
int statusCode = };
await apiServices.updateUserPassword(body); int statusCode =
if (statusCode == 200) { await apiServices.updateUserPassword(body);
showNoIconTopSnackBar( if (statusCode == 200) {
modalBottomSheetContext, showNoIconTopSnackBar(
appLocalization(context) modalBottomSheetContext,
.notification_update_password_success, appLocalization(context)
Colors.green, .notification_update_password_success,
Colors.white); Colors.green,
} else { Colors.white);
showNoIconTopSnackBar( } else {
modalBottomSheetContext, showNoIconTopSnackBar(
appLocalization(context) modalBottomSheetContext,
.notification_update_password_failed, appLocalization(context)
Colors.redAccent, .notification_update_password_failed,
Colors.white); Colors.redAccent,
Colors.white);
}
Navigator.pop(modalBottomSheetContext);
} }
Navigator.pop(modalBottomSheetContext); } catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
} }
}, },
icon: icon:
@@ -390,30 +410,36 @@ changeUserPassword(BuildContext context, SettingsBloc settingsBloc) {
? Center( ? Center(
child: TextButton( child: TextButton(
onPressed: () async { onPressed: () async {
if (formKey.currentState!.validate()) { try {
formKey.currentState!.save(); if (formKey.currentState!.validate()) {
Map<String, dynamic> body = { formKey.currentState!.save();
"password_old": oldPass, Map<String, dynamic> body = {
"password_new": newPass, "password_old": oldPass,
}; "password_new": newPass,
int statusCode = await apiServices };
.updateUserPassword(body); int statusCode =
if (statusCode == 200) { await apiServices.updateUserPassword(body);
showNoIconTopSnackBar( if (statusCode == 200) {
modalBottomSheetContext, showNoIconTopSnackBar(
appLocalization(context) modalBottomSheetContext,
.notification_update_password_success, appLocalization(context)
Colors.green, .notification_update_password_success,
Colors.white); Colors.green,
} else { Colors.white);
showNoIconTopSnackBar( } else {
modalBottomSheetContext, showNoIconTopSnackBar(
appLocalization(context) modalBottomSheetContext,
.notification_update_password_failed, appLocalization(context)
Colors.redAccent, .notification_update_password_failed,
Colors.white); Colors.redAccent,
Colors.white);
}
Navigator.pop(modalBottomSheetContext);
} }
Navigator.pop(modalBottomSheetContext); } catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
} }
}, },
style: const ButtonStyle( style: const ButtonStyle(

View File

@@ -3,6 +3,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 '../../product/shared/shared_snack_bar.dart';
import 'profile/profile_screen.dart'; import 'profile/profile_screen.dart';
import '../../product/constant/icon/icon_constants.dart'; import '../../product/constant/icon/icon_constants.dart';
import '../../product/extension/context_extension.dart'; import '../../product/extension/context_extension.dart';
@@ -27,7 +29,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
void initState() { void initState() {
super.initState(); super.initState();
settingsBloc = BlocProvider.of(context); settingsBloc = BlocProvider.of(context);
getUserProfile(); // getUserProfile();
} }
@override @override
@@ -38,76 +40,75 @@ class _SettingsScreenState extends State<SettingsScreen> {
centerTitle: true, centerTitle: true,
), ),
body: StreamBuilder<User>( body: StreamBuilder<User>(
stream: settingsBloc.streamUserProfile, stream: settingsBloc.streamUserProfile,
initialData: user, // initialData: user,
builder: (context, userSnapshot) { builder: (context, userSnapshot) {
return userSnapshot.data?.id == "" || user.id == "" if (userSnapshot.data == null) {
? Center( settingsBloc.getUserProfile(context);
child: CircularProgressIndicator( return const SharedLoadingAnimation();
value: context.highValue, } else {
return Column(
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( ),
mainAxisAlignment: MainAxisAlignment.center, SizedBox(height: context.lowValue),
crossAxisAlignment: CrossAxisAlignment.center, Row(
children: [ mainAxisAlignment: MainAxisAlignment.center,
CircleAvatar( children: [
backgroundColor: Theme.of(context).focusColor, Text(
radius: 70, userSnapshot.data?.name ?? "User Name",
child: CircleAvatar( style: context.h2,
backgroundColor: Theme.of(context).highlightColor, )
radius: 60, ],
child: CircleAvatar( ),
radius: 50, Row(
child: Text( mainAxisAlignment: MainAxisAlignment.center,
getAvatarContent( children: [Text(userSnapshot.data?.email ?? "Email")],
userSnapshot.data?.username ?? ""), ),
style: context.dynamicResponsiveSize(36), SizedBox(height: context.mediumValue),
), cardContent(
), Icons.account_circle_rounded,
), appLocalization(context).profile_change_info,
), userSnapshot.data ?? user),
SizedBox(height: context.lowValue), SizedBox(height: context.lowValue),
Row( cardContent(
mainAxisAlignment: MainAxisAlignment.center, Icons.lock_outline,
children: [ appLocalization(context).profile_change_pass,
Text( userSnapshot.data ?? user),
userSnapshot.data?.name ?? "User Name", SizedBox(height: context.lowValue),
style: context.h2, cardContent(
) Icons.settings_outlined,
], appLocalization(context).profile_setting,
), 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) { cardContent(IconData icon, String content, User user) {
return GestureDetector( return GestureDetector(
onTap: () async { onTap: () async {
if (icon == Icons.account_circle_rounded) { if (icon == Icons.account_circle_rounded) {
@@ -138,11 +139,16 @@ class _SettingsScreenState extends State<SettingsScreen> {
); );
} }
void getUserProfile() async { // void getUserProfile() async {
String data = await apiServices.getUserDetail(); // try {
user = User.fromJson(jsonDecode(data)); // user = await apiServices.getUserDetail();
settingsBloc.sinkUserProfile.add(user); // settingsBloc.sinkUserProfile.add(user);
} // } catch (e) {
// if (!mounted) return;
// showErrorTopSnackBarCustom(
// context, e.toString());
// }
// }
String getAvatarContent(String username) { String getAvatarContent(String username) {
String name = ""; String name = "";

View File

@@ -25,4 +25,5 @@ class ApplicationConstants {
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 = 15;
} }

View File

@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
@@ -13,6 +14,7 @@ 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);
@@ -35,15 +37,24 @@ 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 {
final url = Uri.https(ApplicationConstants.DOMAIN, path); try {
log("[${DateTime.now().toLocal().toString().split(' ')[1]}] GET url: $url"); final url = Uri.https(ApplicationConstants.DOMAIN, path);
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, stackTrace) {
log('Lỗi khi lấy dữ liệu: $e, StackTrace: $stackTrace');
throw Exception('Lỗi khi lấy dữ liệu: $e');
} }
} }
@@ -60,6 +71,25 @@ 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);
log("[${DateTime.now().toLocal().toString().split(' ')[1]}] GET Params url: $url");
final headers = await getHeaders();
final response = await http.get(url, headers: headers).timeout(
Duration(seconds: ApplicationConstants.CALL_API_TIMEOUT),
onTimeout: () =>
throw TimeoutException('Yêu cầu GET+PARAM hết thời gian'),
);
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); final url = Uri.https(ApplicationConstants.DOMAIN, path, params);
log("[${DateTime.now().toLocal().toString().split(' ')[1]}] GET Params url: $url"); log("[${DateTime.now().toLocal().toString().split(' ')[1]}] GET Params url: $url");
final headers = await getHeaders(); final headers = await getHeaders();
@@ -77,12 +107,27 @@ 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 {
final url = Uri.https(ApplicationConstants.DOMAIN, path); try {
log("[${DateTime.now().toLocal().toString().split(' ')[1]}] POST url: $url"); final url = Uri.https(ApplicationConstants.DOMAIN, path);
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.
@@ -90,12 +135,26 @@ 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 {
final url = Uri.https(ApplicationConstants.DOMAIN, path); try {
log("[${DateTime.now().toLocal().toString().split(' ')[1]}] PUT url: $url"); final url = Uri.https(ApplicationConstants.DOMAIN, path);
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.
@@ -105,10 +164,24 @@ 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 {
final url = Uri.https(ApplicationConstants.DOMAIN, path); try {
log("[${DateTime.now().toLocal().toString().split(' ')[1]}] DELETE url: $url"); final url = Uri.https(ApplicationConstants.DOMAIN, path);
final headers = await getHeaders(); log("[${DateTime.now().toLocal().toString().split(' ')[1]}] 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,21 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'dart:async';
import 'dart:convert'; 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 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:sfm_app/product/shared/model/province_model.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';
@@ -41,6 +52,40 @@ 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, // Thêm handler cho statusCode
}) 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) {
// log('Lỗi API: $e, StackTrace: $stackTrace');
throw Exception('$errorMessage: $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();
@@ -49,14 +94,11 @@ 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 = { Map<String, dynamic> body = {"user_id": uid, "app_token": token};
"user_id": uid, int statusCode = await NetworkManager.instance!
"app_token": token .updateDataInServer(APIPathConstants.NOTIFICATION_TOKEN_PATH, body);
};
int statusCode = await NetworkManager.instance!.updateDataInServer(
APIPathConstants.NOTIFICATION_TOKEN_PATH, body);
return statusCode; return statusCode;
} }
@@ -122,67 +164,88 @@ class APIServices {
return language; return language;
} }
Future<Bell> getBellNotifications(String offset, String pagesize) async { Future<Bell> getBellNotifications(String offset, String pageSize) async {
Bell bell = Bell(); final params = {"offset": offset, "page_size": pageSize};
final params = {"offset": offset, "page_size": pagesize}; return executeApiCall(
final data = await NetworkManager.instance!.getDataFromServerWithParams( () => NetworkManager.instance!.getDataFromServerWithParams(
APIPathConstants.BELL_NOTIFICATIONS_PATH, params); APIPathConstants.BELL_NOTIFICATIONS_PATH, params),
if (data != "") { parser: (json) => Bell.fromJson(json),
bell = Bell.fromJson(jsonDecode(data)); errorMessage: 'Lỗi khi GET /${APIPathConstants.BELL_NOTIFICATIONS_PATH}',
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,
}; };
int statusCode = await NetworkManager.instance!.updateDataInServer( return executeApiCall(
APIPathConstants.BELL_UPDATE_READ_NOTIFICATIONS_PATH, body); () => NetworkManager.instance!.updateDataInServer(
return statusCode; APIPathConstants.BELL_UPDATE_READ_NOTIFICATIONS_PATH, body),
statusCodeHandler: (statusCode) => statusCode,
errorMessage: 'Lỗi khi PUT /${APIPathConstants.BELL_NOTIFICATIONS_PATH}',
);
} }
Future<String> getUserDetail() async { Future<User> getUserDetail() async {
String uid = await getUID(); String uid = await getUID();
String? response = await NetworkManager.instance! return executeApiCall(
.getDataFromServer('${APIPathConstants.USER_PATH}/$uid'); () => NetworkManager.instance!
return response; .getDataFromServer('${APIPathConstants.USER_PATH}/$uid'),
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();
int statusCode = await NetworkManager.instance! return executeApiCall(
.updateDataInServer("${APIPathConstants.USER_PROFILE_PATH}/$uid", body); () => NetworkManager.instance!.updateDataInServer(
return statusCode; "${APIPathConstants.USER_PROFILE_PATH}/$uid", body),
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 statusCode; return executeApiCall(
() => NetworkManager.instance!.updateDataInServer(
"${APIPathConstants.USER_PATH}/$uid/password", body),
statusCodeHandler: (statusCode) => statusCode,
errorMessage: 'Lỗi khi PUT /${APIPathConstants.USER_PATH}');
} }
Future<String> getAllSettingsNotificationOfDevices() async { Future<List<DeviceNotificationSettings>>
String? data = await NetworkManager.instance! getAllSettingsNotificationOfDevices() async {
.getDataFromServer(APIPathConstants.DEVICE_NOTIFICATION_SETTINGS); return executeApiCall(
return data; () => NetworkManager.instance!
.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};
int statusCode = await NetworkManager.instance!.updateDataInServer( return executeApiCall(
APIPathConstants.DEVICE_NOTIFICATION_SETTINGS, body); () => NetworkManager.instance!.updateDataInServer(
return statusCode; APIPathConstants.DEVICE_NOTIFICATION_SETTINGS, body),
statusCodeHandler: (statusCode) => statusCode,
errorMessage:
'Lỗi khi PUT /${APIPathConstants.DEVICE_NOTIFICATION_SETTINGS}');
} }
Future<String> getDashBoardDevices() async { Future<List<DeviceWithAlias>> getDashBoardDevices() async {
String? data = await NetworkManager.instance! return executeApiCall(
.getDataFromServer(APIPathConstants.DASHBOARD_DEVICES); () => NetworkManager.instance!
return data; .getDataFromServer(APIPathConstants.DASHBOARD_DEVICES),
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 {
@@ -202,68 +265,105 @@ class APIServices {
"104": 1, "104": 1,
} }
}; };
int statusCode = await NetworkManager.instance!.updateDataInServer( return executeApiCall(
APIPathConstants.DEVICE_NOTIFICATION_SETTINGS, body); () => NetworkManager.instance!.updateDataInServer(
return statusCode; APIPathConstants.DEVICE_NOTIFICATION_SETTINGS, body),
statusCodeHandler: (statusCode) => statusCode,
errorMessage:
'Lỗi khi PUT /${APIPathConstants.DEVICE_NOTIFICATION_SETTINGS}');
} }
Future<String> getAllProvinces() async { Future<List<Province>> getAllProvinces() async {
String? data = await NetworkManager.instance! return executeApiCall(
.getDataFromServer(APIPathConstants.PROVINCES_PATH); () => NetworkManager.instance!
return data; .getDataFromServer(APIPathConstants.PROVINCES_PATH),
parser: (json) => Province.fromJsonDynamicList(json['items']),
errorMessage: 'Lỗi khi GET /${APIPathConstants.PROVINCES_PATH}');
;
} }
Future<String> getProvincesByName(String name) async { Future<List<Province>> getProvincesByName(String name) async {
final params = {'name': name}; final params = {'name': name};
String? data = await NetworkManager.instance! return executeApiCall(
.getDataFromServerWithParams(APIPathConstants.PROVINCES_PATH, params); () => NetworkManager.instance!
return data; .getDataFromServerWithParams(APIPathConstants.PROVINCES_PATH, params),
parser: (json) => Province.fromJsonDynamicList(json['items']),
errorMessage: 'Lỗi khi GET /${APIPathConstants.PROVINCES_PATH}/$name',
);
;
} }
Future<String> getProvinceByID(String provinceID) async { Future<Province> getProvinceByID(String provinceID) async {
String data = await NetworkManager.instance! return executeApiCall(
.getDataFromServer("${APIPathConstants.PROVINCES_PATH}/$provinceID"); () => NetworkManager.instance!
return data; .getDataFromServer("${APIPathConstants.PROVINCES_PATH}/$provinceID"),
parser: (json) => Province.fromJson(json['data']),
errorMessage:
'Lỗi khi GET /${APIPathConstants.PROVINCES_PATH}/$provinceID}',
);
} }
Future<String> getAllDistricts(String provinceID) async { Future<List<District>> getAllDistricts(String provinceID) async {
final params = {"parent": provinceID}; final params = {"parent": provinceID};
String? data = await NetworkManager.instance! return executeApiCall(
.getDataFromServerWithParams(APIPathConstants.DISTRICTS_PATH, params); () => NetworkManager.instance!
return data; .getDataFromServerWithParams(APIPathConstants.DISTRICTS_PATH, params),
parser: (json) => District.fromJsonDynamicList(json['items']),
errorMessage:
'Lỗi khi GET /${APIPathConstants.DISTRICTS_PATH} by parentCode $provinceID',
);
} }
Future<String> getDistrictsByName(String districtName) async { Future<List<District>> getDistrictsByName(String districtName) async {
final params = {"name": districtName}; final params = {"name": districtName};
String? data = await NetworkManager.instance! return executeApiCall(
.getDataFromServerWithParams(APIPathConstants.DISTRICTS_PATH, params); () => NetworkManager.instance!
return data; .getDataFromServerWithParams(APIPathConstants.DISTRICTS_PATH, params),
parser: (json) => District.fromJsonDynamicList(json['items']),
errorMessage:
'Lỗi khi GET /${APIPathConstants.DISTRICTS_PATH} by name $districtName',
);
} }
Future<String> getDistrictByID(String districtID) async { Future<District> getDistrictByID(String districtID) async {
String? data = await NetworkManager.instance! return executeApiCall(
.getDataFromServer("${APIPathConstants.DISTRICTS_PATH}/$districtID"); () => NetworkManager.instance!
return data; .getDataFromServer("${APIPathConstants.DISTRICTS_PATH}/$districtID"),
parser: (json) => District.fromJson(json['data']),
errorMessage:
'Lỗi khi GET /${APIPathConstants.DISTRICTS_PATH}/$districtID',
);
} }
Future<String> getAllWards(String districtID) async { Future<List<Ward>> getAllWards(String districtID) async {
final params = {'parent': districtID}; final params = {'parent': districtID};
String? data = await NetworkManager.instance! return executeApiCall(
.getDataFromServerWithParams(APIPathConstants.WARDS_PATH, params); () => NetworkManager.instance!
return data; .getDataFromServerWithParams(APIPathConstants.WARDS_PATH, params),
parser: (json) => Ward.fromJsonDynamicList(json['items']),
errorMessage:
'Lỗi khi GET /${APIPathConstants.WARDS_PATH} by parent $districtID',
);
} }
Future<String> getWarsdByName(String wardName) async { Future<List<Ward>> getWardsByName(String wardName) async {
final params = {"name": wardName}; final params = {"name": wardName};
String? data = await NetworkManager.instance! return executeApiCall(
.getDataFromServerWithParams(APIPathConstants.WARDS_PATH, params); () => NetworkManager.instance!
return data; .getDataFromServerWithParams(APIPathConstants.WARDS_PATH, params),
parser: (json) => Ward.fromJsonDynamicList(json['items']),
errorMessage:
'Lỗi khi GET /${APIPathConstants.WARDS_PATH} by name $wardName',
);
} }
Future<String> getWardByID(String wardID) async { Future<Ward> getWardByID(String wardID) async {
String? data = await NetworkManager.instance! return executeApiCall(
.getDataFromServer("${APIPathConstants.WARDS_PATH}/$wardID"); () => NetworkManager.instance!
return data; .getDataFromServer("${APIPathConstants.WARDS_PATH}/$wardID"),
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 {
@@ -271,131 +371,187 @@ 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ả!"
}; };
int statusCode = await NetworkManager.instance! return executeApiCall(
.updateDataInServer("${APIPathConstants.DEVICE_PATH}/$thingID", body); () => NetworkManager.instance!.updateDataInServer(
return statusCode; "${APIPathConstants.DEVICE_PATH}/$thingID", body),
statusCodeHandler: (statusCode) => statusCode,
errorMessage: 'Lỗi khi PUT /${APIPathConstants.DEVICE_PATH}/$thingID');
} }
Future<String> getOwnerDevices() async { Future<List<Device>> getOwnerDevices() async {
String? data = await NetworkManager.instance! return executeApiCall(
.getDataFromServer(APIPathConstants.DEVICE_PATH); () => NetworkManager.instance!
return data; .getDataFromServer(APIPathConstants.DEVICE_PATH),
parser: (json) => Device.fromJsonDynamicList(json['items']),
errorMessage: 'Lỗi khi GET /${APIPathConstants.DEVICE_PATH}',
);
} }
Future<String> getOwnerDeviceByState(Map<String, dynamic> params) async { Future<List<Device>> getOwnerDeviceByState(
String? data = await NetworkManager.instance! Map<String, dynamic> params) async {
.getDataFromServerWithParams(APIPathConstants.DEVICE_PATH, params); return executeApiCall(
return data; () => NetworkManager.instance!
.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 {
int? statusCode = await NetworkManager.instance! return executeApiCall(
.createDataInServer(APIPathConstants.DEVICE_PATH, body); () => NetworkManager.instance!
return statusCode; .createDataInServer(APIPathConstants.DEVICE_PATH, body),
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 {
int? statusCode = await NetworkManager.instance! return executeApiCall(
.createDataInServer(APIPathConstants.DEVICE_REGISTER_PATH, body); () => NetworkManager.instance!
return statusCode; .createDataInServer(APIPathConstants.DEVICE_REGISTER_PATH, body),
statusCodeHandler: (statusCode) => statusCode,
errorMessage: 'Lỗi khi POST /${APIPathConstants.DEVICE_REGISTER_PATH}');
} }
Future<int> deleteDeviceByAdmin(String thingID) async { Future<int> deleteDeviceByAdmin(String thingID) async {
int statusCode = await NetworkManager.instance! return executeApiCall(
.deleteDataInServer("${APIPathConstants.DEVICE_PATH}/$thingID"); () => NetworkManager.instance!
return statusCode; .deleteDataInServer("${APIPathConstants.DEVICE_PATH}/$thingID"),
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 {
int statusCode = await NetworkManager.instance! return executeApiCall(
.createDataInServer(APIPathConstants.DEVICE_UNREGISTER_PATH, body); () => NetworkManager.instance!
return statusCode; .createDataInServer(APIPathConstants.DEVICE_UNREGISTER_PATH, body),
statusCodeHandler: (statusCode) => statusCode,
errorMessage:
'Lỗi khi DELETE /${APIPathConstants.DEVICE_UNREGISTER_PATH} by USER');
} }
Future<String> getDeviceInfomation(String thingID) async { Future<Device> getDeviceInformation(String thingID) async {
String? response = await NetworkManager.instance! return executeApiCall(
.getDataFromServer("${APIPathConstants.DEVICE_PATH}/$thingID"); () => NetworkManager.instance!
return response; .getDataFromServer("${APIPathConstants.DEVICE_PATH}/$thingID"),
parser: (json) => Device.fromJson(json),
errorMessage: 'Lỗi khi GET /${APIPathConstants.DEVICE_PATH}/$thingID',
);
} }
Future<String> getAllGroups() async { Future<List<Group>> getAllGroups() async {
String? body = await NetworkManager.instance! return executeApiCall(
.getDataFromServer(APIPathConstants.ALL_GROUPS_PATH); () => NetworkManager.instance!
return body; .getDataFromServer(APIPathConstants.ALL_GROUPS_PATH),
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 {
int? statusCode = await NetworkManager.instance! return executeApiCall(
.createDataInServer(APIPathConstants.GROUPS_PATH, body); () => NetworkManager.instance!
return statusCode; .createDataInServer(APIPathConstants.GROUPS_PATH, body),
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 {
int? statusCode = await NetworkManager.instance! return executeApiCall(
.updateDataInServer("${APIPathConstants.GROUPS_PATH}/$groupID", body); () => NetworkManager.instance!.updateDataInServer(
return statusCode; "${APIPathConstants.GROUPS_PATH}/$groupID", body),
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 {
int? statusCode = await NetworkManager.instance! return executeApiCall(
.createDataInServer(APIPathConstants.JOIN_GROUP_PATH, body); () => NetworkManager.instance!
return statusCode; .createDataInServer(APIPathConstants.JOIN_GROUP_PATH, body),
statusCodeHandler: (statusCode) => statusCode,
errorMessage: 'Lỗi khi POST /${APIPathConstants.JOIN_GROUP_PATH}');
} }
Future<int> deleteGroup(String groupID) async { Future<int> deleteGroup(String groupID) async {
int? statusCode = await NetworkManager.instance! return executeApiCall(
.deleteDataInServer("${APIPathConstants.GROUPS_PATH}/$groupID"); () => NetworkManager.instance!
return statusCode; .deleteDataInServer("${APIPathConstants.GROUPS_PATH}/$groupID"),
statusCodeHandler: (statusCode) => statusCode,
errorMessage:
'Lỗi khi DELETE /${APIPathConstants.GROUPS_PATH}/$groupID');
} }
Future<String> getGroupDetail(String groupID) async { Future<GroupDetail> getGroupDetail(String groupID) async {
String? body = await NetworkManager.instance! return executeApiCall(
.getDataFromServer("${APIPathConstants.GROUPS_PATH}/$groupID"); () => NetworkManager.instance!
return body; .getDataFromServer("${APIPathConstants.GROUPS_PATH}/$groupID"),
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 {
int statusCode = await NetworkManager.instance! return executeApiCall(
.createDataInServer(APIPathConstants.APPROVE_GROUP_PATH, body); () => NetworkManager.instance!
return statusCode; .createDataInServer(APIPathConstants.APPROVE_GROUP_PATH, body),
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 {
int? statusCode = await NetworkManager.instance!.deleteDataInServer( return executeApiCall(
"${APIPathConstants.GROUPS_PATH}/$groupID/users/$userID"); () => NetworkManager.instance!.deleteDataInServer(
return statusCode; "${APIPathConstants.GROUPS_PATH}/$groupID/users/$userID"),
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 {
int? statusCode = await NetworkManager.instance!.deleteDataInServer( return executeApiCall(
"${APIPathConstants.GROUPS_PATH}/$groupID/devices/$thingID"); () => NetworkManager.instance!.deleteDataInServer(
return statusCode; "${APIPathConstants.GROUPS_PATH}/$groupID/devices/$thingID"),
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 {
int? statusCode = await NetworkManager.instance!.updateDataInServer( return executeApiCall(
APIPathConstants.DEVICE_NOTIFICATION_SETTINGS, body); () => NetworkManager.instance!.updateDataInServer(
return statusCode; APIPathConstants.DEVICE_NOTIFICATION_SETTINGS, body),
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 {
int? statusCode = await NetworkManager.instance!.createDataInServer( return executeApiCall(
"${APIPathConstants.GROUPS_PATH}/$groupID/things", body); () => NetworkManager.instance!.createDataInServer(
return statusCode; "${APIPathConstants.GROUPS_PATH}/$groupID/things", body),
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 {
int? statusCode = await NetworkManager.instance! return executeApiCall(
.updateDataInServer("${APIPathConstants.DEVICE_PATH}/$thingID", body); () => NetworkManager.instance!.updateDataInServer(
return statusCode; "${APIPathConstants.DEVICE_PATH}/$thingID", body),
statusCodeHandler: (statusCode) => statusCode,
errorMessage: 'Lỗi khi PUT /${APIPathConstants.DEVICE_PATH}/$thingID');
} }
Future<String> getLogsOfDevice( Future<DeviceLog> getLogsOfDevice(
String thingID, Map<String, dynamic> params) async { String thingID, Map<String, dynamic> params) async {
String? body = await NetworkManager.instance! return executeApiCall(
.getDataFromServerWithParams(APIPathConstants.DEVICE_LOGS_PATH, params); () => NetworkManager.instance!.getDataFromServerWithParams(
return body; APIPathConstants.DEVICE_LOGS_PATH, params),
parser: (json) => DeviceLog.fromJson(json),
errorMessage: 'Lỗi khi GET /${APIPathConstants.DEVICE_LOGS_PATH}',
);
} }
} }

View File

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

@@ -0,0 +1,23 @@
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(-1); widget.devicesManagerBloc.getDeviceByState(context,-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(0); widget.devicesManagerBloc.getDeviceByState(context,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(1); widget.devicesManagerBloc.getDeviceByState(context,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(2); widget.devicesManagerBloc.getDeviceByState(context,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(3); widget.devicesManagerBloc.getDeviceByState(context,3);
break; break;
} }
} }

View File

@@ -93,30 +93,34 @@ class DeviceUtils {
return map; return map;
} }
Future<String> getFullDeviceLocation( Future<String> getFullDeviceLocation(
BuildContext context, String areaPath) async { BuildContext context, String areaPath, String? deviceName) async {
if (areaPath != "") { if (areaPath.isNotEmpty) {
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];
String provinceBody = await apiServices.getProvinceByID(provinceID); Province province = await apiServices.getProvinceByID(provinceID);
final provinceItem = jsonDecode(provinceBody); District district = await apiServices.getDistrictByID(districtID);
Province province = Province.fromJson(provinceItem['data']); Ward ward = await apiServices.getWardByID(wardID);
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) {
@@ -157,11 +161,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);
} }
} }

View File

@@ -441,10 +441,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: google_maps_flutter name: google_maps_flutter
sha256: "830d8f7b51b4a950bf0d7daa675324fed6c9beb57a7ecca2a59018270c96b4e0" sha256: d7e4704e6b9f3452c7cd9eb6efc226e1f9e8273c28da47b0a1e7451916d71005
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.12.1" version: "2.12.2"
google_maps_flutter_android: google_maps_flutter_android:
dependency: transitive dependency: transitive
description: description:
@@ -565,6 +565,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.0" version: "1.3.0"
lottie:
dependency: "direct main"
description:
name: lottie
sha256: c5fa04a80a620066c15cf19cc44773e19e9b38e989ff23ea32e5903ef1015950
url: "https://pub.dev"
source: hosted
version: "3.3.1"
maps_launcher: maps_launcher:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@@ -43,10 +43,11 @@ dependencies:
# persistent_bottom_nav_bar_v2: ^4.2.8 # persistent_bottom_nav_bar_v2: ^4.2.8
persistent_bottom_nav_bar: ^6.2.1 persistent_bottom_nav_bar: ^6.2.1
win32: ^5.10.0 win32: ^5.10.0
google_maps_flutter: ^2.12.1 google_maps_flutter: ^2.12.2
data_table_2: ^2.5.18 data_table_2: ^2.5.18
url_launcher: ^6.3.1 url_launcher: ^6.3.1
app_settings: ^5.1.1 app_settings: ^5.1.1
lottie: ^3.3.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
@@ -64,6 +65,7 @@ flutter:
- assets/icons/ - assets/icons/
- assets/map_themes/ - assets/map_themes/
- assets/sounds/ - assets/sounds/
- assets/animations/
flutter_launcher_icons: flutter_launcher_icons:
android: "launcher_icon" android: "launcher_icon"

View File

@@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake)
# https://github.com/flutter/flutter/issues/57146. # https://github.com/flutter/flutter/issues/57146.
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
# Set fallback configurations for older versions of the flutter tool.
if (NOT DEFINED FLUTTER_TARGET_PLATFORM)
set(FLUTTER_TARGET_PLATFORM "windows-x64")
endif()
# === Flutter Library === # === Flutter Library ===
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
@@ -92,7 +97,7 @@ add_custom_command(
COMMAND ${CMAKE_COMMAND} -E env COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT} ${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
windows-x64 $<CONFIG> ${FLUTTER_TARGET_PLATFORM} $<CONFIG>
VERBATIM VERBATIM
) )
add_custom_target(flutter_assemble DEPENDS add_custom_target(flutter_assemble DEPENDS

View File

@@ -31,6 +31,11 @@ bool FlutterWindow::OnCreate() {
this->Show(); this->Show();
}); });
// Flutter can complete the first frame before the "show window" callback is
// registered. The following call ensures a frame is pending to ensure the
// window is shown. It is a no-op if the first frame hasn't completed yet.
flutter_controller_->ForceRedraw();
return true; return true;
} }