error_boundary 0.1.0 copy "error_boundary: ^0.1.0" to clipboard
error_boundary: ^0.1.0 copied to clipboard

A robust error boundary widget for Flutter that catches and handles errors gracefully with recovery strategies, error reporting, and testing utilities.

error_boundary #

pub package CI codecov License: BSD-3-Clause

A robust error boundary widget for Flutter that catches and handles errors gracefully, preventing app crashes and providing fallback UI.

Inspired by React's Error Boundaries, this package brings the same pattern to Flutter with additional features like recovery strategies, async error catching, and pluggable error reporters.

Features #

  • Catch build errors - Prevent widget build failures from crashing your app
  • Catch async errors - Optionally catch errors from Futures and Streams within the boundary
  • Custom fallback UI - Show user-friendly error screens instead of red error screens
  • Recovery strategies - Automatically retry, reset, or use custom recovery logic
  • Error reporting - Plug in Sentry, Crashlytics, or any custom reporter
  • Nested boundaries - Isolate errors to specific parts of your widget tree
  • Dev mode - Show detailed error info in development, clean UI in production
  • Testing utilities - Helpers for testing error boundary behavior

Installation #

dependencies:
  error_boundary: ^0.1.0

Quick Start #

Basic Usage #

Wrap any widget that might throw errors:

ErrorBoundary(
  child: MyWidget(),
)

Custom Fallback UI #

ErrorBoundary(
  fallback: (error, retry) => Center(
    child: Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        const Icon(Icons.error, size: 48, color: Colors.red),
        const SizedBox(height: 16),
        Text('Error: ${error.message}'),
        const SizedBox(height: 16),
        ElevatedButton(
          onPressed: retry,
          child: const Text('Retry'),
        ),
      ],
    ),
  ),
  child: MyWidget(),
)

Extension Syntax #

For cleaner code, use the extension method:

MyWidget().withErrorBoundary(
  fallback: (error, retry) => Text('Something went wrong'),
)

Recovery Strategies #

No Recovery (Default) #

Shows fallback until manual retry:

ErrorBoundary(
  recovery: const RecoveryStrategy.none(),
  child: MyWidget(),
)

Auto-Retry #

Automatically retry with exponential backoff:

ErrorBoundary(
  recovery: const RecoveryStrategy.retry(
    maxAttempts: 3,
    delay: Duration(seconds: 1),
    backoff: true, // Exponential backoff
  ),
  child: MyWidget(),
)

Reset #

Completely rebuild the child widget tree:

ErrorBoundary(
  recovery: const RecoveryStrategy.reset(),
  child: MyWidget(),
)

Custom Recovery #

Implement your own recovery logic:

ErrorBoundary(
  recovery: RecoveryStrategy.custom(
    onRecover: () async {
      await clearCache();
      return true; // Return true to retry
    },
  ),
  child: MyWidget(),
)

Error Reporting #

Console Reporter (Built-in) #

Logs errors to the console with detailed information:

ErrorBoundary(
  reporters: [ConsoleReporter()],
  child: MyWidget(),
)

Sentry Reporter (Built-in) #

Send errors to Sentry for monitoring:

// 1. Add sentry_flutter to pubspec.yaml
// dependencies:
//   sentry_flutter: ^7.0.0

// 2. Initialize Sentry in main.dart
await SentryFlutter.init(
  (options) => options.dsn = 'YOUR_DSN',
  appRunner: () => runApp(MyApp()),
);

// 3. Use the reporter
ErrorBoundary(
  reporters: [
    SentryReporter(
      captureException: Sentry.captureException,
      configureScope: Sentry.configureScope,
    ),
  ],
  child: MyWidget(),
)

With filtering:

SentryReporter(
  captureException: Sentry.captureException,
  configureScope: Sentry.configureScope,
  beforeSend: (info) {
    // Skip non-critical errors
    if (info.severity == ErrorSeverity.low) return null;
    return info;
  },
  environment: 'production',
  release: '1.0.0',
)

Firebase Crashlytics Reporter (Built-in) #

Send errors to Firebase Crashlytics:

// 1. Add firebase_crashlytics to pubspec.yaml
// dependencies:
//   firebase_crashlytics: ^3.0.0

// 2. Initialize Firebase in main.dart
await Firebase.initializeApp();

// 3. Use the reporter
final crashlytics = FirebaseCrashlytics.instance;

ErrorBoundary(
  reporters: [
    FirebaseCrashlyticsReporter(
      recordError: crashlytics.recordError,
      setCustomKey: crashlytics.setCustomKey,
      setUserIdentifier: crashlytics.setUserIdentifier,
      log: crashlytics.log,
    ),
  ],
  child: MyWidget(),
)

Custom Reporter #

Implement ErrorReporter for your own error tracking service:

class MyAnalyticsReporter implements ErrorReporter {
  @override
  Future<void> report(ErrorInfo info) async {
    await myAnalytics.trackError(
      error: info.error.toString(),
      stackTrace: info.stackTrace.toString(),
      severity: info.severity.name,
    );
  }

  @override
  void setUserIdentifier(String? userId) {
    myAnalytics.setUserId(userId);
  }

  @override
  void setCustomKey(String key, dynamic value) {
    myAnalytics.setProperty(key, value);
  }
}

Multiple Reporters #

Use multiple reporters simultaneously:

ErrorBoundary(
  reporters: [
    ConsoleReporter(),                    // Dev logging
    SentryReporter(...),                  // Error monitoring
    FirebaseCrashlyticsReporter(...),     // Crash reporting
  ],
  child: MyWidget(),
)

Or use CompositeReporter:

final reporter = CompositeReporter([
  ConsoleReporter(),
  SentryReporter(...),
]);

ErrorBoundary(
  reporters: [reporter],
  child: MyWidget(),
)

Nested Boundaries #

Error boundaries can be nested to isolate errors:

ErrorBoundary(
  onError: (e, s) => print('Outer caught: $e'),
  child: Column(
    children: [
      SafeWidget(),
      ErrorBoundary(
        child: DangerousWidget(), // Errors here won't affect SafeWidget
      ),
    ],
  ),
)

Error Bubbling #

Use shouldRethrow to bubble errors up to parent boundaries:

ErrorBoundary(
  shouldRethrow: (error) => error is CriticalError,
  child: MyWidget(),
)

Async Error Catching #

By default, errors in Futures and Streams within the boundary are caught:

ErrorBoundary(
  catchAsync: true, // Default
  child: MyAsyncWidget(),
)

Disable if you handle async errors differently:

ErrorBoundary(
  catchAsync: false,
  child: MyWidget(),
)

Testing #

The package includes testing utilities:

import 'package:error_boundary/error_boundary.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('shows fallback on error', (tester) async {
    final tracker = ErrorTracker();

    await tester.pumpWidget(
      MaterialApp(
        home: ErrorBoundary(
          onError: tracker.onError,
          child: ThrowingWidget(error: Exception('Test')),
        ),
      ),
    );

    await tester.pumpAndSettle();

    expect(tracker.hasErrors, isTrue);
    expect(tracker.lastError?.message, contains('Test'));
    expect(find.text('Something went wrong'), findsOneWidget);
  });
}

Test Helpers #

  • ErrorTracker - Captures errors for assertions
  • ThrowingWidget - Widget that throws during build
  • AsyncThrowingWidget - Widget that throws async errors

API Reference #

ErrorBoundary #

Property Type Default Description
child Widget required Widget to wrap
fallback FallbackBuilder? null Custom fallback UI builder
onError ErrorCallback? null Called when error occurs
reporters List<ErrorReporter> [] Error reporters
recovery RecoveryStrategy none() Recovery strategy
shouldRethrow ShouldRethrow? null Whether to bubble errors
catchAsync bool true Catch async errors
devMode bool? kDebugMode Show detailed errors

ErrorInfo #

Property Type Description
error Object The error object
stackTrace StackTrace Stack trace
severity ErrorSeverity low, medium, high, critical
type ErrorType build, runtime, async, etc.
source String? Error source identifier
timestamp DateTime When error occurred
context Map<String, dynamic> Additional context

Contributing #

Contributions are welcome! Please read our contributing guidelines before submitting a PR.

License #

BSD 3-Clause License - see LICENSE for details.

0
likes
140
points
35
downloads

Documentation

API reference

Publisher

verified publisherbrahimg.com

Weekly Downloads

A robust error boundary widget for Flutter that catches and handles errors gracefully with recovery strategies, error reporting, and testing utilities.

Homepage
Repository (GitHub)
View/report issues
Contributing

License

BSD-3-Clause (license)

Dependencies

flutter

More

Packages that depend on error_boundary