A React-inspired error boundary wrapper for Flutter that catches subtree build() errors locally, prevents full-screen red error boxes or blank app screens, and displays a localized fallback UI with optional self-healing retry logic, programmatic controllers, global configs, auto-retries, async zone error interception, custom transition animations, boundary naming for telemetry, async retry hooks, and debug stack trace inspection.
Key Features
- Localized UI Fallback: Prevents a single failing widget from crashing the rest of the application UI.
- Async & Zone Error Interception (
ErrorBoundary.async): Intercept uncaught asynchronous exceptions in Futures, button callbacks (onPressed), and asyncinitStatecalls within the subtree. - Custom Transition Animations (
transitionBuilder): Customize state transition animations (Scale, Slide, Fade, Flip) between child content and fallback UI. - Boundary Tagging (
name): Attach identifier names to boundaries for rich error telemetry in Sentry / Firebase Crashlytics. - Async Pre-Retry Hook (
onRetry): Asynchronously refresh providers or re-fetch data before rebuilding, complete with a fallback loading indicator. - Retry Cooldown (
minRetryCooldown): Rate-limit manual retry taps to prevent rapid infinite retry loops. - Self-Healing / Retry Support: Provides a
reset()callback so users can tap "Retry" to attempt rebuilding the failed subtree. - Programmatic Controller: Reset error boundaries from external logic via
ErrorBoundaryController. - Global Configuration: Wrap your app with
GlobalErrorBoundaryConfigto supply app-wide logging, fallback UIs, and filters. - Automated Retries: Self-heal transient errors automatically using
AutoRetryConfigwith exponential backoff support. - Selective Error Filtering: Pass a
shouldCatchpredicate to catch specific errors while letting others propagate up. - Debug Inspector: View expandable exception stack traces directly in the fallback UI during debug mode.
- Zero External Dependencies: Pure Flutter implementation utilizing native
ErrorWidgetinterceptors.
Getting Started
Add error_catch_boundary to your pubspec.yaml:
dependencies:
error_catch_boundary: ^1.3.0
Import the package in your Dart code:
import 'package:error_catch_boundary/error_catch_boundary.dart';
Usage Examples
1. Async & Zone Error Interception (ErrorBoundary.async)
Catch unhandled asynchronous errors thrown outside the widget build() phase (e.g. inside initState async calls, microtasks, or Futures):
ErrorBoundary.async(
name: 'AsyncFormBoundary',
onError: (details) => logToSentry(details),
child: const MyAsyncFormWidget(),
)
2. Custom Transition Animations (transitionBuilder)
Customize how the fallback UI animates into view when an error occurs:
ErrorBoundary(
transitionBuilder: (child, animation) {
return ScaleTransition(scale: animation, child: child);
},
child: const MyCardWidget(),
)
3. Global App Configuration (GlobalErrorBoundaryConfig)
Set app-wide defaults for Sentry/Crashlytics logging, transitions, and debug details:
GlobalErrorBoundaryConfig(
onError: (details) {
Sentry.captureException(
details.error,
stackTrace: details.stackTrace,
hint: Hint.withMap({'boundary': details.name ?? 'unknown'}),
);
},
showDebugDetails: kDebugMode,
child: MaterialApp(
home: const HomeScreen(),
),
)
4. Boundary Naming & Telemetry (name)
Tag individual boundaries so error monitoring services report exact failing UI components:
ErrorBoundary(
name: 'UserFeedSection',
onError: (details) {
debugPrint('Error caught in boundary: ${details.name}');
},
child: const UserFeedWidget(),
)
5. Asynchronous Pre-Retry Hook & Cooldown Rate-Limiting
Execute an asynchronous task (e.g., refresh a Riverpod/Bloc provider or re-fetch network data) before resetting, while displaying a loading spinner on the retry button:
ErrorBoundary(
name: 'UserProfileCard',
minRetryCooldown: const Duration(seconds: 2), // Rate-limit manual retries
onRetry: () async {
// Re-fetch data asynchronously before rebuilding
await ref.refresh(userProfileProvider.future);
},
child: const UserProfileCard(),
)
6. Programmatic Control (ErrorBoundaryController)
Trigger resets across one or multiple boundaries from an AppBar button or refresh handler:
final controller = ErrorBoundaryController();
// Inside your UI
ErrorBoundary(
controller: controller,
child: MyFeedWidget(),
)
// Reset programmatically
controller.reset();
7. Automated Retry Policy (AutoRetryConfig)
Automatically attempt recovery up to 3 times with 2-second intervals:
ErrorBoundary(
autoRetryConfig: const AutoRetryConfig(
maxRetries: 3,
retryInterval: Duration(seconds: 2),
enableExponentialBackoff: true,
),
child: MyAsyncDataCard(),
)
8. Custom Fallback UI
Provide a fallbackBuilder to display custom error UI tailored to your design system:
ErrorBoundary(
fallbackBuilder: (context, details, reset) {
return Container(
padding: const EdgeInsets.all(16),
color: Colors.red.shade50,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Failed to load ${details.name ?? 'item'}'),
ElevatedButton(
onPressed: reset,
child: const Text('Try Again'),
),
],
),
);
},
child: MyComplexCard(),
)
License
This project is licensed under the MIT License - see the LICENSE file for details.