157 lines
4 KiB
Dart
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);
|