-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(process-modal): refactor modal handling to use overlay for better…
… state management
- Loading branch information
Showing
1 changed file
with
26 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,75 +1,43 @@ | ||
import 'dart:async'; | ||
import 'package:flutter/material.dart'; | ||
import 'package:pi_hole_client/functions/logger.dart'; | ||
|
||
import 'package:pi_hole_client/widgets/process_dialog.dart'; | ||
|
||
class ProcessModal { | ||
final BuildContext context; | ||
|
||
bool _isOpenModal = false; | ||
Timer? _closeTimer; | ||
BuildContext? _dialogContext; | ||
OverlayEntry? _overlayEntry; | ||
|
||
ProcessModal({required this.context}); | ||
|
||
/// Opens the modal. If other modals are open, they are closed first. | ||
/// Opens the process dialog as an overlay. | ||
void open(String message) { | ||
if (_isOpenModal) return; | ||
|
||
_isOpenModal = true; | ||
logger.d('Modal opened'); | ||
|
||
// Ensure other modals are closed before opening ProcessModal. | ||
Future.delayed(Duration.zero, () { | ||
if (!context.mounted) return; | ||
if (_overlayEntry != null) return; // If already open, do nothing | ||
|
||
showDialog( | ||
context: context, | ||
builder: (dialogContext) { | ||
_dialogContext = dialogContext; | ||
return ProcessDialog(message: message); | ||
}, | ||
barrierDismissible: false, | ||
useSafeArea: true, | ||
).then((_) { | ||
if (_isOpenModal) { | ||
_resetState(); | ||
logger.d('Modal closed via then'); | ||
} | ||
}); | ||
|
||
// TODO: Close the modal reliably without a timer | ||
// Schedule a timer to close the modal after 1 seconds. | ||
// If `close()` fails for any reason, this prevents the loading from continuing indefinitely. | ||
_closeTimer = Timer(const Duration(milliseconds: 1000), close); | ||
}); | ||
_overlayEntry = _createOverlayEntry(message); | ||
Overlay.of(context).insert(_overlayEntry!); | ||
} | ||
|
||
/// Closes the modal if it is open. | ||
/// Closes the process dialog if it's open. | ||
void close() { | ||
if (!_isOpenModal || _dialogContext == null) return; | ||
|
||
try { | ||
_closeTimer?.cancel(); | ||
_closeTimer = null; | ||
|
||
if (Navigator.of(_dialogContext!).canPop()) { | ||
Navigator.of(_dialogContext!).pop(); | ||
logger.d('Modal closed successfully'); | ||
} else { | ||
logger.w('Modal cannot pop'); | ||
} | ||
} catch (e) { | ||
logger.e('Error closing modal: $e'); | ||
} finally { | ||
_resetState(); | ||
} | ||
_overlayEntry?.remove(); | ||
_overlayEntry = null; | ||
} | ||
|
||
/// Resets the internal state of the modal. | ||
void _resetState() { | ||
_isOpenModal = false; | ||
_dialogContext = null; | ||
logger.d('Modal state reset'); | ||
/// Creates an overlay entry for the process dialog. | ||
OverlayEntry _createOverlayEntry(String message) { | ||
return OverlayEntry( | ||
builder: (context) => Stack( | ||
children: [ | ||
ModalBarrier( | ||
dismissible: false, | ||
color: Colors.black.withOpacity(0.5), | ||
), | ||
Center( | ||
child: ProcessDialog( | ||
message: message, | ||
), | ||
), | ||
], | ||
), | ||
); | ||
} | ||
} |