import 'dart:async'; import 'dart:io'; import 'dart:ui' as ui; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:gen_service/Models/GetInTouchListResponse.dart'; import 'package:geolocator/geolocator.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:geocoding/geocoding.dart' as geocoding; import 'package:location/location.dart' as Location; import 'package:permission_handler/permission_handler.dart'; import 'package:provider/provider.dart'; import '../Services/api_calling.dart'; class MapProvider extends ChangeNotifier { GetInTouchListResponse? _response; List _locationsList = []; final String _googleApikey = "AIzaSyBGzvgMMKwPBAANTwaoRsAnrCpiWCj8wVs"; GoogleMapController? _mapController; CameraPosition? _cameraPosition; final LatLng _startLocation = const LatLng( 17.439112226708446, 78.43292499146135, ); String _latlongs = ""; List _markers = []; List _addresses = []; Location.LocationData? _currentLocation; bool _isLocationEnabled = false; bool _hasLocationPermission = false; Timer? _timer; bool _isLoading = true; Timer? _debounceTimer; LatLng? _mapCenter; GetInTouchListResponse? get response => _response; List get locationsList => _locationsList; String get googleAPIKey => _googleApikey; GoogleMapController? get mapController => _mapController; CameraPosition? get cameraPosition => _cameraPosition; LatLng get startLocation => _startLocation; String get latlongs => _latlongs; List get markers => _markers; List get addresses => _addresses; Location.LocationData? get currentLocation => _currentLocation; bool get isLocationEnabled => _isLocationEnabled; bool get hasLocationPermission => _hasLocationPermission; bool get isLoading => _isLoading; Timer? get timer => _timer; set markers(List value) { _markers = value; if (value.isNotEmpty) { _isLoading = false; // Mark screen as loaded when markers are added } notifyListeners(); } set mapController(GoogleMapController? value) { _mapController = value; notifyListeners(); } set mapCenter(LatLng? value) { _mapCenter = value; _latlongs = value != null ? '${value.latitude},${value.longitude}' : ''; notifyListeners(); } set isLoading(bool value) { _isLoading = value; notifyListeners(); } void resetAll() { _markers = []; _addresses = []; _mapCenter = null; _isLoading = true; notifyListeners(); } Future getLocationPermission( BuildContext context, empId, session, ) async { _isLocationEnabled = await Geolocator.isLocationServiceEnabled(); LocationPermission permission = await Geolocator.checkPermission(); _hasLocationPermission = permission == LocationPermission.always || permission == LocationPermission.whileInUse; final Location.Location location = Location.Location(); bool serviceEnabled; Location.PermissionStatus permissionGranted; serviceEnabled = await location.serviceEnabled(); if (!serviceEnabled) { serviceEnabled = await location.requestService(); if (!serviceEnabled) { _isLoading = false; return; } } permissionGranted = await location.hasPermission(); if (permissionGranted == Location.PermissionStatus.denied) { permissionGranted = await location.requestPermission(); if (permissionGranted != Location.PermissionStatus.granted) { // toast(context, 'Location permission denied.'); _isLoading = false; return; } } final Location.LocationData locData = await location.getLocation(); _currentLocation = locData; if (_currentLocation != null && _mapCenter == null) { _mapCenter = LatLng( _currentLocation!.latitude!, _currentLocation!.longitude!, ); _latlongs = '${_currentLocation!.latitude},${_currentLocation!.longitude}'; _mapController?.animateCamera(CameraUpdate.newLatLng(_mapCenter!)); await nearbyServiceLocations(context, empId, session); } notifyListeners(); } void onCameraMove( BuildContext context, CameraPosition position, empId, session, ) { _timer?.cancel(); _mapCenter = position.target; _latlongs = '${_mapCenter!.latitude},${_mapCenter!.longitude}'; _timer = Timer(Duration(seconds: 1), () { nearbyServiceLocations(context, empId, session); }); notifyListeners(); } void debounce(VoidCallback callback, Duration duration) { _debounceTimer?.cancel(); _debounceTimer = Timer(duration, callback); } Future nearbyServiceLocations( BuildContext context, empId, session, ) async { try { final data = await ApiCalling.fetchGetInTouchListApi(empId, session); if (data != null) { if (data.error == "0") { _isLoading = false; _response = data; _locationsList = data.getInTouchList ?? []; await updateMarkersFromApiResponse( context, data.getInTouchList ?? [], ); if (_locationsList.isEmpty) { // toast(context, 'No leads found within the selected radius.'); } } else { // toast(context, data.message ?? 'Failed to load leads.'); _isLoading = false; notifyListeners(); } } else { // toast(context, "Something went wrong, please try again."); _isLoading = false; notifyListeners(); } } on Exception catch (e, s) { print("nearbyServiceLocations error: $e, stack: $s"); // toast(context, 'An error occurred while loading leads.'); _isLoading = false; notifyListeners(); } } Future updateMarkersFromApiResponse( BuildContext context, List locationsList, ) async { print("markers updating"); try { _markers = await createMarkersFromApiResponse(context, locationsList); _addresses.clear(); await Future.forEach(locationsList, (store) async { String address = await _getAddressFromLatLng(store.loc); _addresses.add(address); }); } catch (e) {} notifyListeners(); } Future> createMarkersFromApiResponse( BuildContext context, List locationsList, ) async { List markers = []; ByteData data = await rootBundle.load("assets/images/maps_ic.png"); Uint8List bytes = data.buffer.asUint8List(); await Future.forEach(locationsList, (leads) async { try { ui.Codec codec = await ui.instantiateImageCodec(bytes); ui.FrameInfo fi = await codec.getNextFrame(); Uint8List resizedBytes = (await fi.image.toByteData( format: ui.ImageByteFormat.png, ))!.buffer.asUint8List(); if (leads.loc != null) { if (leads.loc!.isNotEmpty) { markers.add( Marker( markerId: MarkerId(leads.id.toString()), position: parseLatLng(leads.loc), icon: BitmapDescriptor.fromBytes(resizedBytes), infoWindow: InfoWindow( title: leads.branchName ?? 'Unknown Lead', snippet: leads.address ?? 'No address available', anchor: const Offset(0.5, 1.0), ), zIndex: 100, onTap: () { _mapController?.showMarkerInfoWindow( MarkerId(leads.id.toString()), ); print( 'Marker tapped: id=${leads.id}, name=${leads.branchName}', ); }, ), ); } } } catch (e) { print("Exception at Markers: $e"); } }); return markers; } LatLng parseLatLng(String? location) { if (location != null) { List parts = location.split(','); if (parts.length == 2) { double lat = double.tryParse(parts[0]) ?? 0.0; double lng = double.tryParse(parts[1]) ?? 0.0; if (lat != 0.0 && lng != 0.0) { return LatLng(lat, lng); } } } print('Invalid location string: $location'); return const LatLng(0.0, 0.0); } Future _getAddressFromLatLng(String? location) async { if (location != null) { List parts = location.split(','); if (parts.length == 2) { double lat = double.tryParse(parts[0]) ?? 0.0; double lng = double.tryParse(parts[1]) ?? 0.0; if (lat != 0.0 && lng != 0.0) { try { List placemarks = await geocoding .placemarkFromCoordinates(lat, lng); if (placemarks.isNotEmpty) { final placemark = placemarks.first; String address = '${placemark.street ?? ''}, ' '${placemark.thoroughfare ?? ''} ' '${placemark.subLocality ?? ''}, ' '${placemark.locality ?? ''}, ' '${placemark.administrativeArea ?? ''}, ' '${placemark.subAdministrativeArea ?? ''} ' '${placemark.postalCode ?? ''}, ' '${placemark.country ?? ''}'; return address.trim(); } } catch (e) { print('Geocoding error for location $location: $e'); } } } } return "Address not found"; } }