// 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.broadcast(); StreamSink get sinkDeviceInfo => deviceInfo.sink; Stream get streamDeviceInfo => deviceInfo.stream; // DeviceUpdateScreen final isChanged = StreamController.broadcast(); StreamSink get sinkIsChanged => isChanged.sink; Stream get streamIsChanged => isChanged.stream; final provinceData = StreamController>.broadcast(); StreamSink> get sinkProvinceData => provinceData.sink; Stream> get streamProvinceData => provinceData.stream; final listProvinces = StreamController>>.broadcast(); StreamSink>> get sinkListProvinces => listProvinces.sink; Stream>> get streamListProvinces => listProvinces.stream; final districtData = StreamController>.broadcast(); StreamSink> get sinkDistrictData => districtData.sink; Stream> get streamDistrictData => districtData.stream; final listDistricts = StreamController>>.broadcast(); StreamSink>> get sinkListDistricts => listDistricts.sink; Stream>> get streamListDistricts => listDistricts.stream; final wardData = StreamController>.broadcast(); StreamSink> get sinkWardData => wardData.sink; Stream> get streamWardData => wardData.stream; final listWards = StreamController>>.broadcast(); StreamSink>> get sinkListWards => listWards.sink; Stream>> get streamListWards => listWards.stream; // Show Maps in DeviceUpdateScreen final markers = StreamController>.broadcast(); StreamSink> get sinkMarkers => markers.sink; Stream> get streamMarkers => markers.stream; final searchLocation = StreamController.broadcast(); StreamSink get sinkSearchLocation => searchLocation.sink; Stream get streamSearchLocation => searchLocation.stream; @override void dispose() { // deviceInfo.done; } Future getAllProvinces(BuildContext context) async { List> provincesData = []; provincesData.clear(); sinkListProvinces.add(provincesData); try { List 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 getAllDistricts(BuildContext context, String provinceID) async { List> 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 getAllWards(BuildContext context, String districtID) async { List> 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 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 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 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 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 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 districData = { "name": "Error Loading District", "code": districtCode, }; sinkDistrictData.add(districData); } // Xử lý Ward try { Ward ward = await apiServices.getWardByID(wardCode); Map 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 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 getProvinceByName(BuildContext context, String name) async { try { List 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 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 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 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 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()); } } }