import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:geolocator/geolocator.dart'; import 'package:latlong2/latlong.dart'; import '../../../core/constants.dart'; /// State for the map feature. class MapState { final LatLng center; final double zoom; final LatLng? currentLocation; final SelectedPlace? selectedPlace; final bool isLocating; final String? locationError; const MapState({ required this.center, required this.zoom, this.currentLocation, this.selectedPlace, this.isLocating = false, this.locationError, }); MapState copyWith({ LatLng? center, double? zoom, LatLng? currentLocation, SelectedPlace? selectedPlace, bool? isLocating, String? locationError, bool clearSelectedPlace = false, bool clearLocationError = false, }) { return MapState( center: center ?? this.center, zoom: zoom ?? this.zoom, currentLocation: currentLocation ?? this.currentLocation, selectedPlace: clearSelectedPlace ? null : (selectedPlace ?? this.selectedPlace), isLocating: isLocating ?? this.isLocating, locationError: clearLocationError ? null : (locationError ?? this.locationError), ); } } /// A place selected on the map. class SelectedPlace { final String name; final String? address; final String? category; final double latitude; final double longitude; final int? osmId; final String? osmType; const SelectedPlace({ required this.name, this.address, this.category, required this.latitude, required this.longitude, this.osmId, this.osmType, }); } class MapNotifier extends Notifier { @override MapState build() => MapState( center: AppConstants.defaultCenter, zoom: AppConstants.defaultZoom, ); void updateCamera(LatLng center, double zoom) { state = state.copyWith(center: center, zoom: zoom); } void selectPlace(SelectedPlace place) { state = state.copyWith(selectedPlace: place); } void clearSelectedPlace() { state = state.copyWith(clearSelectedPlace: true); } Future locateUser() async { state = state.copyWith(isLocating: true, clearLocationError: true); try { bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled) { state = state.copyWith( isLocating: false, locationError: 'Location services are disabled.', ); return; } LocationPermission permission = await Geolocator.checkPermission(); if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); if (permission == LocationPermission.denied) { state = state.copyWith( isLocating: false, locationError: 'Location permission denied.', ); return; } } if (permission == LocationPermission.deniedForever) { state = state.copyWith( isLocating: false, locationError: 'Location permission permanently denied.', ); return; } final position = await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.high, ); final location = LatLng(position.latitude, position.longitude); state = state.copyWith( currentLocation: location, center: location, zoom: AppConstants.poiZoom, isLocating: false, ); } catch (e) { state = state.copyWith( isLocating: false, locationError: 'Could not determine location.', ); } } void zoomIn() { final newZoom = (state.zoom + 1).clamp( AppConstants.minZoom, AppConstants.maxZoom, ); state = state.copyWith(zoom: newZoom); } void zoomOut() { final newZoom = (state.zoom - 1).clamp( AppConstants.minZoom, AppConstants.maxZoom, ); state = state.copyWith(zoom: newZoom); } } final mapProvider = NotifierProvider(MapNotifier.new);