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 (
ErrorReporterOverlaywatchedisVisibleand popped aStack-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 — callsIssueReportServicedirectly- Added
triggerManualReport({prefilledMessage, prefilledStack})for user-initiated reports isVisiblegetter always returnsfalse(backward-compat shim)isManualReportVisibleis the new flag — only set bytriggerManualReport()
lib/shared/widgets/error_reporter_overlay.dart
- Rewritten from
ConsumerStatefulWidget(Stack-based) toConsumerWidget - Uses
ref.listento watch onlyisManualReportVisible - Dialog shown via
showDialog(context: rootNavigatorKey.currentContext!)— goes through the Navigator whereOverlayis available - Fixes “No Overlay widget found” crash (previous Stack was above the Navigator)
lib/core/observers/provider_observer.dart
- Removed dependency on
errorNotifierProvider providerDidFailanddidUpdateProvider(AsyncError) now callIssueReportServicedirectly
lib/main.dart
FlutterError.onError→ callsIssueReportServicedirectlyPlatformDispatcher.instance.onError→ callsIssueReportServicedirectlyLogService.registerErrorReporterwired toIssueReportService
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:
| Layer | Mechanism |
|---|---|
| Flutter framework | FlutterError.onError |
| Uncaught async Dart | PlatformDispatcher.instance.onError |
| Riverpod providers | GlobalProviderObserver → providerDidFail |
| Service catch blocks | LogService.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)