333 lines
9.9 KiB
Dart

import 'dart:async';
import 'package:geolocator/geolocator.dart';
import 'package:flutter/foundation.dart';
/// Statische Geolocation Services für alle Plattformen
/// Unterstützt Android, iOS, Web, Windows, macOS und Linux
class GeolocationService {
/// Private Konstruktor um Instanziierung zu verhindern
GeolocationService._();
/// Stream Controller für kontinuierliche Position Updates
static StreamSubscription<Position>? _positionStreamSubscription;
/// Prüft ob Location Services verfügbar und aktiviert sind
static Future<bool> isLocationServiceEnabled() async {
try {
return await Geolocator.isLocationServiceEnabled();
} catch (e) {
if (kDebugMode) {
print('Fehler beim Prüfen der Location Services: $e');
}
return false;
}
}
/// Prüft die aktuellen Location Permissions
static Future<LocationPermission> checkPermission() async {
try {
return await Geolocator.checkPermission();
} catch (e) {
if (kDebugMode) {
print('Fehler beim Prüfen der Berechtigung: $e');
}
return LocationPermission.denied;
}
}
/// Fordert Location Permissions an
static Future<LocationPermission> requestPermission() async {
try {
return await Geolocator.requestPermission();
} catch (e) {
if (kDebugMode) {
print('Fehler beim Anfordern der Berechtigung: $e');
}
return LocationPermission.denied;
}
}
/// Prüft und fordert bei Bedarf Permissions an
static Future<bool> ensurePermissions() async {
// Prüfe ob Location Services aktiviert sind
bool serviceEnabled = await isLocationServiceEnabled();
if (!serviceEnabled) {
if (kDebugMode) {
print('Location Services sind nicht aktiviert');
}
return false;
}
LocationPermission permission = await checkPermission();
if (permission == LocationPermission.denied) {
permission = await requestPermission();
if (permission == LocationPermission.denied) {
if (kDebugMode) {
print('Location Permissions wurden verweigert');
}
return false;
}
}
if (permission == LocationPermission.deniedForever) {
if (kDebugMode) {
print('Location Permissions wurden dauerhaft verweigert');
}
return false;
}
return true;
}
/// Holt die aktuelle Position einmalig
///
/// [accuracy] - Gewünschte Genauigkeit (Standard: LocationAccuracy.high)
/// [timeLimit] - Timeout für die Anfrage (Standard: 10 Sekunden)
static Future<Position?> getCurrentPosition({
LocationAccuracy accuracy = LocationAccuracy.high,
Duration timeLimit = const Duration(seconds: 10),
}) async {
try {
// Prüfe Permissions
bool hasPermission = await ensurePermissions();
if (!hasPermission) {
return null;
}
// Hole aktuelle Position
Position position = await Geolocator.getCurrentPosition(
locationSettings: LocationSettings(
accuracy: accuracy,
timeLimit: timeLimit,
),
);
return position;
} catch (e) {
if (kDebugMode) {
print('Fehler beim Abrufen der aktuellen Position: $e');
}
return null;
}
}
/// Startet kontinuierliche Position Updates
///
/// [onPositionChanged] - Callback Funktion für neue Positionen
/// [onError] - Callback Funktion für Fehler
/// [accuracy] - Gewünschte Genauigkeit
/// [distanceFilter] - Minimale Distanz zwischen Updates in Metern
/// [intervalDuration] - Minimale Zeit zwischen Updates
static Future<bool> startPositionStream({
required Function(Position) onPositionChanged,
Function(String)? onError,
LocationAccuracy accuracy = LocationAccuracy.high,
int distanceFilter = 10,
Duration intervalDuration = const Duration(seconds: 5),
}) async {
try {
// Prüfe Permissions
bool hasPermission = await ensurePermissions();
if (!hasPermission) {
onError?.call('Keine Location Permissions');
return false;
}
// Stoppe vorherigen Stream falls aktiv
await stopPositionStream();
// Erstelle Location Settings basierend auf Plattform
LocationSettings locationSettings;
if (defaultTargetPlatform == TargetPlatform.android) {
locationSettings = AndroidSettings(
accuracy: accuracy,
distanceFilter: distanceFilter,
intervalDuration: intervalDuration,
forceLocationManager: false,
);
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
locationSettings = AppleSettings(
accuracy: accuracy,
distanceFilter: distanceFilter,
activityType: ActivityType.other,
pauseLocationUpdatesAutomatically: true,
showBackgroundLocationIndicator: false,
);
} else {
locationSettings = LocationSettings(
accuracy: accuracy,
distanceFilter: distanceFilter,
);
}
// Starte Position Stream
_positionStreamSubscription = Geolocator.getPositionStream(
locationSettings: locationSettings,
).listen(
onPositionChanged,
onError: (error) {
if (kDebugMode) {
print('Position Stream Fehler: $error');
}
onError?.call(error.toString());
},
);
return true;
} catch (e) {
if (kDebugMode) {
print('Fehler beim Starten des Position Streams: $e');
}
onError?.call(e.toString());
return false;
}
}
/// Stoppt kontinuierliche Position Updates
static Future<void> stopPositionStream() async {
try {
await _positionStreamSubscription?.cancel();
_positionStreamSubscription = null;
} catch (e) {
if (kDebugMode) {
print('Fehler beim Stoppen des Position Streams: $e');
}
}
}
/// Prüft ob Position Stream aktiv ist
static bool isPositionStreamActive() {
return _positionStreamSubscription != null && !_positionStreamSubscription!.isPaused;
}
/// Berechnet die Distanz zwischen zwei Positionen in Metern
static double calculateDistance({
required double startLatitude,
required double startLongitude,
required double endLatitude,
required double endLongitude,
}) {
return Geolocator.distanceBetween(
startLatitude,
startLongitude,
endLatitude,
endLongitude,
);
}
/// Berechnet die Richtung zwischen zwei Positionen in Grad
static double calculateBearing({
required double startLatitude,
required double startLongitude,
required double endLatitude,
required double endLongitude,
}) {
return Geolocator.bearingBetween(
startLatitude,
startLongitude,
endLatitude,
endLongitude,
);
}
/// Öffnet die Geräte-Einstellungen für Location Services
static Future<bool> openLocationSettings() async {
try {
return await Geolocator.openLocationSettings();
} catch (e) {
if (kDebugMode) {
print('Fehler beim Öffnen der Location Settings: $e');
}
return false;
}
}
/// Öffnet die App-Einstellungen
static Future<bool> openAppSettings() async {
try {
return await Geolocator.openAppSettings();
} catch (e) {
if (kDebugMode) {
print('Fehler beim Öffnen der App Settings: $e');
}
return false;
}
}
/// Formatiert Position zu einem lesbaren String
static String positionToString(Position position) {
return 'Lat: ${position.latitude.toStringAsFixed(6)}, '
'Lng: ${position.longitude.toStringAsFixed(6)}, '
'Genauigkeit: ${position.accuracy.toStringAsFixed(1)}m, '
'Zeit: ${position.timestamp}';
}
/// Konvertiert Position zu Map für JSON Serialisierung
static Map<String, dynamic> positionToMap(Position position) {
return {
'latitude': position.latitude,
'longitude': position.longitude,
'accuracy': position.accuracy,
'altitude': position.altitude,
'speed': position.speed,
'speedAccuracy': position.speedAccuracy,
'heading': position.heading,
'timestamp': position.timestamp.toIso8601String(),
};
}
/// Erstellt Position aus Map
static Position? positionFromMap(Map<String, dynamic> map) {
try {
return Position(
latitude: map['latitude']?.toDouble() ?? 0.0,
longitude: map['longitude']?.toDouble() ?? 0.0,
timestamp: map['timestamp'] != null
? DateTime.parse(map['timestamp'])
: DateTime.now(),
accuracy: map['accuracy']?.toDouble() ?? 0.0,
altitude: map['altitude']?.toDouble() ?? 0.0,
altitudeAccuracy: map['altitudeAccuracy']?.toDouble() ?? 0.0,
heading: map['heading']?.toDouble() ?? 0.0,
headingAccuracy: map['headingAccuracy']?.toDouble() ?? 0.0,
speed: map['speed']?.toDouble() ?? 0.0,
speedAccuracy: map['speedAccuracy']?.toDouble() ?? 0.0,
);
} catch (e) {
if (kDebugMode) {
print('Fehler beim Erstellen der Position aus Map: $e');
}
return null;
}
}
/// Cleanup Methode - sollte beim App Shutdown aufgerufen werden
static Future<void> dispose() async {
await stopPositionStream();
}
}
/// Hilfsklasse für Location Permissions Status
class LocationPermissionHelper {
static String getPermissionStatusText(LocationPermission permission) {
switch (permission) {
case LocationPermission.denied:
return 'Standort-Berechtigung verweigert';
case LocationPermission.deniedForever:
return 'Standort-Berechtigung dauerhaft verweigert';
case LocationPermission.whileInUse:
return 'Standort-Berechtigung während App-Nutzung';
case LocationPermission.always:
return 'Standort-Berechtigung immer';
default:
return 'Unbekannter Berechtigungs-Status';
}
}
static bool isPermissionGranted(LocationPermission permission) {
return permission == LocationPermission.whileInUse ||
permission == LocationPermission.always;
}
}