Flutter Error Handling Exception Monitoring • 13 min read

Flutter Error Handling & Production Exception Monitoring

Flutter's error model is more complex than most developers realize. Errors come from three separate surfaces — the Flutter framework, Dart's async runtime, and native platform code. Miss any one of them and you ship a leaky boat. This guide covers every layer, with code patterns and Logtrics integration to monitor them all in production.

Logtrics
Logtrics Team

1. Flutter's Error Hierarchy: 3 Surfaces You Must Cover

Most Flutter apps only handle one or two of these error surfaces, leaving silent crashes in production:

Surface 1: Flutter Framework Errors

Widget build errors, layout exceptions, rendering failures. Reported via FlutterError.onError.

Example: accessing a null property in a build() method, invalid constraints in a layout

Surface 2: Async / Platform Errors

Uncaught async errors outside the Flutter widget tree. Captured via PlatformDispatcher.instance.onError.

Example: network call that throws inside a Future without a .catchError(), async isolate errors

Surface 3: Dart Zone Errors

Any uncaught synchronous or async error in the current Dart Zone. Captured with runZonedGuarded.

Example: errors in code paths not covered by the Flutter widget tree or isolate handlers

Important: You need all three to catch 100% of production errors. Missing even one surface means silent crashes that never appear in your crash reporter.

2. FlutterError.onError — Catching Framework Errors

FlutterError.onError is a static callback invoked whenever the Flutter framework detects an error. By default, it just prints to the console. Override it to report to Logtrics:

main.dart Dart
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Logtrics.initialize(apiKey: 'your-api-key');

  // Store the original Flutter error handler
  final originalOnError = FlutterError.onError;

  FlutterError.onError = (FlutterErrorDetails details) {
    // Report to Logtrics
    Logtrics.reportError(
      details.exception,
      details.stack,
      context: details.context?.toDescription(),
    );

    // In debug mode, also show the red screen
    if (kDebugMode) {
      originalOnError?.call(details);
    }
  };

  runApp(const MyApp());
}

Best practice: Always call the original handler in debug mode to preserve the red screen experience for local development. In production, silence it to avoid unwanted log spam — Logtrics has it.

3. PlatformDispatcher.instance.onError — Catching Async Errors

Introduced in Flutter 3.3, PlatformDispatcher.instance.onError catches errors from async code that runs outside the Flutter widget tree, including Future chains and isolates:

main.dart Dart
PlatformDispatcher.instance.onError = (Object error, StackTrace stack) {
  Logtrics.reportError(error, stack,
    context: 'PlatformDispatcher',
  );
  return true; // CRITICAL: return true to suppress default crash
};

Critical: Always return true from this handler. Returning false causes Flutter to crash the app after the handler runs, which is usually not what you want in production.

4. runZonedGuarded — The Zone-Based Safety Net

runZonedGuarded wraps your entire app in a Dart Zone with a custom error handler — the ultimate catch-all for any error that escapes both FlutterError and PlatformDispatcher:

main.dart — Complete production error setup Dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:logtrics/logtrics.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Logtrics.initialize(apiKey: 'your-api-key');

  // Surface 1: Flutter framework errors
  FlutterError.onError = (details) =>
    Logtrics.reportError(details.exception, details.stack);

  // Surface 2: Async / platform errors (Flutter 3.3+)
  PlatformDispatcher.instance.onError = (error, stack) {
    Logtrics.reportError(error, stack);
    return true;
  };

  // Surface 3: Zone-level catch-all
  runZonedGuarded(
    () => runApp(const MyApp()),
    (error, stack) {
      Logtrics.reportError(error, stack,
        context: 'runZonedGuarded',
      );
    },
  );
}

This pattern gives you 100% error coverage across all three surfaces. With Logtrics initialized first, every captured error is transmitted to the dashboard with full session context.

5. try/catch Best Practices in Dart

Beyond the global handlers, follow these Dart try/catch patterns for cleaner error handling in feature code:

Catch specific exception types, not Object Dart
// ❌ Too broad — hides bugs, catches programming errors
try {
  await fetchPaymentStatus();
} catch (e) {
  showError('Something went wrong');
}

// ✅ Specific — handle known failures, let bugs propagate
try {
  await fetchPaymentStatus();
} on NetworkException catch (e, stack) {
  Logtrics.warn('Payment status fetch failed', error: e);
  showRetryDialog();
} on AuthException catch (e, stack) {
  Logtrics.error('Auth expired during checkout', error: e, stackTrace: stack);
  navigateToLogin();
}

Always pass the StackTrace to your logger

The second parameter in catch (e, stack) is the stack trace. Always pass it to Logtrics.error(error: e, stackTrace: stack) — without it, the crash reporter can't show you where the error originated.

6. Connecting Error Handling to Logtrics Monitoring

Once all three error surfaces feed into Logtrics, you get a unified view of every exception across your entire user base:

In the Logtrics Dashboard

  • Group errors by type (framework / async / zone)
  • Filter by app version to spot regressions
  • See impacted user count per error group
  • AI root cause summary per crash group
  • Session logs before each crash

Enriching Error Reports

Logtrics.addBreadcrumb(

category: 'navigation',

message: 'Entered CheckoutScreen',

);

Logtrics.setExtra(

'cart_total', cartTotal,

);

7. Error Grouping & Deduplication

The same Flutter bug can produce slightly different stack traces depending on the device model, OS version, and Dart runtime version. Without smart grouping, you'd see hundreds of "different" errors that are really the same bug.

Logtrics uses AI to group errors by semantic root cause rather than exact stack trace match. This means:

Without smart grouping

312 separate "crash reports" for what is actually 1 bug in your payment flow, split across device models and OS versions

With Logtrics AI grouping

1 crash group "NullPointerException in CheckoutScreen.build" affecting 312 users — fix once, resolve all

8. Production Error Handling Best Practices

Initialize Logtrics before WidgetsFlutterBinding

Call await Logtrics.initialize() before WidgetsFlutterBinding.ensureInitialized() to ensure errors during Flutter initialization are captured.

Don't swallow errors silently

A bare catch (e) { } is one of the most dangerous patterns in Dart. Always log at minimum a warning to Logtrics, even for expected error paths. Silent errors hide bugs that compound into major production incidents.

Use custom Flutter ErrorWidget in release mode

Override ErrorWidget.builder to show a user-friendly error screen instead of the red "RenderFlex" screen. Users see a graceful fallback; Logtrics still receives the full error details.

Track error rates by Flutter version

Tag every error report with the Flutter SDK version and app build number. When you upgrade Flutter, filter Logtrics by app version to immediately see if the upgrade introduced regressions before it reaches all users.

9. FAQ

How do I catch all exceptions in a Flutter app?

Use all three handlers: FlutterError.onError for widget errors, PlatformDispatcher.instance.onError for async errors, and runZonedGuarded as a catch-all Zone. Logtrics wires all three automatically when you set captureAllErrors: true in the config.

What is FlutterError.onError used for?

It intercepts errors reported by the Flutter framework — widget build errors, rendering exceptions, and framework assertions. Override it to forward these to your crash reporter instead of just printing to the console in production.

What is runZonedGuarded in Flutter?

It runs code inside a Dart Zone — a sandboxed execution context with its own error handler. Any uncaught error that propagates to the Zone boundary is passed to the onError callback, making it a catch-all safety net for your app.

Monitor Every Flutter Exception in Production

Don't let silent exceptions erode your app's quality. Logtrics captures every Flutter error across all three surfaces and gives you AI-powered root cause analysis.