diff --git a/lib/bindings/login_binding.dart b/lib/bindings/login_binding.dart index e3373b0..fb6b2d5 100644 --- a/lib/bindings/login_binding.dart +++ b/lib/bindings/login_binding.dart @@ -4,7 +4,8 @@ import '../controllers/login_controller.dart'; class LoginBinding extends Bindings { @override void dependencies() { - Get.lazyPut(() => LoginController()); + // Permanent für bessere Controller-Stabilität + Get.put(LoginController(), permanent: true); } } diff --git a/lib/controllers/geolocation_controller.dart b/lib/controllers/geolocation_controller.dart index e00578e..54b4fa7 100644 --- a/lib/controllers/geolocation_controller.dart +++ b/lib/controllers/geolocation_controller.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:geolocator/geolocator.dart'; import '../../services/geolocation.dart'; @@ -34,7 +35,7 @@ class GeolocationController extends GetxController { _statusText.value = 'Position wird ermittelt...'; Position? position = await GeolocationService.getCurrentPosition(); - + if (position != null) { _currentPosition.value = position; _statusText.value = GeolocationService.positionToString(position); @@ -42,20 +43,15 @@ class GeolocationController extends GetxController { latitude: position.latitude, longitude: position.longitude, ); - if(resultData != null) { + if (resultData != null) { _ptvModel.value = PTVModel.fromJson(resultData); } else { _ptvModel.value = null; } - // Erfolgs-Snackbar anzeigen - Get.snackbar( - 'Position ermittelt', - 'Aktuelle Position wurde erfolgreich abgerufen', - snackPosition: SnackPosition.BOTTOM, - ); + debugPrint('Position ermittelt: ${_currentPosition.value}'); } else { _statusText.value = 'Position konnte nicht ermittelt werden'; - + debugPrint('Fehler beim Abrufen der Position'); // Fehler-Snackbar anzeigen Get.snackbar( 'Fehler', @@ -65,7 +61,7 @@ class GeolocationController extends GetxController { } } catch (e) { _statusText.value = 'Fehler beim Abrufen der Position: $e'; - + Get.snackbar( 'Fehler', 'Unerwarteter Fehler: $e', @@ -92,7 +88,7 @@ class GeolocationController extends GetxController { onError: (String error) { _statusText.value = 'Tracking Fehler: $error'; _isTracking.value = false; - + Get.snackbar( 'Tracking Fehler', error, @@ -106,7 +102,7 @@ class GeolocationController extends GetxController { if (success) { _isTracking.value = true; _statusText.value = 'Tracking aktiv - Warten auf erste Position...'; - + Get.snackbar( 'Tracking gestartet', 'Position wird kontinuierlich überwacht', @@ -114,7 +110,7 @@ class GeolocationController extends GetxController { ); } else { _statusText.value = 'Tracking konnte nicht gestartet werden'; - + Get.snackbar( 'Fehler', 'Tracking konnte nicht gestartet werden', @@ -123,7 +119,7 @@ class GeolocationController extends GetxController { } } catch (e) { _statusText.value = 'Fehler beim Starten des Trackings: $e'; - + Get.snackbar( 'Fehler', 'Tracking-Fehler: $e', @@ -142,7 +138,7 @@ class GeolocationController extends GetxController { await GeolocationService.stopPositionStream(); _isTracking.value = false; _statusText.value = 'Tracking gestoppt'; - + Get.snackbar( 'Tracking gestoppt', 'Position wird nicht mehr überwacht', @@ -172,14 +168,17 @@ class GeolocationController extends GetxController { _isLoading.value = true; _statusText.value = 'Berechtigungen werden geprüft...'; - LocationPermission permission = await GeolocationService.checkPermission(); + LocationPermission permission = + await GeolocationService.checkPermission(); bool serviceEnabled = await GeolocationService.isLocationServiceEnabled(); - - _statusText.value = 'Service aktiv: $serviceEnabled\n' - 'Berechtigung: ${LocationPermissionHelper.getPermissionStatusText(permission)}'; + + _statusText.value = + 'Service aktiv: $serviceEnabled\n' + 'Berechtigung: ${LocationPermissionHelper.getPermissionStatusText(permission)}'; // Detaillierte Information in Snackbar - String permissionStatus = LocationPermissionHelper.getPermissionStatusText(permission); + String permissionStatus = + LocationPermissionHelper.getPermissionStatusText(permission); Get.snackbar( 'Berechtigungs-Status', 'Location Service: ${serviceEnabled ? "Aktiv" : "Inaktiv"}\n$permissionStatus', @@ -188,7 +187,7 @@ class GeolocationController extends GetxController { ); } catch (e) { _statusText.value = 'Fehler beim Prüfen der Berechtigungen: $e'; - + Get.snackbar( 'Fehler', 'Berechtigungen konnten nicht geprüft werden: $e', @@ -203,7 +202,7 @@ class GeolocationController extends GetxController { Future openLocationSettings() async { try { bool opened = await GeolocationService.openLocationSettings(); - + if (opened) { Get.snackbar( 'Einstellungen geöffnet', @@ -230,7 +229,7 @@ class GeolocationController extends GetxController { Future openAppSettings() async { try { bool opened = await GeolocationService.openAppSettings(); - + if (opened) { Get.snackbar( 'Einstellungen geöffnet', @@ -279,4 +278,4 @@ class GeolocationController extends GetxController { _isTracking.value = false; _isLoading.value = false; } -} \ No newline at end of file +} diff --git a/lib/controllers/input_controller.dart b/lib/controllers/input_controller.dart index d610608..c228e77 100644 --- a/lib/controllers/input_controller.dart +++ b/lib/controllers/input_controller.dart @@ -8,33 +8,76 @@ import '../routes/app_routes.dart'; class InputController extends GetxController { final GeolocationController geoCtrl = Get.put(GeolocationController()); - final LoginController loginCtrl = Get.put(LoginController()); + // Lazy-get für LoginController - wird nur erstellt wenn benötigt + LoginController get loginCtrl => Get.find(); final _ptvModel = Rxn(); final _user = (Get.arguments as user_models.User).obs; final formKey = GlobalKey(); + late TextEditingController dateController; + late TextEditingController odometerController; + late TextEditingController litersController; + late TextEditingController pricePerLiterController; + late TextEditingController locationController; + late TextEditingController sumPriceController; PTVModel? get ptvModel => _ptvModel.value; user_models.User? get currentUser => _user.value; + // Sichere Getter für Controller-Text + String get safeDate => _isControllerValid(dateController) ? dateController.text : ''; + String get safeLocation => _isControllerValid(locationController) ? locationController.text : ''; + + // Hilfsmethode zur Controller-Validierung + bool _isControllerValid(TextEditingController? controller) { + try { + return controller != null && controller.text.isNotEmpty; + } catch (e) { + return false; + } + } + @override void onInit() { + _initController(); _getLocation(); super.onInit(); } + void _initController() { + dateController = TextEditingController(); + odometerController = TextEditingController(); + litersController = TextEditingController(); + pricePerLiterController = TextEditingController(); + locationController = TextEditingController(); + sumPriceController = TextEditingController(); + // init Date today to dateController + dateController.text = DateTime.now().toString().split(' ').first; + } + @override void onReady() {} @override void onClose() { + // Dispose aller TextEditingController + dateController.dispose(); + odometerController.dispose(); + litersController.dispose(); + pricePerLiterController.dispose(); + locationController.dispose(); + sumPriceController.dispose(); + geoCtrl.onClose(); - loginCtrl.onClose(); + // LoginController nicht schließen da er permanent sein könnte super.onClose(); } void _getLocation() async { await geoCtrl.getCurrentPosition().then( - (_) => {_ptvModel.value = geoCtrl.ptvModel}, + (_) => { + _ptvModel.value = geoCtrl.ptvModel, + locationController.text = geoCtrl.ptvModel?.locations?.first.formattedAddress ?? '', + }, ); update(); } @@ -43,9 +86,43 @@ class InputController extends GetxController { //AppNavigation.goToListPage } - void logout() { - loginCtrl.logout(); - //AppNavigation.toLogin(); - AppNavigation.offAllToLogin(); + void logout() async { + // Zeige Loading um weitere Interaktionen zu verhindern + Get.dialog( + const Center( + child: CircularProgressIndicator(), + ), + barrierDismissible: false, + ); + + try { + // Kurze Verzögerung um sicherzustellen dass UI Updates verarbeitet werden + await Future.delayed(const Duration(milliseconds: 100)); + + // Logout ausführen + await loginCtrl.logout(); + + // Dialog schließen + if (Get.isDialogOpen == true) { + Get.back(); + } + + // Navigation nach kurzer Verzögerung + await Future.delayed(const Duration(milliseconds: 100)); + AppNavigation.offAllToLogin(); + + } catch (e) { + // Dialog schließen bei Fehler + if (Get.isDialogOpen == true) { + Get.back(); + } + + Get.snackbar( + 'Fehler', + 'Logout fehlgeschlagen: $e', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Colors.red.shade500, + ); + } } } diff --git a/lib/controllers/login_controller.dart b/lib/controllers/login_controller.dart index 4dc64da..1d85440 100644 --- a/lib/controllers/login_controller.dart +++ b/lib/controllers/login_controller.dart @@ -24,6 +24,23 @@ class LoginController extends GetxController { account_models.Account? get account => _account.value; user_models.User? get logedInUser => _logedInUser.value; + // Sichere Getter für Controller + String get safeEmail => _isControllerValid(mailController) ? mailController.text : ''; + String get safePassword => _isControllerValid(passwordController) ? passwordController.text : ''; + String get safeName => _isControllerValid(nameController) ? nameController.text : ''; + + // Hilfsmethode zur Controller-Validierung + bool _isControllerValid(TextEditingController? controller) { + try { + if (controller == null) return false; + // Teste ob der Controller noch verwendbar ist + controller.text; + return true; + } catch (e) { + return false; + } + } + @override void onInit() { // Initialisiere TextEditingController @@ -71,6 +88,17 @@ class LoginController extends GetxController { Future login() async { logout(); try { + // Sichere Controller-Zugriffe + if (!_isControllerValid(mailController) || !_isControllerValid(passwordController)) { + Get.snackbar( + 'Fehler', + 'Formular nicht verfügbar. Bitte laden Sie die Seite neu.', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Colors.red.shade500, + ); + return; + } + var result = await _account.value!.createEmailPasswordSession( email: mailController.text, password: passwordController.text, @@ -107,13 +135,30 @@ class LoginController extends GetxController { } clearFields() { - mailController.clear(); - passwordController.clear(); - nameController.clear(); + try { + if (_isControllerValid(mailController)) mailController.clear(); + if (_isControllerValid(passwordController)) passwordController.clear(); + if (_isControllerValid(nameController)) nameController.clear(); + } catch (e) { + // Ignore dispose errors beim Clearing + } } Future register() async { try { + // Sichere Controller-Zugriffe + if (!_isControllerValid(mailController) || + !_isControllerValid(passwordController) || + !_isControllerValid(nameController)) { + Get.snackbar( + 'Fehler', + 'Formular nicht verfügbar. Bitte laden Sie die Seite neu.', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Colors.red.shade500, + ); + return; + } + await _account.value!.create( userId: ID.unique(), email: mailController.text, @@ -142,14 +187,30 @@ class LoginController extends GetxController { } Future logout() async { - await _account.value!.deleteSession(sessionId: 'current'); - _logedInUser.value = null; - Get.snackbar( - 'Erfolg', - 'Logout erfolgreich', - snackPosition: SnackPosition.BOTTOM, - backgroundColor: Colors.red.shade500, - ); + try { + await _account.value!.deleteSession(sessionId: 'current'); + _logedInUser.value = null; + + // Erfolgsmeldung nur wenn noch nicht navigiert wurde + if (Get.currentRoute != '/login') { + Get.snackbar( + 'Erfolg', + 'Logout erfolgreich', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Colors.green.shade500, + ); + } + } catch (e) { + if (Get.currentRoute != '/login') { + Get.snackbar( + 'Meldung', + 'Keine aktive Sitzung zum Abmelden', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Colors.orange.shade500, + ); + } + debugPrint('Logout error: $e'); + } } @override diff --git a/lib/pages/input/input_view.dart b/lib/pages/input/input_view.dart index 9edb7ce..bb22a6c 100644 --- a/lib/pages/input/input_view.dart +++ b/lib/pages/input/input_view.dart @@ -45,7 +45,8 @@ class InputPage extends GetView { children: [ Obx( () => inputCtrl.ptvModel != null && inputCtrl.currentUser != null - ? Text('UserEmail: ${inputCtrl.currentUser!.email}\nName: ${inputCtrl.currentUser!.name}\nFormattedAddress: ${inputCtrl.ptvModel!.locations!.first.formattedAddress}', + ? Text( + 'DateNow: ${inputCtrl.safeDate}\nUserEmail: ${inputCtrl.currentUser!.email}\nName: ${inputCtrl.currentUser!.name}\nFormattedAddress: ${inputCtrl.safeLocation}', ) : const Text('Keine Daten gefunden'), ), diff --git a/lib/widgets/my_form_field.dart b/lib/widgets/my_form_field.dart index a5a95d1..c53fd1a 100644 --- a/lib/widgets/my_form_field.dart +++ b/lib/widgets/my_form_field.dart @@ -18,8 +18,36 @@ class MyFormField extends StatelessWidget { final TextInputType keyboardType; final String? Function(String?)? validator; + // Prüft ob Controller noch verwendbar ist + bool _isControllerValid() { + try { + userController.text; + return true; + } catch (e) { + return false; + } + } + @override Widget build(BuildContext context) { + // Wenn Controller disposed ist, zeige Ersatz-Widget + if (!_isControllerValid()) { + return Container( + height: 56, + decoration: BoxDecoration( + color: fillColor, + borderRadius: BorderRadius.circular(4), + border: Border.all(color: Colors.grey), + ), + child: Center( + child: Text( + '$labelText (Controller nicht verfügbar)', + style: TextStyle(color: Colors.grey[600]), + ), + ), + ); + } + return TextFormField( autovalidateMode: AutovalidateMode.onUserInteraction, style: const TextStyle(color: Colors.black),