Fix: Error Reporter Overlay (No Overlay Widget)
Branch: fix/error-reporter-overlay
Merged into: develop
Date: March 2026
Overview
Fixed a recurring crash where the error reporting dialog itself caused a new error: "No Overlay widget found". Every time an error was detected, the error reporter tried to show a TextField in a Stack overlay — but Flutter’s TextField requires an Overlay ancestor, and none was available at that position in the widget tree.
This was the most-reported error in the app_issues table (multiple identical reports), making it a critical fix.
Root Cause
ErrorReporterOverlay was placed inside MaterialApp.router’s builder callback:
MaterialApp.router(
builder: (context, child) {
return ErrorReporterOverlay(child: child!);
},
)The builder callback wraps the Router widget (which contains the Navigator). So ErrorReporterOverlay was an ancestor of the Navigator, not a descendant. The widget tree looked like:
MaterialApp
ErrorReporterOverlay ← our widget
Stack
Router (contains Navigator)
Navigator
Overlay ← TextField needs this, but it's a DESCENDANT
TextField (in error dialog) ← no Overlay above here → crash
Flutter’s TextField uses Overlay.of(context) internally to display the selection cursor/handles. Without an Overlay in its ancestor chain, any text interaction caused a crash.
Fix
Replaced the Stack-based approach with showDialog() routed through rootNavigatorKey:
class ErrorReporterOverlay extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
ref.listen<ErrorState>(errorNotifierProvider, (previous, next) {
if (next.isVisible && !(previous?.isVisible ?? false)) {
_showErrorDialog(ref, next);
}
});
return child; // passthrough — no Stack needed
}
void _showErrorDialog(WidgetRef ref, ErrorState errorState) {
final navigatorContext = rootNavigatorKey.currentContext;
if (navigatorContext == null) return;
showDialog(context: navigatorContext, builder: (_) => _ErrorReportDialog(...));
}
}rootNavigatorKey is defined in app_router.dart and is passed as navigatorKey to GoRouter. Using it gives showDialog access to the Navigator’s context — where the Overlay is available.
What Changed
ErrorReporterOverlay: converted fromConsumerStatefulWidgetwith aStackto a leanConsumerWidgetthat usesref.listen+showDialog_ErrorReportDialog: extracted into its ownStatefulWidgetto manage theTextEditingControllerand send state- Removed unused
AppThemeimport
Architecture Notes
The ref.listen callback fires on every state change during build, but the !(previous?.isVisible ?? false) guard ensures the dialog is only triggered on the leading edge (false → true transition), preventing duplicate dialogs.
rootNavigatorKey.currentContext can theoretically be null if the dialog fires before the router is fully initialized. The null check ensures silent failure in that edge case rather than a secondary crash.