maps/mobile/lib/features/map/providers/map_provider.dart
2026-04-05 18:05:14 +02:00

157 lines
4 KiB
Dart

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<MapState> {
@override
MapState build() => MapState(
center: AppConstants.defaultCenter,
zoom: AppConstants.defaultZoom,
isLocating: true,
);
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<void> 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, MapState>(MapNotifier.new);