Add mounted guards for BuildContext use across async gaps

This commit is contained in:
Doug Borg
2026-02-02 00:55:25 -07:00
parent 4fddd8e807
commit 3570104800
10 changed files with 26 additions and 15 deletions

View File

@@ -364,6 +364,7 @@ class AppState extends ChangeNotifier {
/// Show re-authentication dialog if needed
Future<void> checkAndPromptReauthForMessages(BuildContext context) async {
if (await needsReauthForMessages()) {
if (!context.mounted) return;
_showReauthDialog(context);
}
}

View File

@@ -146,7 +146,7 @@ class OneTimeMigrations {
debugPrint('[Migration] Stack trace: $stackTrace');
// Nuclear option: clear everything and show non-dismissible error dialog
if (context != null) {
if (context != null && context.mounted) {
NuclearResetDialog.show(context, error, stackTrace);
} else {
// If no context available, just log and hope for the best

View File

@@ -117,9 +117,8 @@ class SheetCoordinator {
controller.closed.then((_) {
_addSheetHeight = 0.0;
onStateChanged();
// Handle dismissal by canceling session if still active
final appState = context.read<AppState>();
if (appState.session != null) {
debugPrint('[SheetCoordinator] AddNodeSheet dismissed - canceling session');
appState.cancelSession();
@@ -186,9 +185,8 @@ class SheetCoordinator {
_editSheetHeight = 0.0;
_transitioningToEdit = false;
onStateChanged();
// Handle dismissal by canceling session if still active
final appState = context.read<AppState>();
if (appState.editSession != null) {
debugPrint('[SheetCoordinator] EditNodeSheet dismissed - canceling edit session');
appState.cancelEditSession();

View File

@@ -185,6 +185,7 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
// Run any needed migrations first
final versionsNeedingMigration = await ChangelogService().getVersionsNeedingMigration();
if (!mounted) return;
for (final version in versionsNeedingMigration) {
await ChangelogService().runMigration(version, appState, context);
}
@@ -209,6 +210,7 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
case PopupType.changelog:
final changelogContent = await ChangelogService().getChangelogContentForDisplay();
if (!mounted) return;
if (changelogContent != null) {
await showDialog(
context: context,
@@ -368,9 +370,11 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
);
// Reset height and clear selection when sheet is dismissed
final appState = context.read<AppState>();
controller.closed.then((_) {
if (!mounted) return;
_sheetCoordinator.resetTagSheetHeight(() => setState(() {}));
context.read<AppState>().clearSuspectedLocationSelection();
appState.clearSuspectedLocationSelection();
});
}

View File

@@ -118,7 +118,7 @@ class NodeProfilesSection extends StatelessWidget {
);
// If user chose to create custom profile, open the profile editor
if (result == 'create') {
if (result == 'create' && context.mounted) {
_createNewProfile(context);
}
// If user chose import from website, ProfileAddChoiceDialog handles opening the URL

View File

@@ -89,7 +89,8 @@ class _AddNodeSheetState extends State<AddNodeSheet> {
void _checkSubmissionGuideAndProceed(BuildContext context, AppState appState, LocalizationService locService) async {
// Check if user has seen the submission guide
final hasSeenGuide = await ChangelogService().hasSeenSubmissionGuide();
if (!context.mounted) return;
if (!hasSeenGuide) {
// Show submission guide dialog first
final shouldProceed = await showDialog<bool>(
@@ -97,13 +98,14 @@ class _AddNodeSheetState extends State<AddNodeSheet> {
barrierDismissible: false,
builder: (context) => const SubmissionGuideDialog(),
);
if (!context.mounted) return;
// If user canceled the submission guide, don't proceed with submission
if (shouldProceed != true) {
return;
}
}
// Now proceed with proximity check
_checkProximityOnly(context, appState, locService);
}

View File

@@ -185,6 +185,7 @@ class AdvancedEditOptionsSheet extends StatelessWidget {
}
// No custom scheme or app launch failed - redirect to app store
if (!context.mounted) return;
await _redirectToAppStore(context, editor);
}

View File

@@ -264,8 +264,9 @@ class _DownloadAreaDialogState extends State<DownloadAreaDialog> {
try {
final id = DateTime.now().toIso8601String().replaceAll(':', '-');
final appDocDir = await OfflineAreaService().getOfflineAreaDir();
if (!context.mounted) return;
final dir = "${appDocDir.path}/$id";
// Get current tile provider info
final appState = context.read<AppState>();
final selectedProvider = appState.selectedTileProvider;
@@ -292,6 +293,7 @@ class _DownloadAreaDialogState extends State<DownloadAreaDialog> {
builder: (context) => const DownloadStartedDialog(),
);
} catch (e) {
if (!context.mounted) return;
Navigator.pop(context);
showDialog(
context: context,

View File

@@ -92,7 +92,8 @@ class _EditNodeSheetState extends State<EditNodeSheet> {
void _checkSubmissionGuideAndProceed(BuildContext context, AppState appState, LocalizationService locService) async {
// Check if user has seen the submission guide
final hasSeenGuide = await ChangelogService().hasSeenSubmissionGuide();
if (!context.mounted) return;
if (!hasSeenGuide) {
// Show submission guide dialog first
final shouldProceed = await showDialog<bool>(
@@ -100,13 +101,14 @@ class _EditNodeSheetState extends State<EditNodeSheet> {
barrierDismissible: false,
builder: (context) => const SubmissionGuideDialog(),
);
if (!context.mounted) return;
// If user canceled the submission guide, don't proceed with submission
if (shouldProceed != true) {
return;
}
}
// Now proceed with proximity check
_checkProximityOnly(context, appState, locService);
}

View File

@@ -96,7 +96,8 @@ class NuclearResetDialog extends StatelessWidget {
// Clear all app data
await NuclearResetService.clearEverything();
if (!context.mounted) return;
// Show non-dismissible dialog
await showDialog(
context: context,