Files
sfm_app_final/lib/bloc/device_update_bloc.dart
anhtunz 3a8fa3633c update
feat(api_service): Update try-catch funtion and handle exception
update(loading_animation): Update loading animation using Lottie
2025-06-09 14:29:43 +07:00

323 lines
11 KiB
Dart

// ignore_for_file: use_build_context_synchronously
import 'dart:async';
import 'dart:convert';
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';
import '../product/base/bloc/base_bloc.dart';
class DeviceUpdateBloc extends BlocBase {
APIServices apiServices = APIServices();
final deviceInfo = StreamController<Device>.broadcast();
StreamSink<Device> get sinkDeviceInfo => deviceInfo.sink;
Stream<Device> get streamDeviceInfo => deviceInfo.stream;
// DeviceUpdateScreen
final isChanged = StreamController<bool>.broadcast();
StreamSink<bool> get sinkIsChanged => isChanged.sink;
Stream<bool> get streamIsChanged => isChanged.stream;
final provinceData = StreamController<Map<String, String>>.broadcast();
StreamSink<Map<String, String>> get sinkProvinceData => provinceData.sink;
Stream<Map<String, String>> get streamProvinceData => provinceData.stream;
final listProvinces =
StreamController<List<DropdownMenuItem<Province>>>.broadcast();
StreamSink<List<DropdownMenuItem<Province>>> get sinkListProvinces =>
listProvinces.sink;
Stream<List<DropdownMenuItem<Province>>> get streamListProvinces =>
listProvinces.stream;
final districtData = StreamController<Map<String, String>>.broadcast();
StreamSink<Map<String, String>> get sinkDistrictData => districtData.sink;
Stream<Map<String, String>> get streamDistrictData => districtData.stream;
final listDistricts =
StreamController<List<DropdownMenuItem<District>>>.broadcast();
StreamSink<List<DropdownMenuItem<District>>> get sinkListDistricts =>
listDistricts.sink;
Stream<List<DropdownMenuItem<District>>> get streamListDistricts =>
listDistricts.stream;
final wardData = StreamController<Map<String, String>>.broadcast();
StreamSink<Map<String, String>> get sinkWardData => wardData.sink;
Stream<Map<String, String>> get streamWardData => wardData.stream;
final listWards = StreamController<List<DropdownMenuItem<Ward>>>.broadcast();
StreamSink<List<DropdownMenuItem<Ward>>> get sinkListWards => listWards.sink;
Stream<List<DropdownMenuItem<Ward>>> get streamListWards => listWards.stream;
// Show Maps in DeviceUpdateScreen
final markers = StreamController<Set<Marker>>.broadcast();
StreamSink<Set<Marker>> get sinkMarkers => markers.sink;
Stream<Set<Marker>> get streamMarkers => markers.stream;
final searchLocation = StreamController<TextEditingController>.broadcast();
StreamSink<TextEditingController> get sinkSearchLocation =>
searchLocation.sink;
Stream<TextEditingController> get streamSearchLocation =>
searchLocation.stream;
@override
void dispose() {
// deviceInfo.done;
}
Future<void> getAllProvinces(BuildContext context) async {
List<DropdownMenuItem<Province>> provincesData = [];
provincesData.clear();
sinkListProvinces.add(provincesData);
try {
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 {
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 {
final wards = await apiServices.getAllWards(districtID);
for (var ward in wards) {
wardsData.add(DropdownMenuItem(value: ward, child: Text(ward.fullName!)));
}
sinkListWards.add(wardsData);
} catch (e) {
if (!context.mounted) return;
showErrorTopSnackBarCustom(
context, e.toString());
}
}
Future<void> getDeviceInformation(
BuildContext context,
String thingID,
TextEditingController deviceNameController,
TextEditingController latitudeController,
TextEditingController longitudeController) async {
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) {
print("Lỗi khi lấy thông tin district ($districtCode): $e");
Map<String, String> districData = {
"name": "Error Loading District",
"code": districtCode,
};
sinkDistrictData.add(districData);
}
// Xử lý Ward
try {
Ward ward = await apiServices.getWardByID(wardCode);
Map<String, String> wardMap = {
"name": ward.fullName ?? "Unknown Ward",
"code": ward.code ?? wardCode,
};
sinkWardData.add(wardMap);
} catch (e) {
print("Lỗi khi lấy thông tin ward ($wardCode): $e");
Map<String, String> wardMap = {
"name": "Error Loading Ward",
"code": wardCode,
};
sinkWardData.add(wardMap);
}
} catch (e) {
print("Lỗi khi gọi getAllDistricts hoặc getAllWards: $e");
}
} else {
await getAllProvinces(context);
print("Một hoặc nhiều mã địa phương trống: Province: $provinceCode, District: $districtCode, Ward: $wardCode");
}
} else {
showNoIconTopSnackBar(context,"AreaPath không đủ thông tin: ${device.areaPath}",Colors.orangeAccent, Colors.white);
}
}
} catch (e) {
showNoIconTopSnackBar(context,"Lỗi trong getDeviceInfomation: $e",Colors.orangeAccent, Colors.white);
}
}
Future<Province> getProvinceByName(BuildContext context, String name) async {
try {
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 {
final districts = await apiServices.getDistrictsByName(name);
return districts.firstWhere(
(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 {
final wards = await apiServices.getWardsByName(name);
return wards.firstWhere(
(ward) => ward.districtCode == districtCode,
orElse: () => Ward(name: "null"),
);
} catch (e) {
if (context.mounted) {
showErrorTopSnackBarCustom(context, e.toString());
}
return Ward(name: "null");
}
}
Future<void> updateDevice(
BuildContext context,
String thingID,
String name,
String latitude,
String longitude,
String provinceCode,
String districtCode,
String wardCode,
) async {
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());
}
}
}