Complete refactoring SFM App Source Code

This commit is contained in:
anhtunz
2024-12-15 00:59:02 +07:00
parent caa73ca43c
commit 2e27d59278
247 changed files with 18390 additions and 0 deletions

View File

@@ -0,0 +1,255 @@
// 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/utils/response_status_utils.dart';
import '../../../product/shared/model/district_model.dart';
import '../../../product/shared/model/province_model.dart';
import '../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() async {
List<DropdownMenuItem<Province>> provincesData = [];
provincesData.clear();
sinkListProvinces.add(provincesData);
final body = await apiServices.getAllProvinces();
final data = jsonDecode(body);
List<dynamic> items = data["items"];
final provinces = Province.fromJsonDynamicList(items);
for (var province in provinces) {
provincesData.add(
DropdownMenuItem(value: province, child: Text(province.fullName!)));
}
sinkListProvinces.add(provincesData);
}
Future<void> getAllDistricts(String provinceID) async {
List<DropdownMenuItem<District>> districtsData = [];
districtsData.clear();
sinkListDistricts.add(districtsData);
final body = await apiServices.getAllDistricts(provinceID);
final data = jsonDecode(body);
List<dynamic> items = data["items"];
final districts = District.fromJsonDynamicList(items);
for (var district in districts) {
districtsData.add(
DropdownMenuItem(value: district, child: Text(district.fullName!)));
}
sinkListDistricts.add(districtsData);
}
Future<void> getAllWards(String districtID) async {
List<DropdownMenuItem<Ward>> wardsData = [];
wardsData.clear();
sinkListWards.add(wardsData);
final body = await apiServices.getAllWards(districtID);
final data = jsonDecode(body);
List<dynamic> items = data["items"];
final wards = Ward.fromJsonDynamicList(items);
for (var ward in wards) {
wardsData.add(DropdownMenuItem(value: ward, child: Text(ward.fullName!)));
}
sinkListWards.add(wardsData);
}
Future<void> getDeviceInfomation(
String thingID,
List<DropdownMenuItem<District>> districtsData,
List<DropdownMenuItem<Ward>> wardsData,
TextEditingController deviceNameController,
TextEditingController latitudeController,
TextEditingController longitudeController) async {
String body = await apiServices.getDeviceInfomation(thingID);
final data = jsonDecode(body);
Device device = Device.fromJson(data);
sinkDeviceInfo.add(device);
deviceNameController.text = device.name ?? "";
latitudeController.text = device.settings!.latitude ?? "";
longitudeController.text = device.settings!.longitude ?? "";
if (device.areaPath != "") {
List<String> areaPath = device.areaPath!.split('_');
String provinceCode = areaPath[0];
String districtCode = areaPath[1];
String wardCode = areaPath[2];
getAllDistricts(provinceCode);
getAllWards(districtCode);
final provinceResponse = await apiServices.getProvinceByID(provinceCode);
final provincesData = jsonDecode(provinceResponse);
Province province = Province.fromJson(provincesData['data']);
final districtResponse = await apiServices.getDistrictByID(districtCode);
final districtData = jsonDecode(districtResponse);
District district = District.fromJson(districtData['data']);
final wardResponse = await apiServices.getWardByID(wardCode);
final wardData = jsonDecode(wardResponse);
Ward ward = Ward.fromJson(wardData['data']);
Map<String, String> provinceData = {
"name": province.fullName!,
"code": province.code!
};
sinkProvinceData.add(provinceData);
Map<String, String> districData = {
"name": district.fullName!,
"code": district.code!,
};
sinkDistrictData.add(districData);
Map<String, String> wardMap = {
"name": ward.fullName!,
"code": ward.code!,
};
sinkWardData.add(wardMap);
}
}
Future<Province> getProvinceByName(String name) async {
final response = await apiServices.getProvincesByName(name);
final data = jsonDecode(response);
if (data != null &&
data.containsKey('items') &&
data['items'] != null &&
data['items'].isNotEmpty) {
List<dynamic> items = data['items'];
List<Province> provinces = Province.fromJsonDynamicList(items);
if (provinces.isNotEmpty) {
return provinces[0];
}
}
return Province(name: "null");
}
Future<District> getDistrictByName(String name, String provinceCode) async {
final response = await apiServices.getDistrictsByName(name);
if (response != "") {
final data = jsonDecode(response);
List<dynamic> items = data['items'];
if (items.isNotEmpty) {
List<District> districts = District.fromJsonDynamicList(items);
if (districts.isNotEmpty) {
for (var district in districts) {
if (district.provinceCode == provinceCode) {
return district;
}
}
}
}
}
return District(name: "null");
}
Future<Ward> getWardByName(String name, String districtCode) async {
final response = await apiServices.getWarsdByName(name);
final data = jsonDecode(response);
if (data != null && data['items'] != null) {
List<dynamic> items = data['items'];
if (items.isNotEmpty) {
List<Ward> wards = Ward.fromJsonDynamicList(items);
if (wards.isNotEmpty) {
for (var ward in wards) {
if (ward.districtCode == districtCode) {
return ward;
}
}
}
}
}
return Ward(name: "null");
}
Future<void> updateDevice(
BuildContext context,
String thingID,
String name,
String latitude,
String longitude,
String provinceCode,
String districtCode,
String wardCode,
) async {
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,
);
}
}

View File

@@ -0,0 +1,515 @@
import 'package:flutter/material.dart';
import 'package:search_choices/search_choices.dart';
import '../device_model.dart';
import 'device_update_bloc.dart';
import 'map_dialog.dart';
import '../../../product/base/bloc/base_bloc.dart';
import '../../../product/extention/context_extention.dart';
import '../../../product/services/api_services.dart';
import '../../../product/services/language_services.dart';
import '../../../product/shared/model/district_model.dart';
import '../../../product/shared/model/province_model.dart';
import '../../../product/shared/model/ward_model.dart';
class DeviceUpdateScreen extends StatefulWidget {
const DeviceUpdateScreen({super.key, required this.thingID});
final String thingID;
@override
State<DeviceUpdateScreen> createState() => _DeviceUpdateScreenState();
}
class _DeviceUpdateScreenState extends State<DeviceUpdateScreen> {
late DeviceUpdateBloc deviceUpdateBloc;
APIServices apiServices = APIServices();
Device device = Device();
bool isChanged = false;
TextEditingController deviceNameController = TextEditingController();
TextEditingController deviceLatitudeController = TextEditingController();
TextEditingController deviceLongitudeController = TextEditingController();
List<DropdownMenuItem<Province>> provincesData = [];
String? selectedProvince = "";
List<DropdownMenuItem<District>> districtsData = [];
String? selectedDistrict = "";
List<DropdownMenuItem<Ward>> wardsData = [];
String? selectedWard = "";
// static String provinceCode = "";
// static String districtCode = "";
// static String wardCode = "";
Map<String, String> provinceData = {};
Map<String, String> districtData = {};
Map<String, String> wardData = {};
@override
void initState() {
super.initState();
deviceUpdateBloc = BlocProvider.of(context);
deviceUpdateBloc.getAllProvinces();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text(appLocalization(context).device_update_title),
),
body: StreamBuilder<Map<String, String>>(
stream: deviceUpdateBloc.streamProvinceData,
builder: (context, provinceNameSnapshot) {
return StreamBuilder<Map<String, String>>(
stream: deviceUpdateBloc.streamDistrictData,
builder: (context, districtNameSnapshot) {
return StreamBuilder<Map<String, String>>(
stream: deviceUpdateBloc.streamWardData,
builder: (context, wardNameSnapshot) {
return SafeArea(
child: StreamBuilder<Device>(
stream: deviceUpdateBloc.streamDeviceInfo,
initialData: device,
builder: (context, deviceInfoSnapshot) {
if (deviceInfoSnapshot.data!.thingId == null) {
deviceUpdateBloc.getDeviceInfomation(
widget.thingID,
districtsData,
wardsData,
deviceNameController,
deviceLatitudeController,
deviceLongitudeController);
return const Center(
child: CircularProgressIndicator(),
);
} else {
return StreamBuilder<bool>(
stream: deviceUpdateBloc.streamIsChanged,
initialData: isChanged,
builder: (context, isChangedSnapshot) {
return SingleChildScrollView(
child: Padding(
padding: context.paddingLow,
child: Column(
mainAxisAlignment:
MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
"${appLocalization(context).input_name_device_device}:",
style: context
.titleMediumTextStyle),
Padding(
padding:
context.paddingLowVertical,
child: TextField(
onChanged: (value) {
isChangedListener();
},
textInputAction:
TextInputAction.next,
controller:
deviceNameController,
decoration: InputDecoration(
hintText: appLocalization(
context)
.input_name_device_hintText,
),
),
),
Text(
"${appLocalization(context).device_update_location}:",
style: context
.titleMediumTextStyle),
Padding(
padding:
context.paddingLowVertical,
child: Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
SizedBox(
width: context
.dynamicWidth(0.6),
child: Column(
children: [
SizedBox(
child: TextField(
onChanged: (value) {
isChangedListener();
},
textInputAction:
TextInputAction
.next,
controller:
deviceLatitudeController,
decoration:
InputDecoration(
label: Text(
"${appLocalization(context).update_device_dialog_location_longitude}:"),
hintText: appLocalization(
context)
.update_device_dialog_location_longitude_hintText,
),
),
),
Padding(
padding: context
.paddingLowVertical,
child: SizedBox(
child: TextField(
onChanged:
(value) {
isChangedListener();
},
controller:
deviceLongitudeController,
textInputAction:
TextInputAction
.next,
decoration:
InputDecoration(
label: Text(
"${appLocalization(context).update_device_dialog_location_latitude}:"),
hintText: appLocalization(
context)
.update_device_dialog_location_latitude_hintText,
),
),
),
),
],
),
),
Center(
child: IconButton.filled(
style: const ButtonStyle(
backgroundColor:
MaterialStatePropertyAll(
Colors
.lightGreen)),
// iconSize: 24,
onPressed: () async {
showMapDialog(
context,
deviceUpdateBloc,
deviceLatitudeController,
deviceLongitudeController);
},
icon: const Icon(
Icons.map_outlined,
),
),
),
SizedBox(
width: context.lowValue),
],
),
),
Text(
"${appLocalization(context).device_update_province}:",
style: context
.titleMediumTextStyle),
Padding(
padding:
context.paddingLowVertical,
child: StreamBuilder<
List<
DropdownMenuItem<
Province>>>(
stream: deviceUpdateBloc
.streamListProvinces,
builder: (dialogContext,
listProvinces) {
return Container(
decoration: BoxDecoration(
borderRadius:
const BorderRadius
.all(
Radius.circular(20),
),
border: Border.all(),
),
child: SearchChoices.single(
items:
listProvinces.data ??
provincesData,
hint: provinceNameSnapshot
.data !=
null
? Text(
provinceNameSnapshot
.data![
'name'] ??
"",
)
: appLocalization(
context)
.update_device_dialog_location_province_hintText,
searchHint: appLocalization(
context)
.update_device_dialog_location_province_searchHint,
displayClearIcon: false,
onChanged: (value) {
isChangedListener();
// provinceCode =
// value.code;
selectedProvince =
value.fullName;
provinceData['name'] =
value.fullName;
provinceData['code'] =
value.code;
deviceUpdateBloc
.sinkProvinceData
.add(provinceData);
deviceUpdateBloc
.getAllDistricts(
value.code);
selectedDistrict = "";
districtData['name'] =
selectedDistrict!;
// deviceUpdateBloc.sinkDistrictName
// .add(selectedDistrict);
deviceUpdateBloc
.sinkDistrictData
.add(districtData);
selectedWard = "";
wardData['name'] =
selectedWard!;
deviceUpdateBloc
.sinkWardData
.add(wardData);
},
isExpanded: true,
),
);
},
),
),
Text(
"${appLocalization(context).device_update_district}:",
style: context
.titleMediumTextStyle),
Padding(
padding:
context.paddingLowVertical,
child: StreamBuilder<
List<
DropdownMenuItem<
District>>>(
stream: deviceUpdateBloc
.streamListDistricts,
builder: (dialogContext,
listDistricts) {
return Container(
decoration: BoxDecoration(
borderRadius:
const BorderRadius
.all(
Radius.circular(20),
),
border: Border.all(),
),
child: SearchChoices.single(
items:
listDistricts.data ??
districtsData,
hint: districtNameSnapshot
.data !=
null
? Text(
districtNameSnapshot
.data![
'name'] ??
selectedDistrict!,
)
: appLocalization(
context)
.update_device_dialog_location_district_hintText,
searchHint: appLocalization(
context)
.update_device_dialog_location_district_searchHint,
displayClearIcon: false,
onChanged: (value) {
isChangedListener();
// districtCode =
// value.code;
selectedDistrict =
value.fullName;
districtData['name'] =
value.fullName!;
districtData['code'] =
value.code;
deviceUpdateBloc
.sinkDistrictData
.add(districtData);
deviceUpdateBloc
.getAllWards(
value.code);
selectedWard = "";
wardData['name'] =
selectedWard!;
deviceUpdateBloc
.sinkWardData
.add(wardData);
},
isExpanded: true,
),
);
},
),
),
Text(
"${appLocalization(context).device_update_ward}:",
style: context
.titleMediumTextStyle),
Padding(
padding:
context.paddingLowVertical,
child: StreamBuilder<
List<DropdownMenuItem<Ward>>>(
stream: deviceUpdateBloc
.streamListWards,
builder:
(dialogContext, listWards) {
return Container(
decoration: BoxDecoration(
borderRadius:
const BorderRadius
.all(
Radius.circular(
20)),
border: Border.all()),
child: SearchChoices.single(
items: listWards.data ??
wardsData,
hint: wardNameSnapshot
.data !=
null
? Text(
wardNameSnapshot
.data![
'name'] ??
selectedWard!,
)
: appLocalization(
context)
.update_device_dialog_location_ward_hintText,
searchHint: appLocalization(
context)
.update_device_dialog_location_ward_searchHint,
displayClearIcon: false,
onChanged: (value) {
isChangedListener();
// wardCode = value.code;
selectedWard =
value.fullName;
wardData['name'] =
value.fullName!;
wardData['code'] =
value.code!;
deviceUpdateBloc
.sinkWardData
.add(wardData);
},
isExpanded: true,
),
);
},
),
),
if (isChangedSnapshot.data == true)
Center(
child: SizedBox(
width:
context.dynamicWidth(0.6),
child: TextButton(
style: ButtonStyle(
foregroundColor:
MaterialStateProperty
.all(Colors
.white),
backgroundColor:
MaterialStateProperty
.all(Colors
.blue)),
onPressed: () async {
String provinceCode =
provinceNameSnapshot
.data![
"code"] ??
"";
String districtCode =
districtNameSnapshot
.data![
"code"] ??
"";
String wardCode =
wardNameSnapshot
.data![
"code"] ??
"";
String latitude =
deviceLatitudeController
.value.text;
String longitude =
deviceLongitudeController
.value.text;
String deviceName =
deviceNameController
.value.text;
// log("ProvinceCode: $provinceCode");
// log("DistrictCode: $districtCode");
// log("WardCode: $wardCode");
// log("Latitude: $latitude");
// log("Longitude: $longitude");
// log("Device Name: $deviceName");
await deviceUpdateBloc
.updateDevice(
context,
deviceInfoSnapshot
.data!.thingId!,
deviceName,
latitude,
longitude,
provinceCode,
districtCode,
wardCode,
);
Future.delayed(
// ignore: use_build_context_synchronously
context.lowDuration,
() {
Navigator.pop(
context);
});
},
child: Text(appLocalization(
context)
.update_button_content)),
),
),
],
),
),
);
},
);
}
},
),
);
});
});
}),
);
}
void isChangedListener() {
isChanged = true;
deviceUpdateBloc.sinkIsChanged.add(isChanged);
}
}

View File

@@ -0,0 +1,47 @@
class Geocode {
List<AddressComponent>? addressComponents;
String? formattedAddress;
Geocode({
this.addressComponents,
this.formattedAddress,
});
Geocode.fromJson(Map<String, dynamic> json) {
formattedAddress = json['formatted_address'];
if (json['address_components'] != null) {
addressComponents = [];
json['address_components'].forEach((v) {
addressComponents!.add(AddressComponent.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['formatted_address'] = formattedAddress;
if (addressComponents != null) {
data['address_components'] =
addressComponents!.map((e) => e.toJson()).toList();
}
return data;
}
}
class AddressComponent {
String? longName;
String? shortName;
List<String>? types;
AddressComponent({this.longName, this.shortName, this.types});
AddressComponent.fromJson(Map<String, dynamic> json) {
longName = json['long_name'];
shortName = json['short_name'];
types = json['types'].cast<String>();
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['long_name'] = longName;
data['short_name'] = shortName;
data['type'] = types;
return data;
}
}

View File

@@ -0,0 +1,298 @@
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'device_update_bloc.dart';
import '../../../product/constant/app/app_constants.dart';
import '../../../product/extention/context_extention.dart';
import '../../../product/services/language_services.dart';
import '../../../product/shared/find_location_maps/shared_map_search_location.dart';
import '../../../product/shared/find_location_maps/model/prediction_model.dart';
import '../../../product/shared/shared_transition.dart';
import 'geocode_model.dart';
showMapDialog(
BuildContext context,
DeviceUpdateBloc deviceUpdateBloc,
TextEditingController latitudeController,
TextEditingController longitudeController) async {
const CameraPosition defaultPosition = CameraPosition(
target: LatLng(20.985424, 105.738354),
zoom: 12,
);
TextEditingController searchLocationController = TextEditingController();
TextEditingController mapDialogLatitudeController = TextEditingController();
TextEditingController mapDialogLongitudeController = TextEditingController();
Completer<GoogleMapController> ggmapController = Completer();
final streamController = StreamController<GoogleMapController>.broadcast();
showGeneralDialog(
barrierDismissible: false,
transitionDuration: context.normalDuration,
transitionBuilder: transitionsLeftToRight,
context: context,
pageBuilder: (context, animation, secondaryAnimation) {
return StreamBuilder<Set<Marker>>(
stream: deviceUpdateBloc.streamMarkers,
builder: (context, markerSnapshot) {
if (!markerSnapshot.hasData) {
if (latitudeController.value.text != "" &&
longitudeController.value.text != "") {
double latitude = double.parse(latitudeController.text);
double longitude = double.parse(longitudeController.text);
addMarker(
LatLng(latitude, longitude),
ggmapController,
deviceUpdateBloc,
mapDialogLatitudeController,
mapDialogLongitudeController,
);
}
}
return Scaffold(
appBar: AppBar(
title: Text(appLocalization(context)
.update_device_dialog_maps_dialog_title),
centerTitle: true,
actions: [
IconButton(
onPressed: () async {
String latitude = mapDialogLatitudeController.text;
String longitude = mapDialogLongitudeController.text;
log("Finish -- Latitude: $latitude, longitude: $longitude --");
getDataFromApi(latitude, longitude, deviceUpdateBloc);
latitudeController.text =
mapDialogLatitudeController.text;
longitudeController.text =
mapDialogLongitudeController.text;
bool isChange = true;
deviceUpdateBloc.sinkIsChanged.add(isChange);
Navigator.of(context).pop();
},
icon: const Icon(Icons.check))
],
),
body: Stack(
children: [
GoogleMap(
onTap: (location) async {
addMarker(
location,
ggmapController,
deviceUpdateBloc,
mapDialogLatitudeController,
mapDialogLongitudeController,
);
},
markers: markerSnapshot.data ?? {},
onMapCreated: (GoogleMapController mapController) {
ggmapController.complete(mapController);
streamController.add(mapController);
},
initialCameraPosition: defaultPosition,
),
Container(
// color: Colors.white,
height: 80,
alignment: Alignment.topCenter,
padding: const EdgeInsets.all(10),
child: StreamBuilder<TextEditingController>(
stream: deviceUpdateBloc.streamSearchLocation,
builder: (context, searchLocation) {
return NearBySearchSFM(
textInputAction: TextInputAction.done,
textEditingController:
searchLocation.data ?? searchLocationController,
googleAPIKey: ApplicationConstants.MAP_KEY,
locationLatitude: 20.985424,
locationLongitude: 105.738354,
radius: 50000,
inputDecoration: InputDecoration(
hintText: appLocalization(context)
.update_device_dialog_search_location_hint,
border: InputBorder.none),
debounceTime: 600,
isLatLngRequired: true,
getPlaceDetailWithLatLng: (Prediction prediction) {
FocusScope.of(context).unfocus();
addMarker(
LatLng(double.parse(prediction.lat!),
double.parse(prediction.lng!)),
ggmapController,
deviceUpdateBloc,
mapDialogLatitudeController,
mapDialogLongitudeController);
},
itemClick: (Prediction prediction) {
searchLocationController.text =
prediction.structuredFormatting!.mainText!;
deviceUpdateBloc.sinkSearchLocation
.add(searchLocationController);
searchLocationController.selection =
TextSelection.fromPosition(TextPosition(
offset: prediction.structuredFormatting!
.mainText!.length));
},
boxDecoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.grey,
width: 0.5,
),
borderRadius: const BorderRadius.all(
Radius.circular(20))),
);
}),
)
],
),
);
});
},
);
}
addMarker(
LatLng position,
Completer<GoogleMapController> mapController,
DeviceUpdateBloc deviceUpdateBloc,
TextEditingController mapDialogLatitudeController,
TextEditingController mapDialogLongitudeController,
) async {
log("AddMarker -- Latitude: ${position.latitude}, longitude: ${position.longitude} --");
Set<Marker> marker = {};
deviceUpdateBloc.sinkMarkers.add(marker);
Marker newMarker = Marker(
markerId: const MarkerId('value'),
position: LatLng(position.latitude, position.longitude),
icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen),
draggable: true,
onDragEnd: (position) {
mapDialogLatitudeController.text = position.latitude.toString();
mapDialogLongitudeController.text = position.longitude.toString();
},
);
marker.add(newMarker);
deviceUpdateBloc.sinkMarkers.add(marker);
mapDialogLatitudeController.text = position.latitude.toString();
mapDialogLongitudeController.text = position.longitude.toString();
updateCameraPosition(position, 14, mapController);
}
void getDataFromApi(String latitude, String longitude,
DeviceUpdateBloc deviceUpdateBloc) async {
String path =
"maps/api/geocode/json?latlng=$latitude,$longitude&language=vi&result_type=political&key=${ApplicationConstants.MAP_KEY}";
var url = Uri.parse('https://maps.googleapis.com/$path');
final response = await http.get(url);
if (response.statusCode != 200) {
log("Loi: ${response.statusCode}");
return;
}
Map<String, dynamic> data = jsonDecode(response.body);
if (!data.containsKey('results') || data['results'].isEmpty) {
log("Khong co result");
return;
}
List<dynamic> results = data['results'];
List<Geocode> geocodes =
results.map((result) => Geocode.fromJson(result)).toList();
Map<String, String> locations =
_extractLocationComponents(geocodes[0].addressComponents!);
// In ra thông tin của các location
locations.forEach((key, value) {
log("$key: $value");
});
await _processLocations(locations, deviceUpdateBloc);
}
Map<String, String> _extractLocationComponents(
List<AddressComponent> addressComponents) {
Map<String, String> locations = {};
for (var addressComponent in addressComponents) {
String longName = addressComponent.longName ?? "";
if (addressComponent.types!.contains('administrative_area_level_3') ||
addressComponent.types!.contains('sublocality_level_1')) {
locations['wardkey'] = longName;
} else if (addressComponent.types!
.contains('administrative_area_level_2') ||
addressComponent.types!.contains('sublocality_level_2') ||
addressComponent.types!.contains('locality')) {
locations['districtkey'] = longName;
} else if (addressComponent.types!
.contains('administrative_area_level_1')) {
locations['provincekey'] = longName;
}
}
return locations;
}
Future<void> _processLocations(
Map<String, String> locations, DeviceUpdateBloc deviceUpdateBloc) async {
String provinceNameFromAPI = locations['provincekey'] ?? "";
String districtNameFromAPI = locations['districtkey'] ?? "";
String wardNameFromAPI = locations['wardkey'] ?? "";
final province =
await deviceUpdateBloc.getProvinceByName(provinceNameFromAPI);
if (province.name != "null") {
log("Province: ${province.fullName}, ProvinceCode: ${province.code}");
deviceUpdateBloc.sinkProvinceData
.add({"code": province.code!, "name": province.fullName!});
deviceUpdateBloc.getAllProvinces();
final district = await deviceUpdateBloc.getDistrictByName(
districtNameFromAPI, province.code!);
log("Districtname: ${district.fullName}, districtCode: ${district.code}");
deviceUpdateBloc.getAllDistricts(province.code!);
if (district.name != "null") {
deviceUpdateBloc.sinkDistrictData
.add({"code": district.code!, "name": district.fullName!});
final ward =
await deviceUpdateBloc.getWardByName(wardNameFromAPI, district.code!);
log("Wardname: ${ward.fullName}, WardCode: ${ward.code}");
deviceUpdateBloc.getAllWards(district.code!);
if (ward.name != "null") {
log("Xac dinh duoc het thong tin tu toa do");
deviceUpdateBloc.sinkWardData
.add({"code": ward.code!, "name": ward.fullName!});
} else {
deviceUpdateBloc.sinkWardData.add({});
}
} else {
deviceUpdateBloc.sinkDistrictData.add({});
}
} else {
deviceUpdateBloc.sinkProvinceData.add({});
}
}
Future<void> updateCameraPosition(LatLng location, double zoom,
Completer<GoogleMapController> mapController) async {
final CameraPosition cameraPosition = CameraPosition(
target: LatLng(
location.latitude,
location.longitude,
),
zoom: zoom,
);
final GoogleMapController mapControllerNew = await mapController.future;
mapControllerNew.animateCamera(
CameraUpdate.newCameraPosition(
cameraPosition,
),
);
}