diff --git a/lib/content_views/task_list_page.dart b/lib/content_views/task_list_page.dart index a23c2b0..066e29c 100644 --- a/lib/content_views/task_list_page.dart +++ b/lib/content_views/task_list_page.dart @@ -257,6 +257,7 @@ class _TaskListPageState extends State with ProducerMixin { _processTaskArrivedOrFinished(TaskInfo taskInfo) { Util.logInfo('Task ${taskInfo.task.sId} called'); + if (!mounted) return; showTaskArrivedPopup(context, taskInfo, () async { DateFormat dateFormat = DateFormat("dd.MM.yyyy HH:mm"); @@ -268,7 +269,9 @@ class _TaskListPageState extends State with ProducerMixin { _sendArrivedMessage(taskInfo); - setState(() {}); + if (mounted) { + setState(() {}); + } _finishTaskProcessing(taskInfo); }); @@ -277,6 +280,7 @@ class _TaskListPageState extends State with ProducerMixin { _processCommissionNumber(TaskInfo taskInfo) { Util.logInfo('Task ${taskInfo.task.sId} called'); + if (!mounted) return; showComissionNumberPopup(context, taskInfo, (comissionNumber) async { var answer = await _createAnswerHeader(taskInfo); answer.val = comissionNumber; @@ -287,7 +291,9 @@ class _TaskListPageState extends State with ProducerMixin { _commisionNumber = comissionNumber; - setState(() {}); + if (mounted) { + setState(() {}); + } _finishTaskProcessing(taskInfo); }); @@ -316,6 +322,7 @@ class _TaskListPageState extends State with ProducerMixin { _processTakePhotos(TaskInfo taskInfo) { Util.logInfo('Task ${taskInfo.task.sId} called'); + if (!mounted) return; showTakePhotosPopup(context, taskInfo, (List? photos) async { if (photos != null) { await MyApp.isarInstance.writeTxn(() async { @@ -335,7 +342,9 @@ class _TaskListPageState extends State with ProducerMixin { taskInfo.additionalText = "Fotos aufgenommen"; taskInfo.completed = true; - setState(() {}); + if (mounted) { + setState(() {}); + } _finishTaskProcessing(taskInfo); } @@ -362,6 +371,7 @@ class _TaskListPageState extends State with ProducerMixin { void _processReceiptGiver(TaskInfo taskInfo) { Util.logInfo('Task ${taskInfo.task.sId} called'); + if (!mounted) return; showReceiptGiverPopup(context, taskInfo, (receiptGiver, selectedTourEntry) async { var answer = await _createAnswerHeader(taskInfo); answer.code = selectedTourEntry.code; @@ -371,7 +381,9 @@ class _TaskListPageState extends State with ProducerMixin { taskInfo.additionalText = receiptGiver; taskInfo.completed = true; - setState(() {}); + if (mounted) { + setState(() {}); + } _finishTaskProcessing(taskInfo); }); @@ -380,8 +392,10 @@ class _TaskListPageState extends State with ProducerMixin { void _processPickupDeliveryStatusTask(TaskInfo taskInfo) { Util.logInfo('Task ${taskInfo.task.sId} called'); + final currentContext = context; Future.delayed(const Duration(seconds: 1), () async { - showPickupDeliveryStatusPopup(context, taskInfo, (tourEntry, remark) async { + if (!mounted) return; + showPickupDeliveryStatusPopup(currentContext, taskInfo, (tourEntry, remark) async { var answer = await _createAnswerHeader(taskInfo); answer.code = tourEntry!.code; answer.val = remark; @@ -392,7 +406,9 @@ class _TaskListPageState extends State with ProducerMixin { _finishTaskProcessing(taskInfo); - setState(() {}); + if (mounted) { + setState(() {}); + } }); }); } @@ -404,8 +420,10 @@ class _TaskListPageState extends State with ProducerMixin { SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft]); + final currentContext = context; Future.delayed(const Duration(seconds: 1), () async { - showSignaturePopup(context, taskInfo, (String? signature) async { + if (!mounted) return; + showSignaturePopup(currentContext, taskInfo, (String? signature) async { MyApp.suppressAcceptDialog = false; SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); diff --git a/lib/main.dart b/lib/main.dart index 75aa08d..6290194 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -211,7 +211,7 @@ class _MainState extends State
with SingleTickerProviderStateMixin, Produc final ValueNotifier _connectionLostValueNotifier = ValueNotifier(true); - late AnimationController? _animationController = null; + late AnimationController? _animationController; late Animation _animation; Timer? _checkForNewState0JobsJobsTimer; @@ -426,20 +426,28 @@ class _MainState extends State
with SingleTickerProviderStateMixin, Produc } void _replaceContentView(ContentViewWidget widget) async { + final currentContext = context; Future.delayed(const Duration(seconds: 0), () async { contentStack.clear(); - setState(() { - _currentContent = widget; - }); + if (mounted) { + setState(() { + _currentContent = widget; + }); + } - _currentTitleWidget = await _currentContent.getHeaderWidget(context); + if (mounted) { + _currentTitleWidget = await _currentContent.getHeaderWidget(currentContext); + } - setState(() {}); + if (mounted) { + setState(() {}); + } }); } void _popContentView(int count) { + final currentContext = context; Future.delayed(const Duration(seconds: 0), () async { for (int i = 0; i < count - 1; i++) { contentStack.pop(); @@ -449,19 +457,26 @@ class _MainState extends State
with SingleTickerProviderStateMixin, Produc _currentContent = previousContentView; - _currentTitleWidget = await _currentContent.getHeaderWidget(context); + if (mounted) { + _currentTitleWidget = await _currentContent.getHeaderWidget(currentContext); + } - setState(() {}); + if (mounted) { + setState(() {}); + } }); } void _pushContentView(ContentViewWidget widget) { + final currentContext = context; Future.delayed(const Duration(seconds: 0), () async { contentStack.push(_currentContent); _currentContent = widget; - _currentTitleWidget = await _currentContent.getHeaderWidget(context); + if (mounted) { + _currentTitleWidget = await _currentContent.getHeaderWidget(currentContext); + } if (mounted) { setState(() {}); diff --git a/lib/popups/accept_jobs_popup.dart b/lib/popups/accept_jobs_popup.dart index 4aa3d2b..048f95c 100644 --- a/lib/popups/accept_jobs_popup.dart +++ b/lib/popups/accept_jobs_popup.dart @@ -198,9 +198,7 @@ Future> _fetchJobWidgets(StateSetter dialogState, Function() onFini payload: RemoveJobMessage(id: job.id), ); - if (mounted) { - setState(() {}); - } + setState(() {}); }); }), ), @@ -217,9 +215,7 @@ Future> _fetchJobWidgets(StateSetter dialogState, Function() onFini payload: RemoveJobMessage(id: job.id), ); - if (mounted) { - setState(() {}); - } + setState(() {}); }); }), ) diff --git a/lib/popups/tasks_popups/arrived_popup.dart b/lib/popups/tasks_popups/arrived_popup.dart index 5508368..4d8bbc9 100644 --- a/lib/popups/tasks_popups/arrived_popup.dart +++ b/lib/popups/tasks_popups/arrived_popup.dart @@ -6,10 +6,16 @@ showTaskArrivedPopup(BuildContext context, TaskInfo taskInfo, Function() onFinis await showDialog( context: context, builder: (BuildContext context) { - return WillPopScope( - onWillPop: () async { - return await taskDialogSecurityException(context); - }, // False will prevent and true will allow to dismiss + return PopScope( + canPop: false, + onPopInvoked: (bool didPop) async { + if (!didPop) { + final shouldPop = await taskDialogSecurityException(context); + if (shouldPop && context.mounted) { + Navigator.of(context).pop(); + } + } + }, child: AlertDialog( contentPadding: EdgeInsets.zero, shadowColor: Colors.transparent, diff --git a/lib/popups/tasks_popups/commision_number_popup.dart b/lib/popups/tasks_popups/commision_number_popup.dart index eb7a5cf..6900fb5 100644 --- a/lib/popups/tasks_popups/commision_number_popup.dart +++ b/lib/popups/tasks_popups/commision_number_popup.dart @@ -7,9 +7,15 @@ showComissionNumberPopup(BuildContext context, TaskInfo taskInfo, Function(Strin await showDialog( context: context, builder: (BuildContext context) { - return WillPopScope( - onWillPop: () async { - return await taskDialogSecurityException(context); + return PopScope( + canPop: false, + onPopInvoked: (bool didPop) async { + if (!didPop) { + final shouldPop = await taskDialogSecurityException(context); + if (shouldPop && context.mounted) { + Navigator.of(context).pop(); + } + } }, child: AlertDialog( contentPadding: EdgeInsets.zero, diff --git a/lib/popups/tasks_popups/pickup_delivery_status_popup.dart b/lib/popups/tasks_popups/pickup_delivery_status_popup.dart index a164930..9858219 100644 --- a/lib/popups/tasks_popups/pickup_delivery_status_popup.dart +++ b/lib/popups/tasks_popups/pickup_delivery_status_popup.dart @@ -18,10 +18,16 @@ showPickupDeliveryStatusPopup(BuildContext context, TaskInfo taskInfo, Function( TextEditingController controller = TextEditingController(); TextEditingController remController = TextEditingController(); - return WillPopScope( - onWillPop: () async { - return await taskDialogSecurityException(context); - }, + return PopScope( + canPop: false, + onPopInvoked: (bool didPop) async { + if (!didPop) { + final shouldPop = await taskDialogSecurityException(context); + if (shouldPop && context.mounted) { + Navigator.of(context).pop(); + } + } + }, child: AlertDialog( contentPadding: EdgeInsets.zero, shadowColor: Colors.transparent, diff --git a/lib/popups/tasks_popups/receipt_giver_popup.dart b/lib/popups/tasks_popups/receipt_giver_popup.dart index 58889d5..6f3fa9f 100644 --- a/lib/popups/tasks_popups/receipt_giver_popup.dart +++ b/lib/popups/tasks_popups/receipt_giver_popup.dart @@ -17,11 +17,17 @@ showReceiptGiverPopup(BuildContext context, TaskInfo taskInfo, Function(String, await showDialog( context: context, builder: (BuildContext context) { - return WillPopScope( - onWillPop: () async { - return await taskDialogSecurityException(context); - }, - child: AlertDialog( + return PopScope( + canPop: false, + onPopInvoked: (bool didPop) async { + if (!didPop) { + final shouldPop = await taskDialogSecurityException(context); + if (shouldPop && context.mounted) { + Navigator.of(context).pop(); + } + } + }, + child: AlertDialog( contentPadding: EdgeInsets.zero, shadowColor: Colors.transparent, backgroundColor: Colors.white, diff --git a/lib/popups/tasks_popups/signature_popup.dart b/lib/popups/tasks_popups/signature_popup.dart index be1237b..62ca833 100644 --- a/lib/popups/tasks_popups/signature_popup.dart +++ b/lib/popups/tasks_popups/signature_popup.dart @@ -8,15 +8,18 @@ showSignaturePopup(BuildContext context, TaskInfo taskInfo, Function(String?) on await showDialog( context: context, builder: (BuildContext context) { - return WillPopScope( - onWillPop: () async { - var result = await taskDialogSecurityException(context); - - if (result) { - SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); + return PopScope( + canPop: false, + onPopInvoked: (bool didPop) async { + if (!didPop) { + final shouldPop = await taskDialogSecurityException(context); + if (shouldPop) { + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); + if (context.mounted) { + Navigator.of(context).pop(); + } + } } - - return result; }, child: AlertDialog( contentPadding: EdgeInsets.zero, diff --git a/lib/popups/tasks_popups/station_notice_popup.dart b/lib/popups/tasks_popups/station_notice_popup.dart index f8916ed..867add1 100644 --- a/lib/popups/tasks_popups/station_notice_popup.dart +++ b/lib/popups/tasks_popups/station_notice_popup.dart @@ -7,11 +7,17 @@ showStationNoticePopup(BuildContext context, TaskInfo taskInfo, Function(String) await showDialog( context: context, builder: (BuildContext context) { - return WillPopScope( - onWillPop: () async { - return await taskDialogSecurityException(context); - }, - child: AlertDialog( + return PopScope( + canPop: false, + onPopInvoked: (bool didPop) async { + if (!didPop) { + final shouldPop = await taskDialogSecurityException(context); + if (shouldPop && context.mounted) { + Navigator.of(context).pop(); + } + } + }, + child: AlertDialog( contentPadding: EdgeInsets.zero, shadowColor: Colors.transparent, backgroundColor: Colors.white, diff --git a/lib/popups/tasks_popups/take_photos_popup.dart b/lib/popups/tasks_popups/take_photos_popup.dart index edc7a5e..a1ef486 100644 --- a/lib/popups/tasks_popups/take_photos_popup.dart +++ b/lib/popups/tasks_popups/take_photos_popup.dart @@ -16,10 +16,16 @@ Future showTakePhotosPopup(BuildContext context, TaskInfo taskInfo, Functi await showDialog( context: context, builder: (BuildContext context) { - return WillPopScope( - onWillPop: () async { - return await taskDialogSecurityException(context); - }, + return PopScope( + canPop: false, + onPopInvoked: (bool didPop) async { + if (!didPop) { + final shouldPop = await taskDialogSecurityException(context); + if (shouldPop && context.mounted) { + Navigator.of(context).pop(); + } + } + }, child: AlertDialog( contentPadding: EdgeInsets.zero, shadowColor: Colors.transparent, diff --git a/lib/popups/tasks_popups/zip_popup.dart b/lib/popups/tasks_popups/zip_popup.dart index 0d30164..b7b7921 100644 --- a/lib/popups/tasks_popups/zip_popup.dart +++ b/lib/popups/tasks_popups/zip_popup.dart @@ -5,11 +5,17 @@ showZipPopup(BuildContext context, String text, String buttonText, Function(Stri await showDialog( context: context, builder: (BuildContext context) { - return WillPopScope( - onWillPop: () async { - return await taskDialogSecurityException(context); - }, - child: AlertDialog( + return PopScope( + canPop: false, + onPopInvoked: (bool didPop) async { + if (!didPop) { + final shouldPop = await taskDialogSecurityException(context); + if (shouldPop && context.mounted) { + Navigator.of(context).pop(); + } + } + }, + child: AlertDialog( contentPadding: EdgeInsets.zero, shadowColor: Colors.transparent, backgroundColor: Colors.white, diff --git a/lib/tools/mqtt_core.dart b/lib/tools/mqtt_core.dart index 53b87f1..59e60b4 100644 --- a/lib/tools/mqtt_core.dart +++ b/lib/tools/mqtt_core.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:io'; import 'dart:math'; import 'package:archive/archive.dart'; @@ -274,6 +275,10 @@ class MqttCore with ProducerMixin, ConsumerMixin { _messageAcknowledgeReceived(jsonDecode(payloadString)); break; + case "command/logout": + _commandLogoutReceived(jsonDecode(payloadString)); + break; + default: Util.logInfo('Unknown topic: $topic'); break; @@ -724,6 +729,25 @@ class MqttCore with ProducerMixin, ConsumerMixin { ); } + void _commandLogoutReceived(Map payload) { + try { + String timestamp = payload['timestamp'] ?? 'Unknown timestamp'; + Util.logInfo('Command logout received with timestamp: $timestamp'); + + // Disconnect MQTT client gracefully + _disconnectMqttClient(); + + // Exit the application reliably + Future.delayed(const Duration(milliseconds: 500), () { + exit(0); + }); + } catch (ex) { + Util.logException('Error processing logout command: ${ex.toString()}'); + // Force exit even if there's an error + exit(0); + } + } + String _encapsulateMessage(String messageId, bool requiresAck, Map message) { var messageHeader = MessageHeader(); messageHeader.messageId = messageId;