Files
sfm_app_final/lib/feature/devices/device_update/map_dialog.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

300 lines
12 KiB
Dart

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 '../../../bloc/device_update_bloc.dart';
import '../../../product/constant/app/app_constants.dart';
import '../../../product/extension/context_extension.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(context,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(BuildContext context,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(context,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(BuildContext context,
Map<String, String> locations, DeviceUpdateBloc deviceUpdateBloc) async {
String provinceNameFromAPI = locations['provincekey'] ?? "";
String districtNameFromAPI = locations['districtkey'] ?? "";
String wardNameFromAPI = locations['wardkey'] ?? "";
final province =
await deviceUpdateBloc.getProvinceByName(context,provinceNameFromAPI);
if (province.name != "null") {
log("Province: ${province.fullName}, ProvinceCode: ${province.code}");
deviceUpdateBloc.sinkProvinceData
.add({"code": province.code!, "name": province.fullName!});
deviceUpdateBloc.getAllProvinces(context);
final district = await deviceUpdateBloc.getDistrictByName(context,
districtNameFromAPI, province.code!);
log("Districtname: ${district.fullName}, districtCode: ${district.code}");
deviceUpdateBloc.getAllDistricts(context,province.code!);
if (district.name != "null") {
deviceUpdateBloc.sinkDistrictData
.add({"code": district.code!, "name": district.fullName!});
final ward =
await deviceUpdateBloc.getWardByName(context,wardNameFromAPI, district.code!);
log("Wardname: ${ward.fullName}, WardCode: ${ward.code}");
deviceUpdateBloc.getAllWards(context,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,
),
);
}