Logging data
Try-catch function
This commit is contained in:
anhtunz
2025-06-17 16:43:45 +07:00
parent 22fef0e0a8
commit 2d53f2cdd3
41 changed files with 1591 additions and 1299 deletions

View File

@@ -36,6 +36,7 @@
android:windowSoftInputMode="adjustResize"
android:showWhenLocked="true"
android:turnScreenOn="true"
android:enableOnBackInvokedCallback="true"
android:exported="true">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user

3
devtools_options.yaml Normal file
View File

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

View File

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

View File

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

View File

@@ -1,13 +1,10 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import '../feature/devices/device_model.dart';
import '../product/base/bloc/base_bloc.dart';
import '../product/constant/app/app_constants.dart';
import '../product/services/api_services.dart';
import '../product/shared/shared_snack_bar.dart';
import '../product/utils/date_time_utils.dart';
import '../product/utils/device_utils.dart';
@@ -39,15 +36,12 @@ class DeviceLogsBloc extends BlocBase {
void dispose() {}
void getAllDevices(BuildContext context) async {
try {
await apiServices.execute(context, () async {
List<Device> originalDevices = await apiServices.getOwnerDevices();
List<Device> devices =
DeviceUtils.instance.sortDeviceByState(originalDevices);
sinkAllDevices.add(devices);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(context, e.toString());
}
});
}
void getDeviceLogByThingID(
@@ -57,7 +51,7 @@ class DeviceLogsBloc extends BlocBase {
DateTime fromDate,
List<SensorLogs> sensors,
) async {
try {
await apiServices.execute(context, () async {
sinkmessage.add(ApplicationConstants.LOADING);
String fromDateString =
DateTimeUtils.instance.formatDateTimeToString(fromDate);
@@ -85,9 +79,38 @@ class DeviceLogsBloc extends BlocBase {
sinkmessage.add(ApplicationConstants.NO_DATA);
}
sinkSensors.add(sensors);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(context, e.toString());
}
});
// try {
// sinkmessage.add(ApplicationConstants.LOADING);
// String fromDateString =
// DateTimeUtils.instance.formatDateTimeToString(fromDate);
// String now =
// DateTimeUtils.instance.formatDateTimeToString(DateTime.now());
// Map<String, dynamic> params = {
// 'thing_id': thingID,
// 'from': fromDateString,
// 'to': now,
// 'limit': '30',
// "offset": offset.toString(),
// "asc": "true"
// };
// DeviceLog devicesListLog =
// await apiServices.getLogsOfDevice(thingID, params);
// if (devicesListLog.sensors!.isEmpty) {
// bool hasMore = false;
// sinkHasMore.add(hasMore);
// }
// if (devicesListLog.sensors!.isNotEmpty) {
// for (var sensor in devicesListLog.sensors!) {
// sensors.add(sensor);
// }
// } else {
// sinkmessage.add(ApplicationConstants.NO_DATA);
// }
// sinkSensors.add(sensors);
// } catch (e) {
// if (!context.mounted) return;
// showErrorTopSnackBarCustom(context, e.toString());
// }
}
}

View File

@@ -36,7 +36,6 @@ class DeviceNotificationSettingsBloc extends BlocBase {
StreamSink<String> get sinkMessageChange => messageChange.sink;
Stream<String> get streaMmessageChange => messageChange.stream;
@override
void dispose() {}
}

View File

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

View File

@@ -1,13 +1,10 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import '../feature/devices/device_model.dart';
import '../product/base/bloc/base_bloc.dart';
import '../product/constant/app/app_constants.dart';
import '../product/services/api_services.dart';
import '../product/shared/shared_snack_bar.dart';
import '../product/utils/device_utils.dart';
@@ -76,7 +73,7 @@ class DevicesManagerBloc extends BlocBase {
// }
// }
void getDeviceByState(BuildContext context,int state) async {
void getDeviceByState(BuildContext context, int state) async {
try {
sinkTagStates.add([state]);
@@ -91,16 +88,16 @@ class DevicesManagerBloc extends BlocBase {
List<Device> devices = [];
List<Device> originalDevices = [];
if (state != -2) {
originalDevices =
await apiServices.getOwnerDeviceByState({"state": state.toString()});
originalDevices = await apiServices
.getOwnerDeviceByState({"state": state.toString()});
} else {
originalDevices = await apiServices.getOwnerDevices();
}
List<Device> publicDevices = [];
for(var device in originalDevices){
if(device.visibility == "PUBLIC"){
for (var device in originalDevices) {
if (device.visibility == "PUBLIC") {
publicDevices.add(device);
}
}
@@ -119,10 +116,8 @@ class DevicesManagerBloc extends BlocBase {
sinkAllDevices.add(devices);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
showErrorTopSnackBarCustom(context, e.toString());
}
}
String _getStateKey(int state) {

View File

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

View File

@@ -5,18 +5,19 @@ import '../feature/home/device_alias_model.dart';
import '../product/base/bloc/base_bloc.dart';
class HomeBloc extends BlocBase {
final allDevicesAliasMap = StreamController<Map<String,List<DeviceWithAlias>>>.broadcast();
StreamSink<Map<String,List<DeviceWithAlias>>> get sinkAllDevicesAliasMap =>
final allDevicesAliasMap =
StreamController<Map<String, List<DeviceWithAlias>>>.broadcast();
StreamSink<Map<String, List<DeviceWithAlias>>> get sinkAllDevicesAliasMap =>
allDevicesAliasMap.sink;
Stream<Map<String,List<DeviceWithAlias>>> get streamAllDevicesAliasMap =>
Stream<Map<String, List<DeviceWithAlias>>> get streamAllDevicesAliasMap =>
allDevicesAliasMap.stream;
final allDevicesAliasJoinedMap = StreamController<Map<String,List<DeviceWithAlias>>>.broadcast();
StreamSink<Map<String,List<DeviceWithAlias>>> get sinkAllDevicesAliasJoinedMap =>
allDevicesAliasJoinedMap.sink;
Stream<Map<String,List<DeviceWithAlias>>> get streamAllDevicesAliasJoinedMap =>
allDevicesAliasJoinedMap.stream;
final allDevicesAliasJoinedMap =
StreamController<Map<String, List<DeviceWithAlias>>>.broadcast();
StreamSink<Map<String, List<DeviceWithAlias>>>
get sinkAllDevicesAliasJoinedMap => allDevicesAliasJoinedMap.sink;
Stream<Map<String, List<DeviceWithAlias>>>
get streamAllDevicesAliasJoinedMap => allDevicesAliasJoinedMap.stream;
final countNotification = StreamController<int>.broadcast();
StreamSink<int> get sinkCountNotification => countNotification.sink;
@@ -26,13 +27,19 @@ class HomeBloc extends BlocBase {
StreamSink<bool?> get sinkHasJoinedDevice => hasJoinedDevice.sink;
Stream<bool?> get streamHasJoinedDevice => hasJoinedDevice.stream;
final ownerDevicesStatus =
final ownerDevicesStatus =
StreamController<Map<String, List<DeviceWithAlias>>>.broadcast();
StreamSink<Map<String, List<DeviceWithAlias>>>
get sinkOwnerDevicesStatus => ownerDevicesStatus.sink;
StreamSink<Map<String, List<DeviceWithAlias>>> get sinkOwnerDevicesStatus =>
ownerDevicesStatus.sink;
Stream<Map<String, List<DeviceWithAlias>>> get streamOwnerDevicesStatus =>
ownerDevicesStatus.stream;
@override
void dispose() {}
void dispose() {
allDevicesAliasMap.close();
allDevicesAliasJoinedMap.close();
countNotification.close();
hasJoinedDevice.close();
ownerDevicesStatus.close();
}
}

View File

@@ -1,16 +1,13 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/material.dart';
import '../product/constant/app/app_constants.dart';
import '../product/constant/app/app_constants.dart';
import '../product/services/api_services.dart';
import '../product/base/bloc/base_bloc.dart';
import '../product/services/language_services.dart';
import '../product/shared/shared_snack_bar.dart';
import '../product/utils/response_status_utils.dart';
import '../feature/inter_family/groups/groups_model.dart';
@@ -36,10 +33,10 @@ class InterFamilyBloc extends BlocBase {
@override
void dispose() {}
void getAllGroup(String role) async {
void getAllGroup(BuildContext context, String role) async {
List<Group> groups = [];
sinkCurrentGroups.add(groups);
try {
await apiServices.execute(context, () async {
groups = await apiServices.getAllGroups();
groups = sortGroupByName(groups);
@@ -55,19 +52,13 @@ class InterFamilyBloc extends BlocBase {
return false;
},
).toList();
sinkCurrentGroups.add(currentGroups);
} catch (e) {
// Xử lý lỗi khi jsonDecode thất bại
log("Error decoding JSON: $e");
sinkCurrentGroups.add([]);
}
log("Inter Family Role: $role");
});
}
Future<void> createGroup(
BuildContext context, String name, String description) async {
try {
await apiServices.execute(context, () async {
Map<String, dynamic> body = {"name": name, "description": description};
int? statusCode = await apiServices.createGroup(body);
showSnackBarResponseByStatusCode(
@@ -75,17 +66,12 @@ class InterFamilyBloc extends BlocBase {
statusCode,
appLocalization(context).notification_add_group_success,
appLocalization(context).notification_add_group_failed);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
});
}
Future<void> changeGroupInformation(BuildContext context, String groupID,
String name, String description) async {
try {
await apiServices.execute(context, () async {
Map<String, dynamic> body = {"name": name, "description": description};
int statusCode = await apiServices.updateGroup(body, groupID);
showSnackBarResponseByStatusCode(
@@ -93,44 +79,32 @@ class InterFamilyBloc extends BlocBase {
statusCode,
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 {
try {
Map<String, dynamic> body = {
"group_id": groupID,
};
Map<String, dynamic> body = {
"group_id": groupID,
};
await apiServices.execute(context, () async {
int statusCode = await apiServices.joinGroup(groupID, body);
showSnackBarResponseByStatusCode(
context,
statusCode,
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 {
try {
await apiServices.execute(context, () async {
int statusCode = await apiServices.deleteGroup(groupID);
showSnackBarResponseByStatusCode(
context,
statusCode,
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) {

View File

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

View File

@@ -1,18 +1,16 @@
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:sfm_app/product/cache/local_manager.dart';
import 'package:sfm_app/product/constant/app/api_path_constant.dart';
import 'package:sfm_app/product/constant/enums/local_keys_enums.dart';
import 'package:sfm_app/product/network/network_manager.dart';
import '../product/cache/local_manager.dart';
import '../product/constant/app/api_path_constant.dart';
import '../product/constant/enums/local_keys_enums.dart';
import '../product/network/network_manager.dart';
import '../product/base/bloc/base_bloc.dart';
import '../product/services/api_services.dart';
import '../feature/bell/bell_model.dart';
import '../feature/settings/profile/profile_model.dart';
import '../product/shared/shared_snack_bar.dart';
class MainBloc extends BlocBase {
APIServices apiServices = APIServices();
@@ -44,19 +42,13 @@ class MainBloc extends BlocBase {
void dispose() {}
void getUserProfile(BuildContext context) async {
try {
await apiServices.execute(context, () async {
User user = await apiServices.getUserDetail();
sinkUserProfile.add(user);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
});
}
getFCMTokenAndPresentations() async {
String? firebaseAppToken = await FirebaseMessaging.instance.getToken();
if (firebaseAppToken != null) {
@@ -67,14 +59,11 @@ class MainBloc extends BlocBase {
}
}
Future<int> sendNotificationToken(String token) async{
Future<int> sendNotificationToken(String token) async {
String uid = await getUID();
Map<String,dynamic> body = {
"user_id": uid,
"app_token": token
};
int statusCode = await NetworkManager.instance!.updateDataInServer(
APIPathConstants.NOTIFICATION_TOKEN_PATH, body);
Map<String, dynamic> body = {"user_id": uid, "app_token": token};
int statusCode = await NetworkManager.instance!
.updateDataInServer(APIPathConstants.NOTIFICATION_TOKEN_PATH, body);
return statusCode;
}

View File

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

View File

@@ -1,11 +1,9 @@
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:sfm_app/product/services/api_services.dart';
import '../product/services/api_services.dart';
import '../feature/settings/profile/profile_model.dart';
import '../product/base/bloc/base_bloc.dart';
import '../product/shared/shared_snack_bar.dart';
class SettingsBloc extends BlocBase {
// Settings Screen
@@ -14,7 +12,6 @@ class SettingsBloc extends BlocBase {
StreamSink<User> get sinkUserProfile => userProfile.sink;
Stream<User> get streamUserProfile => userProfile.stream;
// Profile Screen
final isChangeProfileInfomation = StreamController<bool>.broadcast();
StreamSink<bool> get sinkIsChangeProfileInfomation =>
@@ -22,20 +19,13 @@ class SettingsBloc extends BlocBase {
Stream<bool> get streamIsChangeProfileInfomation =>
isChangeProfileInfomation.stream;
void getUserProfile(BuildContext context) async {
try {
void getUserProfile(BuildContext context) async {
await apiServices.execute(context, () async {
User user = await apiServices.getUserDetail();
sinkUserProfile.add(user);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
});
}
@override
void dispose() {
}
void dispose() {}
}

View File

@@ -1,8 +1,7 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:sfm_app/product/shared/shared_snack_bar.dart';
import '../../product/extension/context_extension.dart';
import '../../product/services/language_services.dart';
import '../../bloc/bell_bloc.dart';
import '../../product/base/bloc/base_bloc.dart';
@@ -77,7 +76,8 @@ class _BellScreenState extends State<BellScreen> {
if (index < bellSnapshot.data!.length) {
return GestureDetector(
onTap: () async {
readNotification(bellSnapshot.data![index].id!);
readNotification(
bellSnapshot.data![index].id!);
},
child: Column(
children: [
@@ -163,24 +163,18 @@ class _BellScreenState extends State<BellScreen> {
getBellNotification(offset);
}
void readNotification(String id) async{
try {
void readNotification(String id) async {
await apiServices.execute(context, () async {
List<String> read = [];
read.add(id);
await apiServices
.updateStatusOfNotification(read);
await apiServices.updateStatusOfNotification(read);
read.clear();
} catch (e) {
if (mounted){
showErrorTopSnackBarCustom(
context, e.toString());
}
}
});
refresh();
}
Future<void> getBellNotification(int offset) async {
try {
apiServices.execute(context, () async {
bell = await apiServices.getBellNotifications(
offset.toString(), (offset + 20).toString());
if (bell.items!.isEmpty) {
@@ -193,10 +187,7 @@ class _BellScreenState extends State<BellScreen> {
}
bellBloc.bellItems.add(items);
check = false;
} catch (e) {
if (!mounted) return;
showErrorTopSnackBarCustom(context, e.toString());
}
});
}
String timeAgo(BuildContext context, DateTime dateTime) {

View File

@@ -1,8 +1,9 @@
// ignore_for_file: use_build_context_synchronously
import 'package:flutter/material.dart';
import 'package:sfm_app/bloc/devices_manager_bloc.dart';
import 'package:sfm_app/product/shared/shared_snack_bar.dart';
import '../../bloc/devices_manager_bloc.dart';
import '../../product/shared/shared_snack_bar.dart';
import '../../product/utils/response_status_utils.dart';
import '../../product/constant/enums/role_enums.dart';
import '../../product/services/api_services.dart';
@@ -11,7 +12,8 @@ import '../../product/constant/icon/icon_constants.dart';
import '../../product/extension/context_extension.dart';
import '../../product/services/language_services.dart';
addNewDevice(BuildContext context, String role, DevicesManagerBloc deviceManagerBloc) async {
addNewDevice(BuildContext context, String role,
DevicesManagerBloc deviceManagerBloc) async {
TextEditingController extIDController = TextEditingController(text: "");
TextEditingController deviceNameController = TextEditingController(text: "");
ScaffoldMessenger.of(context).showSnackBar(
@@ -77,7 +79,8 @@ addNewDevice(BuildContext context, String role, DevicesManagerBloc deviceManager
Colors.white);
ScaffoldMessenger.of(context).hideCurrentSnackBar();
} else {
addDevices(context, role, extID, deviceName, deviceManagerBloc);
addDevices(
context, role, extID, deviceName, deviceManagerBloc);
ScaffoldMessenger.of(context).hideCurrentSnackBar();
}
},
@@ -90,12 +93,12 @@ addNewDevice(BuildContext context, String role, DevicesManagerBloc deviceManager
);
}
void addDevices(
BuildContext context, String role, String extID, String deviceName, DevicesManagerBloc deviceManagerBloc) async {
void addDevices(BuildContext context, String role, String extID,
String deviceName, DevicesManagerBloc deviceManagerBloc) async {
APIServices apiServices = APIServices();
Map<String, dynamic> body = {};
if (role == RoleEnums.ADMIN.name) {
try {
await apiServices.execute(context,() async {
body = {"ext_id": extID, "name": deviceName};
int statusCode = await apiServices.createDeviceByAdmin(body);
showSnackBarResponseByStatusCode(
@@ -103,15 +106,10 @@ void addDevices(
statusCode,
appLocalization(context).notification_create_device_success,
appLocalization(context).notification_create_device_failed);
deviceManagerBloc.getDeviceByState(context,-2);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
deviceManagerBloc.getDeviceByState(context, -2);
});
} else {
try {
await apiServices.execute(context,() async {
body = {"ext_id": extID};
int statusCode = await apiServices.registerDevice(body);
showSnackBarResponseByStatusCode(
@@ -119,12 +117,7 @@ void addDevices(
statusCode,
appLocalization(context).notification_add_device_success,
appLocalization(context).notification_device_not_exist);
deviceManagerBloc.getDeviceByState(context,-2);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
deviceManagerBloc.getDeviceByState(context, -2);
});
}
}

View File

@@ -5,7 +5,6 @@ import '../../bloc/devices_manager_bloc.dart';
import '../../product/constant/enums/role_enums.dart';
import '../../product/services/api_services.dart';
import '../../product/services/language_services.dart';
import '../../product/shared/shared_snack_bar.dart';
import '../../product/utils/response_status_utils.dart';
handleDeleteDevice(BuildContext context, DevicesManagerBloc devicesManagerBloc,
@@ -48,27 +47,24 @@ deleteOrUnregisterDevice(BuildContext context, DevicesManagerBloc devicesBloc,
Map<String, dynamic> body = {
"ext_id": extID,
};
int statusCode = await apiServices.unregisterDevice(body);
showSnackBarResponseByStatusCode(
context,
statusCode,
appLocalization(context).notification_delete_device_success,
appLocalization(context).notification_delete_device_failed);
devicesBloc.getDeviceByState(context,-2);
await apiServices.execute(context, () async {
int statusCode = await apiServices.unregisterDevice(body);
showSnackBarResponseByStatusCode(
context,
statusCode,
appLocalization(context).notification_delete_device_success,
appLocalization(context).notification_delete_device_failed);
devicesBloc.getDeviceByState(context, -2);
});
} else {
try {
await apiServices.execute(context, () async {
int statusCode = await apiServices.deleteDeviceByAdmin(extID);
showSnackBarResponseByStatusCode(
context,
statusCode,
appLocalization(context).notification_delete_device_success,
appLocalization(context).notification_delete_device_failed);
devicesBloc.getDeviceByState(context,-2);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
devicesBloc.getDeviceByState(context, -2);
});
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,10 @@
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:persistent_bottom_nav_bar/persistent_bottom_nav_bar.dart';
import '../../product/shared/shared_component_loading_animation.dart';
import '../../product/shared/shared_loading_animation.dart';
import '../../product/shared/shared_snack_bar.dart';
import '../../product/utils/app_logger_utils.dart';
import 'shared/alert_card.dart';
import 'shared/warning_card.dart';
import '../../product/utils/device_utils.dart';
@@ -19,8 +18,8 @@ import '../../bloc/home_bloc.dart';
import '../../product/base/bloc/base_bloc.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
const HomeScreen({super.key, required this.persistentTabController});
final PersistentTabController persistentTabController;
@override
State<HomeScreen> createState() => _HomeScreenState();
}
@@ -29,7 +28,6 @@ class _HomeScreenState extends State<HomeScreen> {
late HomeBloc homeBloc;
APIServices apiServices = APIServices();
Map<String, List<DeviceWithAlias>> allDevicesAliasMap = {};
Map<String, List<DeviceWithAlias>> allDevicesAliasJoinedMap = {};
List<DeviceWithAlias> devices = [];
bool isFunctionCall = false;
Timer? getAllDevicesTimer;
@@ -41,6 +39,8 @@ class _HomeScreenState extends State<HomeScreen> {
void initState() {
super.initState();
homeBloc = BlocProvider.of(context);
getAllDevicesTimer?.cancel();
AppLoggerUtils.debug("Init State HomeScreen");
getOwnerAndJoinedDevices();
const duration = Duration(seconds: 10);
getAllDevicesTimer =
@@ -50,6 +50,7 @@ class _HomeScreenState extends State<HomeScreen> {
@override
void dispose() {
getAllDevicesTimer?.cancel();
homeBloc.dispose();
super.dispose();
}
@@ -198,107 +199,23 @@ class _HomeScreenState extends State<HomeScreen> {
),
),
),
StreamBuilder<bool?>(
stream: homeBloc.streamHasJoinedDevice,
initialData: null,
builder: (context, hasJoinedDeviceSnapshot) {
return StreamBuilder<Map<String, List<DeviceWithAlias>>>(
stream: homeBloc.streamAllDevicesAliasMap,
builder: (context, allDevicesAliasMapSnapshot) {
return StreamBuilder<Map<String, List<DeviceWithAlias>>>(
stream: homeBloc.streamAllDevicesAliasJoinedMap,
builder: (context, allDevicesAliasJoinedMapSnapshot) {
if (hasJoinedDeviceSnapshot.data == null) {
return const SharedComponentLoadingAnimation();
} else {
final data = allDevicesAliasMapSnapshot.data!;
final dataJoined =
allDevicesAliasJoinedMapSnapshot.data!;
if (hasJoinedDeviceSnapshot.data == false) {
if (!allDevicesAliasMapSnapshot.hasData ||
allDevicesAliasMapSnapshot.data == null) {
return const Center(
child: CircularProgressIndicator());
}
return OverviewCard(
isOwner: true,
total: data['all']?.length ?? 0,
active: data['online']?.length ?? 0,
inactive: data['offline']?.length ?? 0,
warning: data['warn']?.length ?? 0,
unused: data['not-use']?.length ?? 0,
showUnused: false,
);
} else {
return DefaultTabController(
length: 2,
child: Column(
// mainAxisSize: MainAxisSize.min,
// crossAxisAlignment: CrossAxisAlignment.start,
children: [
TabBar(
tabs: [
Tab(
text: appLocalization(context)
.over_view_owner_devices),
Tab(
text: appLocalization(context)
.over_view_joined_devices),
],
labelColor: Colors.blue,
unselectedLabelColor: Colors.grey,
indicatorColor: Colors.blue,
),
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: context.dynamicHeight(0.5),
minHeight: context.dynamicHeight(0.3),
),
child: TabBarView(
children: [
OverviewCard(
isOwner: true,
total: data['all']?.length ?? 0,
active: data['online']?.length ?? 0,
inactive:
data['offline']?.length ?? 0,
warning: data['warn']?.length ?? 0,
unused:
data['not-use']?.length ?? 0,
showUnused: false,
),
OverviewCard(
isOwner: false,
total:
dataJoined['all']?.length ?? 0,
active:
dataJoined['online']?.length ??
0,
inactive:
dataJoined['offline']?.length ??
0,
warning:
dataJoined['warn']?.length ?? 0,
unused:
dataJoined['not-use']?.length ??
0,
showUnused: false,
showActive: false,
showInactive: false,
),
],
),
),
],
),
);
}
}
},
);
},
);
StreamBuilder<Map<String, List<DeviceWithAlias>>>(
stream: homeBloc.streamAllDevicesAliasMap,
builder: (context, allDevicesAliasMapSnapshot) {
if(allDevicesAliasMapSnapshot.data == null){
return const SharedComponentLoadingAnimation();
}else{
final data = allDevicesAliasMapSnapshot.data!;
return OverviewCard(
isOwner: true,
total: data['all']?.length ?? 0,
active: data['online']?.length ?? 0,
inactive: data['offline']?.length ?? 0,
warning: data['warn']?.length ?? 0,
unused: data['not-use']?.length ?? 0,
showUnused: false,
);
}
},
),
],
@@ -309,8 +226,8 @@ class _HomeScreenState extends State<HomeScreen> {
}
void getOwnerAndJoinedDevices() async {
try {
devices = await apiServices.getDashBoardDevices();
await apiServices.execute(context, () async {
devices = await apiServices.getDashBoardDevices().handleApiError();
List<DeviceWithAlias> publicDevices = [];
for (var device in devices) {
if (device.visibility == "PUBLIC") {
@@ -320,10 +237,7 @@ class _HomeScreenState extends State<HomeScreen> {
getOwnerDeviceState(publicDevices);
checkSettingDevice(publicDevices);
getDeviceStatusAliasMap(publicDevices);
} catch (e) {
if (!mounted) return;
showErrorTopSnackBarCustom(context, e.toString());
}
});
}
void getOwnerDeviceState(List<DeviceWithAlias> allDevices) async {
@@ -335,7 +249,7 @@ class _HomeScreenState extends State<HomeScreen> {
int count = 0;
for (var device in allDevices) {
if (device.isOwner != true) continue;
// if (device.isOwner != true) continue;
if (!mounted) return;
Map<String, dynamic> sensorMap = DeviceUtils.instance
@@ -368,55 +282,34 @@ class _HomeScreenState extends State<HomeScreen> {
void getDeviceStatusAliasMap(List<DeviceWithAlias> devices) {
allDevicesAliasMap.clear();
allDevicesAliasJoinedMap.clear();
bool check = false;
for (var key in ['all', 'online', 'offline', 'warning', 'not-use']) {
allDevicesAliasMap[key] = [];
allDevicesAliasJoinedMap[key] = [];
}
for (DeviceWithAlias device in devices) {
if (device.isOwner == true) {
allDevicesAliasMap['all']!.add(device);
if (device.state == 0 || device.state == 1) {
allDevicesAliasMap['online']!.add(device);
}
if (device.state == -1) {
allDevicesAliasMap['offline']!.add(device);
}
if (device.state == 1) {
allDevicesAliasMap['warning']!.add(device);
}
if (device.state == -2) {
allDevicesAliasMap['not-use']!.add(device);
}
} else {
check = true;
allDevicesAliasJoinedMap['all']!.add(device);
if (device.state == 0 || device.state == 1) {
allDevicesAliasJoinedMap['online']!.add(device);
}
if (device.state == -1) {
allDevicesAliasJoinedMap['offline']!.add(device);
}
if (device.state == 1) {
allDevicesAliasJoinedMap['warning']!.add(device);
}
if (device.state == -2) {
allDevicesAliasJoinedMap['not-use']!.add(device);
}
allDevicesAliasMap['all']!.add(device);
if (device.state == 0 || device.state == 1) {
allDevicesAliasMap['online']!.add(device);
}
if (device.state == -1) {
allDevicesAliasMap['offline']!.add(device);
}
if (device.state == 1) {
allDevicesAliasMap['warning']!.add(device);
}
if (device.state == -2) {
allDevicesAliasMap['not-use']!.add(device);
}
}
homeBloc.sinkHasJoinedDevice.add(check);
homeBloc.sinkAllDevicesAliasMap.add(allDevicesAliasMap);
homeBloc.sinkAllDevicesAliasJoinedMap.add(allDevicesAliasJoinedMap);
}
void checkSettingDevice(List<DeviceWithAlias> devices) async {
try {
if (isFunctionCall) {
log("Ham check setting da duoc goi");
} else {
if (isFunctionCall) {
log("Ham check setting da duoc goi");
} else {
await apiServices.execute(context, () async {
List<DeviceNotificationSettings> list =
await apiServices.getAllSettingsNotificationOfDevices();
// log("List: $list");
@@ -425,24 +318,16 @@ class _HomeScreenState extends State<HomeScreen> {
for (var device in devices) {
if (!thingIdsInList.contains(device.thingId)) {
log("Device with Thing ID ${device.thingId} is not in the notification settings list.");
try {
await apiServices.execute(context, () async {
await apiServices.setupDeviceNotification(
device.thingId!, device.name!);
} catch (e) {
if (!mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
});
} else {
log("All devices are in the notification settings list.");
}
}
}
isFunctionCall = true;
} catch (e) {
if (!mounted) return;
showErrorTopSnackBarCustom(context, e.toString());
});
}
isFunctionCall = true;
}
}

View File

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

View File

@@ -4,6 +4,8 @@ import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:maps_launcher/maps_launcher.dart';
import 'package:badges/badges.dart' as badges;
import '../../../product/shared/shared_rocket_container.dart';
import '../device_alias_model.dart';
import '../../../product/constant/icon/icon_constants.dart';
import '../../../product/constant/image/image_constants.dart';
@@ -21,7 +23,7 @@ Future<Widget> warningCard(BuildContext context, APIServices apiServices,
String fullLocation = "";
if (device.areaPath != "") {
fullLocation = await DeviceUtils.instance
.getFullDeviceLocation(context, device.areaPath!,"");
.getFullDeviceLocation(context, device.areaPath!, "");
}
String time = "";
for (var sensor in device.status!.sensors!) {
@@ -187,84 +189,185 @@ Future<Widget> warningCard(BuildContext context, APIServices apiServices,
),
),
SizedBox(width: context.mediumValue),
Expanded(
child: Align(
alignment: Alignment.centerRight,
child: OutlinedButton(
style: ButtonStyle(
backgroundColor:
WidgetStatePropertyAll(backgroundColor)),
onPressed: () async {
if (message ==
appLocalization(context).button_fake_fire_message) {
await showDialog(
context: context,
builder: (context) => AlertDialog(
icon: const Icon(Icons.warning),
iconColor: Colors.red,
title: Text(appLocalization(context)
.confirm_fake_fire_message),
content: Text(appLocalization(context)
.confirm_fake_fire_body),
actions: [
TextButton(
onPressed: () async {
try {
int statusCode = await apiServices
.confirmFakeFireByUser(device.thingId!);
if (statusCode == 200) {
showNoIconTopSnackBar(
context,
device.isOwner!
? Expanded(
child: Align(
alignment: Alignment.centerRight,
child: OutlinedButton(
style: ButtonStyle(
backgroundColor:
WidgetStatePropertyAll(backgroundColor)),
onPressed: () async {
if (message ==
appLocalization(context)
.button_fake_fire_message) {
await showDialog(
context: context,
builder: (context) => AlertDialog(
icon: const Icon(Icons.warning),
iconColor: Colors.red,
title: Text(appLocalization(context)
.confirm_fake_fire_message),
content: Text(appLocalization(context)
.confirm_fake_fire_body),
actions: [
TextButton(
onPressed: () async {
await apiServices.execute(context,
() async {
int statusCode = await apiServices
.confirmFakeFireByUser(
device.thingId!);
if (statusCode == 200) {
showNoIconTopSnackBar(
context,
appLocalization(context)
.notification_confirm_fake_fire_success,
Colors.green,
Colors.white);
} else {
showNoIconTopSnackBar(
context,
appLocalization(context)
.notification_confirm_fake_fire_failed,
Colors.red,
Colors.red);
}
});
Navigator.of(context).pop();
},
child: Text(
appLocalization(context)
.notification_confirm_fake_fire_success,
Colors.green,
Colors.white);
} else {
showNoIconTopSnackBar(
context,
appLocalization(context)
.notification_confirm_fake_fire_failed,
Colors.red,
Colors.red);
}
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
Navigator.of(context).pop();
},
child: Text(
appLocalization(context)
.confirm_fake_fire_sure_message,
style:
const TextStyle(color: Colors.red)),
),
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(appLocalization(context)
.cancel_button_content),
),
],
.confirm_fake_fire_sure_message,
style: const TextStyle(
color: Colors.red)),
),
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(appLocalization(context)
.cancel_button_content),
),
],
),
);
} else {
showNoIconTopSnackBar(
context,
appLocalization(context)
.let_PCCC_handle_message,
Colors.orange,
Colors.white);
}
},
child: Text(
message,
style: TextStyle(color: textColor),
),
);
} else {
showNoIconTopSnackBar(
context,
appLocalization(context).let_PCCC_handle_message,
Colors.orange,
Colors.white);
}
},
child: Text(
message,
style: TextStyle(color: textColor),
),
),
)
: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ClipPath(
clipper: SharedRocketContainer(),
child: Container(
padding: EdgeInsets.all(context.lowValue),
height: context.mediumValue,
width: context.dynamicWidth(0.22),
decoration: BoxDecoration(
color: Colors.green[300],
),
child: Text(
appLocalization(context).interfamily_page_name,
),
),
),
Expanded(
child: Align(
alignment: Alignment.centerRight,
child: OutlinedButton(
style: ButtonStyle(
backgroundColor: WidgetStatePropertyAll(
backgroundColor)),
onPressed: () async {
if (message ==
appLocalization(context)
.button_fake_fire_message) {
await showDialog(
context: context,
builder: (context) => AlertDialog(
icon: const Icon(Icons.warning),
iconColor: Colors.red,
title: Text(appLocalization(context)
.confirm_fake_fire_message),
content: Text(appLocalization(context)
.confirm_fake_fire_body),
actions: [
TextButton(
onPressed: () async {
await apiServices.execute(context,
() async {
int statusCode =
await apiServices
.confirmFakeFireByUser(
device.thingId!);
if (statusCode == 200) {
showNoIconTopSnackBar(
context,
appLocalization(context)
.notification_confirm_fake_fire_success,
Colors.green,
Colors.white);
} else {
showNoIconTopSnackBar(
context,
appLocalization(context)
.notification_confirm_fake_fire_failed,
Colors.red,
Colors.red);
}
});
Navigator.of(context).pop();
},
child: Text(
appLocalization(context)
.confirm_fake_fire_sure_message,
style: const TextStyle(
color: Colors.red)),
),
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(appLocalization(context)
.cancel_button_content),
),
],
),
);
} else {
showNoIconTopSnackBar(
context,
appLocalization(context)
.let_PCCC_handle_message,
Colors.orange,
Colors.white);
}
},
child: Text(
message,
style: TextStyle(color: textColor),
),
),
),
),
],
),
),
),
),
],
),
],

View File

@@ -5,7 +5,6 @@ import 'dart:async';
import 'package:flutter/material.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 '../../../product/base/bloc/base_bloc.dart';
import '../../../product/constant/app/app_constants.dart';
@@ -325,7 +324,7 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
Future.delayed(context.lowDuration).then(
(value) => Navigator.pop(context),
);
try {
await apiServices.execute(context, () async {
int statusCode = await apiServices
.deleteGroup(widget.group);
showSnackBarResponseByStatusCode(
@@ -335,11 +334,7 @@ class _DetailGroupScreenState extends State<DetailGroupScreen> {
.notification_delete_group_success,
appLocalization(context)
.notification_delete_group_failed);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
});
},
child: Text(
appLocalization(context)

View File

@@ -39,7 +39,7 @@ class _GroupsScreenState extends State<GroupsScreen> {
const duration = Duration(seconds: 5);
getAllGroupsTimer = Timer.periodic(
duration,
(Timer t) => interFamilyBloc.getAllGroup(widget.role),
(Timer t) => interFamilyBloc.getAllGroup(context, widget.role),
);
}
@@ -57,7 +57,7 @@ class _GroupsScreenState extends State<GroupsScreen> {
stream: interFamilyBloc.streamCurrentGroups,
builder: (context, groupsSnapshot) {
if (groupsSnapshot.data == null) {
interFamilyBloc.getAllGroup(widget.role);
interFamilyBloc.getAllGroup(context,widget.role);
return const SharedLoadingAnimation();
} else if (groupsSnapshot.data!.isEmpty) {
return Center(

View File

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

View File

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

View File

@@ -1,8 +1,6 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:developer';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@@ -10,16 +8,14 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:go_router/go_router.dart';
import 'package:badges/badges.dart' as badges;
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 '../../product/utils/permission_handler.dart';
import '../../product/permission/notification_permission.dart';
import '../../product/services/notification_services.dart';
import '../settings/profile/profile_model.dart';
import '../../product/extension/context_extension.dart';
import '../../bloc/home_bloc.dart';
import '../../product/constant/app/app_constants.dart';
import '../../product/constant/enums/app_route_enums.dart';
import '../../product/permission/location_permission.dart';
import '../../product/services/theme_services.dart';
import '../../bloc/devices_manager_bloc.dart';
import '../devices/devices_manager_screen.dart';
@@ -47,8 +43,6 @@ class MainScreen extends StatefulWidget {
State<MainScreen> createState() => _MainScreenState();
}
PersistentTabController controller = PersistentTabController(initialIndex: 0);
// @pragma('vm:entry-point')
// Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// log("Full background message payload: ${message.toMap()}");
@@ -59,8 +53,8 @@ PersistentTabController controller = PersistentTabController(initialIndex: 0);
// log("Background message handled: ${message.data['title']}");
// }
class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
PersistentTabController controller = PersistentTabController(initialIndex: 0);
APIServices apiServices = APIServices();
// final NotificationServices notificationServices = NotificationServices();
late MainBloc mainBloc;
@@ -112,17 +106,19 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification?.android;
if (notification != null && android != null ) {
if (notification != null && android != null) {
const AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails(
'your channel id', 'your channel name',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker');
AndroidNotificationDetails('your channel id', 'your channel name',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker');
const NotificationDetails platformChannelSpecifics =
NotificationDetails(android: androidPlatformChannelSpecifics);
NotificationDetails(android: androidPlatformChannelSpecifics);
flutterLocalNotificationsPlugin.show(
notification.hashCode, notification.title, notification.body, platformChannelSpecifics,
notification.hashCode,
notification.title,
notification.body,
platformChannelSpecifics,
);
}
});
@@ -151,7 +147,6 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
WidgetsBinding.instance.removeObserver(this);
}
List<PersistentBottomNavBarItem> _navBarsItems() {
return [
PersistentBottomNavBarItem(
@@ -200,7 +195,9 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
List<Widget> _buildScreens() {
return [
BlocProvider(
child: const HomeScreen(),
child: HomeScreen(
persistentTabController: controller,
),
blocBuilder: () => HomeBloc(),
),
BlocProvider(
@@ -238,7 +235,7 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
SizedBox(
width: context.lowValue,
),
Flexible( child: Text(userSnapshot.data?.name ?? ""))
Flexible(child: Text(userSnapshot.data?.name ?? ""))
],
);
}),
@@ -404,12 +401,10 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
}
Future<void> getBellNotification() async {
try{
await apiServices.execute(context, () async {
bell = await apiServices.getBellNotifications("0", "20");
mainBloc.bellBloc.add(bell);
}catch(e){
showErrorTopSnackBarCustom(context, e.toString());
}
});
}
bool checkStatus(List<BellItems> bells) {

View File

@@ -1,23 +1,20 @@
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_cluster_manager_2/google_maps_cluster_manager_2.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart'
hide ClusterManager, Cluster;
import 'package:sfm_app/feature/devices/device_model.dart';
import 'package:sfm_app/bloc/map_bloc.dart';
import 'package:sfm_app/feature/map/widget/on_tap_marker_widget.dart';
import 'package:sfm_app/product/base/bloc/base_bloc.dart';
import 'package:sfm_app/product/constant/icon/icon_constants.dart';
import 'package:sfm_app/product/permission/location_permission.dart';
import 'package:sfm_app/product/services/api_services.dart';
import 'package:sfm_app/product/utils/permission_handler.dart';
import '../../bloc/map_bloc.dart';
import '../../product/base/bloc/base_bloc.dart';
import '../../product/constant/enums/app_theme_enums.dart';
import '../../product/shared/shared_snack_bar.dart';
import '../../product/constant/icon/icon_constants.dart';
import '../../product/services/api_services.dart';
import '../../product/utils/permission_handler.dart';
import '../devices/device_model.dart';
import 'widget/on_tap_marker_widget.dart';
class MapScreen extends StatefulWidget {
const MapScreen({super.key});
@@ -39,7 +36,7 @@ class _MapScreenState extends State<MapScreen> with WidgetsBindingObserver {
APIServices apiServices = APIServices();
final streamController = StreamController<GoogleMapController>.broadcast();
List<Device> devices = [];
Completer<GoogleMapController> _controller = Completer();
final Completer<GoogleMapController> _controller = Completer();
List<String> imageAssets = [
IconConstants.instance.getIcon("normal_icon"),
IconConstants.instance.getIcon("offline_icon"),
@@ -121,7 +118,8 @@ class _MapScreenState extends State<MapScreen> with WidgetsBindingObserver {
clusterManager.updateMap();
},
polylines: {
if (polylinesSnapshot.data != null && polylinesSnapshot.data!.isNotEmpty) ... [
if (polylinesSnapshot.data != null &&
polylinesSnapshot.data!.isNotEmpty) ...[
Polyline(
polylineId: const PolylineId('router'),
points: polylinesSnapshot.data!,
@@ -189,7 +187,8 @@ class _MapScreenState extends State<MapScreen> with WidgetsBindingObserver {
position: cluster.location,
onTap: () async {
LocationPermission permission = await checkAndRequestPermission();
if (permission == LocationPermission.whileInUse || permission == LocationPermission.always) {
if (permission == LocationPermission.whileInUse ||
permission == LocationPermission.always) {
Position position = await Geolocator.getCurrentPosition();
onTapMarker(
// ignore: use_build_context_synchronously
@@ -277,23 +276,12 @@ class _MapScreenState extends State<MapScreen> with WidgetsBindingObserver {
}
void getAllMarkers() async {
try {
await apiServices.execute(context, () async {
devices.clear();
final devicesList = await apiServices.getOwnerDevices();
for (var device in devicesList) {
devices.add(device);
}
} catch (e) {
if (!mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
});
}
// Future<bool> checkLocationPermission(context) async {
// bool check = await LocationPermissionRequest.instance
// .checkLocationPermission(context);
// return check;
// }
}

View File

@@ -1,8 +1,8 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:convert';
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
import '../../../product/shared/shared_snack_bar.dart';
import '../../../bloc/device_notification_settings_bloc.dart';
import 'device_notification_settings_model.dart';
@@ -111,15 +111,12 @@ class _DeviceNotificationSettingsScreenState
}
void getNotificationSetting() async {
try {
await apiServices.execute(context, () async {
deviceNotifications =
await apiServices.getAllSettingsNotificationOfDevices();
deviceNotificationSettingsBloc.sinkListNotifications
.add(deviceNotifications);
} catch (e) {
if (!mounted) return;
showErrorTopSnackBarCustom(context, e.toString());
}
});
}
Widget listNotificationSetting(
@@ -293,13 +290,14 @@ class _DeviceNotificationSettingsScreenState
void updateDeviceNotification(String thingID, Map<String, int> notifiSettings,
bool isDataChange) async {
try {
await apiServices.execute(context, () async {
int statusCode = await apiServices.updateDeviceNotificationSettings(
thingID, notifiSettings);
if (statusCode == 200) {
showNoIconTopSnackBar(
context,
appLocalization(context).notification_update_device_settings_success,
appLocalization(context)
.notification_update_device_settings_success,
Colors.green,
Colors.white);
} else {
@@ -311,11 +309,6 @@ class _DeviceNotificationSettingsScreenState
}
isDataChange = false;
deviceNotificationSettingsBloc.sinkIsDataChange.add(isDataChange);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
});
}
}

View File

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

View File

@@ -1,10 +1,8 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.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 '../../product/constant/icon/icon_constants.dart';
import '../../product/extension/context_extension.dart';
@@ -29,7 +27,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
void initState() {
super.initState();
settingsBloc = BlocProvider.of(context);
// getUserProfile();
}
@override
@@ -41,7 +38,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
),
body: StreamBuilder<User>(
stream: settingsBloc.streamUserProfile,
// initialData: user,
builder: (context, userSnapshot) {
if (userSnapshot.data == null) {
settingsBloc.getUserProfile(context);
@@ -139,17 +135,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
);
}
// void getUserProfile() async {
// try {
// user = await apiServices.getUserDetail();
// settingsBloc.sinkUserProfile.add(user);
// } catch (e) {
// if (!mounted) return;
// showErrorTopSnackBarCustom(
// context, e.toString());
// }
// }
String getAvatarContent(String username) {
String name = "";
if (username.isNotEmpty) {

View File

@@ -1,6 +1,7 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:sfm_app/product/utils/app_logger_utils.dart';
import 'package:sfm_app/product/utils/responsive_text_utils.dart';
import '../theme/app_theme_light.dart';
@@ -169,3 +170,15 @@ extension TextStyleExtention on BuildContext {
TextStyle get headlineLargeTextStyle =>
Theme.of(this).textTheme.headlineLarge!;
}
extension FutureExtension<T> on Future<T> {
Future<T> handleApiError() async {
try {
return await this;
} catch (e) {
AppLoggerUtils.error(e.toString());
return Future.error(e);
}
}
}

View File

@@ -2,6 +2,8 @@ import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import '../utils/app_logger_utils.dart';
import '../constant/status_code/status_code_constants.dart';
import '../cache/local_manager.dart';
@@ -39,7 +41,8 @@ class NetworkManager {
Future<String> getDataFromServer(String path) async {
try {
final url = Uri.https(ApplicationConstants.DOMAIN, path);
log("[${DateTime.now().toLocal().toString().split(' ')[1]}] GET url: $url");
AppLoggerUtils.info("GET url: $url");
// log("[${DateTime.now().toLocal().toString().split(' ')[1]}] GET url: $url");
final headers = await getHeaders();
final response = await http.get(url, headers: headers).timeout(
Duration(seconds: ApplicationConstants.CALL_API_TIMEOUT),
@@ -53,7 +56,8 @@ class NetworkManager {
throw Exception('Lỗi server: ${response.statusCode}');
}
} catch (e, stackTrace) {
log('Lỗi khi lấy dữ liệu: $e, StackTrace: $stackTrace');
// AppLoggerUtils.error(message)
// log('Lỗi khi lấy dữ liệu: $e, StackTrace: $stackTrace');
throw Exception('Lỗi khi lấy dữ liệu: $e');
}
}
@@ -73,7 +77,8 @@ class NetworkManager {
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");
AppLoggerUtils.info("GET Params url: $url");
// 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),
@@ -90,16 +95,16 @@ class NetworkManager {
log('Lỗi khi lấy dữ liệu: $e, StackTrace: $stackTrace');
throw Exception('Lỗi khi lấy dữ liệu: $e');
}
final url = Uri.https(ApplicationConstants.DOMAIN, path, params);
log("[${DateTime.now().toLocal().toString().split(' ')[1]}] GET Params url: $url");
final headers = await getHeaders();
final response = await http.get(url, headers: headers);
if (response.statusCode == StatusCodeConstants.CREATED ||
response.statusCode == StatusCodeConstants.OK) {
return response.body;
} else {
return "";
}
// final url = Uri.https(ApplicationConstants.DOMAIN, path, params);
// log("[${DateTime.now().toLocal().toString().split(' ')[1]}] GET Params url: $url");
// final headers = await getHeaders();
// final response = await http.get(url, headers: headers);
// if (response.statusCode == StatusCodeConstants.CREATED ||
// response.statusCode == StatusCodeConstants.OK) {
// return response.body;
// } else {
// return "";
// }
}
/// Creates new data on the server using a POST request.
@@ -109,7 +114,8 @@ class NetworkManager {
Future<int> createDataInServer(String path, Map<String, dynamic> body) async {
try {
final url = Uri.https(ApplicationConstants.DOMAIN, path);
log("[${DateTime.now().toLocal().toString().split(' ')[1]}] POST url: $url");
AppLoggerUtils.info("POST url: $url");
// log("[${DateTime.now().toLocal().toString().split(' ')[1]}] POST url: $url");
final headers = await getHeaders();
final response = await http
.post(url, headers: headers, body: jsonEncode(body))
@@ -137,7 +143,8 @@ class NetworkManager {
Future<int> updateDataInServer(String path, Map<String, dynamic> body) async {
try {
final url = Uri.https(ApplicationConstants.DOMAIN, path);
log("[${DateTime.now().toLocal().toString().split(' ')[1]}] PUT url: $url");
AppLoggerUtils.info("PUT url: $url");
// log("[${DateTime.now().toLocal().toString().split(' ')[1]}] PUT url: $url");
final headers = await getHeaders();
final response =
await http.put(url, headers: headers, body: jsonEncode(body)).timeout(
@@ -166,7 +173,8 @@ class NetworkManager {
Future<int> deleteDataInServer(String path) async {
try {
final url = Uri.https(ApplicationConstants.DOMAIN, path);
log("[${DateTime.now().toLocal().toString().split(' ')[1]}] DELETE url: $url");
// log("[${DateTime.now().toLocal().toString().split(' ')[1]}] DELETE url: $url");
AppLoggerUtils.info("DELETE url: $url");
final headers = await getHeaders();
final response = await http.delete(url, headers: headers).timeout(
Duration(seconds: ApplicationConstants.CALL_API_TIMEOUT),

View File

@@ -5,7 +5,8 @@ import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http;
import 'package:sfm_app/product/shared/model/province_model.dart';
import '../shared/model/province_model.dart';
import '../utils/app_logger_utils.dart';
import '../../feature/device_log/device_logs_model.dart';
import '../../feature/devices/device_model.dart';
import '../../feature/home/device_alias_model.dart';
@@ -26,6 +27,8 @@ import '../constant/enums/local_keys_enums.dart';
import '../network/network_manager.dart';
class APIServices {
Map<String, String> headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
@@ -52,12 +55,10 @@ class APIServices {
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 {
Future<T> executeApiCall<T>(Future<dynamic> Function() apiCall,
{T Function(dynamic)? parser,
String errorMessage = 'Lỗi khi gọi API',
T Function(int)? statusCodeHandler}) async {
try {
final response = await apiCall().timeout(
Duration(seconds: ApplicationConstants.CALL_API_TIMEOUT),
@@ -81,11 +82,38 @@ class APIServices {
throw Exception('Dữ liệu trả về rỗng');
}
} catch (e, stackTrace) {
// log('Lỗi API: $e, StackTrace: $stackTrace');
AppLoggerUtils.error("Lỗi gọi API", e, stackTrace);
throw Exception('$errorMessage: $e');
}
}
/// Most Used Function
// Future<T> execute<T>(Future<T> Function() apiCall) async {
// try {
// return await apiCall();
// } catch (e) {
// AppLoggerUtils.error(e.toString());
// return Future.error(e);
// }
// }
Future<T> execute<T>(
BuildContext context,
Future<T> Function() apiCall, {
bool checkMounted = true,
}) async {
try {
return await apiCall();
} catch (e) {
if (checkMounted && !context.mounted) {
return Future.error('Widget not mounted');
}
showErrorTopSnackBarCustom(context, "Lỗi hệ thống");
return Future.error(e);
}
}
Future<String> login(String path, Map<String, dynamic> loginRequest) async {
final url = Uri.https(ApplicationConstants.DOMAIN, path);
final headers = await getHeaders();
@@ -229,7 +257,8 @@ class APIServices {
);
}
Future<int> updateDeviceNotificationSettings(String thingID, Map<String, int> data) async {
Future<int> updateDeviceNotificationSettings(
String thingID, Map<String, int> data) async {
Map<String, dynamic> body = {"thing_id": thingID, "notifi_settings": data};
return executeApiCall(
() => NetworkManager.instance!.updateDataInServer(
@@ -279,7 +308,7 @@ class APIServices {
.getDataFromServer(APIPathConstants.PROVINCES_PATH),
parser: (json) => Province.fromJsonDynamicList(json['items']),
errorMessage: 'Lỗi khi GET /${APIPathConstants.PROVINCES_PATH}');
;
}
Future<List<Province>> getProvincesByName(String name) async {
@@ -290,7 +319,7 @@ class APIServices {
parser: (json) => Province.fromJsonDynamicList(json['items']),
errorMessage: 'Lỗi khi GET /${APIPathConstants.PROVINCES_PATH}/$name',
);
;
}
Future<Province> getProvinceByID(String provinceID) async {

View File

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

View File

@@ -13,6 +13,7 @@ class AppThemeDark extends AppTheme {
@override
ThemeData get theme => FlexThemeData.dark(
scaffoldBackground: Colors.black,
useMaterial3: true,
scheme: FlexScheme.flutterDash,
subThemesData: const FlexSubThemesData(

View File

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

View File

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

View File

@@ -557,6 +557,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
logger:
dependency: "direct main"
description:
name: logger
sha256: be4b23575aac7ebf01f225a241eb7f6b5641eeaf43c6a8613510fc2f8cf187d1
url: "https://pub.dev"
source: hosted
version: "2.5.0"
logging:
dependency: transitive
description:

View File

@@ -48,6 +48,7 @@ dependencies:
url_launcher: ^6.3.1
app_settings: ^5.1.1
lottie: ^3.3.1
logger: ^2.5.0
dev_dependencies:
flutter_test: