{"id":48810847,"url":"https://github.com/brahim-guaali/error_boundary","last_synced_at":"2026-04-14T07:06:38.051Z","repository":{"id":336897104,"uuid":"1151572452","full_name":"brahim-guaali/error_boundary","owner":"brahim-guaali","description":"A robust error boundary widget for Flutter that catches and handles errors gracefully with recovery strategies, error reporting, and testing utilities.","archived":false,"fork":false,"pushed_at":"2026-03-27T19:15:48.000Z","size":39,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-11T05:15:08.066Z","etag":null,"topics":["crashlytics","dart","error-boundary","error-handling","flutter","flutter-package","sentry"],"latest_commit_sha":null,"homepage":null,"language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brahim-guaali.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-06T16:26:30.000Z","updated_at":"2026-02-06T16:48:34.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/brahim-guaali/error_boundary","commit_stats":null,"previous_names":["brahim-guaali/error_boundary"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/brahim-guaali/error_boundary","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brahim-guaali%2Ferror_boundary","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brahim-guaali%2Ferror_boundary/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brahim-guaali%2Ferror_boundary/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brahim-guaali%2Ferror_boundary/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brahim-guaali","download_url":"https://codeload.github.com/brahim-guaali/error_boundary/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brahim-guaali%2Ferror_boundary/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31785682,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-14T02:24:21.117Z","status":"ssl_error","status_checked_at":"2026-04-14T02:24:20.627Z","response_time":153,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["crashlytics","dart","error-boundary","error-handling","flutter","flutter-package","sentry"],"created_at":"2026-04-14T07:05:32.272Z","updated_at":"2026-04-14T07:06:38.043Z","avatar_url":"https://github.com/brahim-guaali.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# error_boundary\n\n[![pub package](https://img.shields.io/pub/v/error_boundary.svg)](https://pub.dev/packages/error_boundary)\n[![CI](https://github.com/brahim-guaali/error_boundary/actions/workflows/ci.yml/badge.svg)](https://github.com/brahim-guaali/error_boundary/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/brahim-guaali/error_boundary/branch/main/graph/badge.svg)](https://codecov.io/gh/brahim-guaali/error_boundary)\n[![License: BSD-3-Clause](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)\n\nA robust error boundary widget for Flutter that catches and handles errors gracefully, preventing app crashes and providing fallback UI.\n\nInspired 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.\n\n## Features\n\n- **Catch build errors** - Prevent widget build failures from crashing your app\n- **Catch async errors** - Optionally catch errors from Futures and Streams within the boundary\n- **Custom fallback UI** - Show user-friendly error screens instead of red error screens\n- **Recovery strategies** - Automatically retry, reset, or use custom recovery logic\n- **Error reporting** - Plug in Sentry, Crashlytics, or any custom reporter\n- **Nested boundaries** - Isolate errors to specific parts of your widget tree\n- **Dev mode** - Show detailed error info in development, clean UI in production\n- **Testing utilities** - Helpers for testing error boundary behavior\n\n## Installation\n\n```yaml\ndependencies:\n  error_boundary: ^0.1.0\n```\n\n## Quick Start\n\n### Basic Usage\n\nWrap any widget that might throw errors:\n\n```dart\nErrorBoundary(\n  child: MyWidget(),\n)\n```\n\n### Custom Fallback UI\n\n```dart\nErrorBoundary(\n  fallback: (error, retry) =\u003e Center(\n    child: Column(\n      mainAxisSize: MainAxisSize.min,\n      children: [\n        const Icon(Icons.error, size: 48, color: Colors.red),\n        const SizedBox(height: 16),\n        Text('Error: ${error.message}'),\n        const SizedBox(height: 16),\n        ElevatedButton(\n          onPressed: retry,\n          child: const Text('Retry'),\n        ),\n      ],\n    ),\n  ),\n  child: MyWidget(),\n)\n```\n\n### Extension Syntax\n\nFor cleaner code, use the extension method:\n\n```dart\nMyWidget().withErrorBoundary(\n  fallback: (error, retry) =\u003e Text('Something went wrong'),\n)\n```\n\n## Recovery Strategies\n\n### No Recovery (Default)\n\nShows fallback until manual retry:\n\n```dart\nErrorBoundary(\n  recovery: const RecoveryStrategy.none(),\n  child: MyWidget(),\n)\n```\n\n### Auto-Retry\n\nAutomatically retry with exponential backoff:\n\n```dart\nErrorBoundary(\n  recovery: const RecoveryStrategy.retry(\n    maxAttempts: 3,\n    delay: Duration(seconds: 1),\n    backoff: true, // Exponential backoff\n  ),\n  child: MyWidget(),\n)\n```\n\n### Reset\n\nCompletely rebuild the child widget tree:\n\n```dart\nErrorBoundary(\n  recovery: const RecoveryStrategy.reset(),\n  child: MyWidget(),\n)\n```\n\n### Custom Recovery\n\nImplement your own recovery logic:\n\n```dart\nErrorBoundary(\n  recovery: RecoveryStrategy.custom(\n    onRecover: () async {\n      await clearCache();\n      return true; // Return true to retry\n    },\n  ),\n  child: MyWidget(),\n)\n```\n\n## Error Reporting\n\n### Console Reporter (Built-in)\n\nLogs errors to the console with detailed information:\n\n```dart\nErrorBoundary(\n  reporters: [ConsoleReporter()],\n  child: MyWidget(),\n)\n```\n\n### Sentry Reporter (Built-in)\n\nSend errors to [Sentry](https://sentry.io) for monitoring:\n\n```dart\n// 1. Add sentry_flutter to pubspec.yaml\n// dependencies:\n//   sentry_flutter: ^7.0.0\n\n// 2. Initialize Sentry in main.dart\nawait SentryFlutter.init(\n  (options) =\u003e options.dsn = 'YOUR_DSN',\n  appRunner: () =\u003e runApp(MyApp()),\n);\n\n// 3. Use the reporter\nErrorBoundary(\n  reporters: [\n    SentryReporter(\n      captureException: Sentry.captureException,\n      configureScope: Sentry.configureScope,\n    ),\n  ],\n  child: MyWidget(),\n)\n```\n\nWith filtering:\n\n```dart\nSentryReporter(\n  captureException: Sentry.captureException,\n  configureScope: Sentry.configureScope,\n  beforeSend: (info) {\n    // Skip non-critical errors\n    if (info.severity == ErrorSeverity.low) return null;\n    return info;\n  },\n  environment: 'production',\n  release: '1.0.0',\n)\n```\n\n### Firebase Crashlytics Reporter (Built-in)\n\nSend errors to [Firebase Crashlytics](https://firebase.google.com/products/crashlytics):\n\n```dart\n// 1. Add firebase_crashlytics to pubspec.yaml\n// dependencies:\n//   firebase_crashlytics: ^3.0.0\n\n// 2. Initialize Firebase in main.dart\nawait Firebase.initializeApp();\n\n// 3. Use the reporter\nfinal crashlytics = FirebaseCrashlytics.instance;\n\nErrorBoundary(\n  reporters: [\n    FirebaseCrashlyticsReporter(\n      recordError: crashlytics.recordError,\n      setCustomKey: crashlytics.setCustomKey,\n      setUserIdentifier: crashlytics.setUserIdentifier,\n      log: crashlytics.log,\n    ),\n  ],\n  child: MyWidget(),\n)\n```\n\n### Custom Reporter\n\nImplement `ErrorReporter` for your own error tracking service:\n\n```dart\nclass MyAnalyticsReporter implements ErrorReporter {\n  @override\n  Future\u003cvoid\u003e report(ErrorInfo info) async {\n    await myAnalytics.trackError(\n      error: info.error.toString(),\n      stackTrace: info.stackTrace.toString(),\n      severity: info.severity.name,\n    );\n  }\n\n  @override\n  void setUserIdentifier(String? userId) {\n    myAnalytics.setUserId(userId);\n  }\n\n  @override\n  void setCustomKey(String key, dynamic value) {\n    myAnalytics.setProperty(key, value);\n  }\n}\n```\n\n### Multiple Reporters\n\nUse multiple reporters simultaneously:\n\n```dart\nErrorBoundary(\n  reporters: [\n    ConsoleReporter(),                    // Dev logging\n    SentryReporter(...),                  // Error monitoring\n    FirebaseCrashlyticsReporter(...),     // Crash reporting\n  ],\n  child: MyWidget(),\n)\n```\n\nOr use `CompositeReporter`:\n\n```dart\nfinal reporter = CompositeReporter([\n  ConsoleReporter(),\n  SentryReporter(...),\n]);\n\nErrorBoundary(\n  reporters: [reporter],\n  child: MyWidget(),\n)\n```\n\n## Nested Boundaries\n\nError boundaries can be nested to isolate errors:\n\n```dart\nErrorBoundary(\n  onError: (e, s) =\u003e print('Outer caught: $e'),\n  child: Column(\n    children: [\n      SafeWidget(),\n      ErrorBoundary(\n        child: DangerousWidget(), // Errors here won't affect SafeWidget\n      ),\n    ],\n  ),\n)\n```\n\n### Error Bubbling\n\nUse `shouldRethrow` to bubble errors up to parent boundaries:\n\n```dart\nErrorBoundary(\n  shouldRethrow: (error) =\u003e error is CriticalError,\n  child: MyWidget(),\n)\n```\n\n## Async Error Catching\n\nBy default, errors in Futures and Streams within the boundary are caught:\n\n```dart\nErrorBoundary(\n  catchAsync: true, // Default\n  child: MyAsyncWidget(),\n)\n```\n\nDisable if you handle async errors differently:\n\n```dart\nErrorBoundary(\n  catchAsync: false,\n  child: MyWidget(),\n)\n```\n\n## Testing\n\nThe package includes testing utilities:\n\n```dart\nimport 'package:error_boundary/error_boundary.dart';\nimport 'package:flutter_test/flutter_test.dart';\n\nvoid main() {\n  testWidgets('shows fallback on error', (tester) async {\n    final tracker = ErrorTracker();\n\n    await tester.pumpWidget(\n      MaterialApp(\n        home: ErrorBoundary(\n          onError: tracker.onError,\n          child: ThrowingWidget(error: Exception('Test')),\n        ),\n      ),\n    );\n\n    await tester.pumpAndSettle();\n\n    expect(tracker.hasErrors, isTrue);\n    expect(tracker.lastError?.message, contains('Test'));\n    expect(find.text('Something went wrong'), findsOneWidget);\n  });\n}\n```\n\n### Test Helpers\n\n- `ErrorTracker` - Captures errors for assertions\n- `ThrowingWidget` - Widget that throws during build\n- `AsyncThrowingWidget` - Widget that throws async errors\n\n## API Reference\n\n### ErrorBoundary\n\n| Property | Type | Default | Description |\n|----------|------|---------|-------------|\n| `child` | `Widget` | required | Widget to wrap |\n| `fallback` | `FallbackBuilder?` | null | Custom fallback UI builder |\n| `onError` | `ErrorCallback?` | null | Called when error occurs |\n| `reporters` | `List\u003cErrorReporter\u003e` | `[]` | Error reporters |\n| `recovery` | `RecoveryStrategy` | `none()` | Recovery strategy |\n| `shouldRethrow` | `ShouldRethrow?` | null | Whether to bubble errors |\n| `catchAsync` | `bool` | `true` | Catch async errors |\n| `devMode` | `bool?` | `kDebugMode` | Show detailed errors |\n\n### ErrorInfo\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `error` | `Object` | The error object |\n| `stackTrace` | `StackTrace` | Stack trace |\n| `severity` | `ErrorSeverity` | low, medium, high, critical |\n| `type` | `ErrorType` | build, runtime, async, etc. |\n| `source` | `String?` | Error source identifier |\n| `timestamp` | `DateTime` | When error occurred |\n| `context` | `Map\u003cString, dynamic\u003e` | Additional context |\n\n## Contributing\n\nContributions are welcome! Please read our [contributing guidelines](https://github.com/brahim-guaali/error_boundary/blob/main/CONTRIBUTING.md) before submitting a PR.\n\n## License\n\nBSD 3-Clause License - see [LICENSE](LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrahim-guaali%2Ferror_boundary","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrahim-guaali%2Ferror_boundary","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrahim-guaali%2Ferror_boundary/lists"}