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? _positionStreamSubscription; /// Prüft ob Location Services verfügbar und aktiviert sind static Future 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 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 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 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 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 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 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 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 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 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 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 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; } }