Unified Error Handling — Silent Capture, No Automatic Dialogs

Branch: feat/unified-error-handling Merged into: develop Date: 2026-03-17


Summary

Establishes a unified, consistent error handling architecture for the entire app. All errors are captured silently and sent to Supabase — no automatic dialogs are shown. The only dialog that can appear is one explicitly triggered by the user via “Segnala un problema”.


Problem

The previous implementation had inconsistent error handling:

  • Some errors showed a dialog (ErrorReporterOverlay watched isVisible and popped a Stack-based overlay)
  • Other errors were silently swallowed
  • ErrorNotifier.reportError() mutated state while the widget tree was building, causing a cascade:
    • "Tried to modify a provider while widget tree is building" error appeared 14 times in Supabase
    • Each error triggered another error report, then that report errored — infinite loop risk

Design Decision: Silent Capture

Chose the “never show a dialog automatically” model (like Sentry / Crashlytics):

  • Automatic errors → silently captured by IssueReportService, no widget state change
  • Manual report → user taps “Segnala un problema” → dialog with a description field appears

This avoids:

  • Disrupting UX mid-flow
  • The widget-tree-is-building mutation problem
  • Inconsistent behavior (some errors show dialogs, some don’t)

Files Modified

lib/shared/notifiers/error_notifier.dart

  • reportError() no longer mutates state — calls IssueReportService directly
  • Added triggerManualReport({prefilledMessage, prefilledStack}) for user-initiated reports
  • isVisible getter always returns false (backward-compat shim)
  • isManualReportVisible is the new flag — only set by triggerManualReport()

lib/shared/widgets/error_reporter_overlay.dart

  • Rewritten from ConsumerStatefulWidget (Stack-based) to ConsumerWidget
  • Uses ref.listen to watch only isManualReportVisible
  • Dialog shown via showDialog(context: rootNavigatorKey.currentContext!) — goes through the Navigator where Overlay is available
  • Fixes “No Overlay widget found” crash (previous Stack was above the Navigator)

lib/core/observers/provider_observer.dart

  • Removed dependency on errorNotifierProvider
  • providerDidFail and didUpdateProvider (AsyncError) now call IssueReportService directly

lib/main.dart

  • FlutterError.onError → calls IssueReportService directly
  • PlatformDispatcher.instance.onError → calls IssueReportService directly
  • LogService.registerErrorReporter wired to IssueReportService

lib/features/settings/screens/help_support_screen.dart

  • “Segnala un problema” button now calls ref.read(errorNotifierProvider.notifier).triggerManualReport()

Error Coverage

After this change, errors are captured from all four layers:

LayerMechanism
Flutter frameworkFlutterError.onError
Uncaught async DartPlatformDispatcher.instance.onError
Riverpod providersGlobalProviderObserverproviderDidFail
Service catch blocksLogService.instance.log() → registered reporter

Errors Resolved in Database

  • "Tried to modify a provider while widget tree is building" (×14)
  • "No Overlay widget found" (×3 — caused by the old Stack-based overlay)