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 from ConsumerStatefulWidget with a Stack to a lean ConsumerWidget that uses ref.listen + showDialog
  • _ErrorReportDialog: extracted into its own StatefulWidget to manage the TextEditingController and send state
  • Removed unused AppTheme import

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.