Tutorials
Build a Flutter root integration
Attach framework and platform hooks inside an application-owned Zone and present errors through the root Navigator.
Build a Flutter root integration
This tutorial connects one core manager to the three root failure paths relevant to a typical Flutter application: the application Zone, Flutter framework reporting, and uncaught root-isolate platform errors. Child Isolates remain a separate explicit binding.
Zone error ───────────────┐
FlutterError.onError ─────┼─→ one ErrorManager pipeline
PlatformDispatcher error ┘
Install the Flutter package
flutter pub add ark_error_manager_flutter:^0.1.0-dev.1
The package exports the core API, so the bootstrap file needs one import.
Own bootstrap state explicitly
final class ApplicationBootstrap {
ErrorManager? _manager;
FlutterErrorManagerBinding? _binding;
void run() => runZonedGuarded(_start, _onZoneError);
}
The nullable manager allows the Zone callback to use a direct fallback if manager construction itself fails.
Create and attach inside the Zone
void _start() {
WidgetsFlutterBinding.ensureInitialized();
final navigatorKey = GlobalKey<NavigatorState>();
final manager = ErrorManager(
configuration: ErrorManagerConfiguration(
environment: FlutterErrorBuildMode.environment(
deployment: ErrorDeploymentEnvironment.production,
application: const ErrorApplicationInfo(
name: 'Example application',
version: '1.0.0',
),
),
classifiers: const <ErrorClassifier>[
FlutterFrameworkErrorClassifier(),
],
contextProviders: <ErrorContextProvider>[
FlutterRuntimeErrorContextProvider(),
],
reporters: const <ErrorReporter>[DeveloperLogErrorReporter()],
presenter: NavigatorErrorPresenter(
navigatorKey: navigatorKey,
delegate: const ApplicationErrorPresentationDelegate(),
),
),
);
final binding = FlutterErrorManagerBinding(manager: manager)..attach();
_manager = manager;
_binding = binding;
runApp(Application(navigatorKey: navigatorKey));
}
Provide a construction fallback
void _onZoneError(Object error, StackTrace stackTrace) {
final manager = _manager;
if (manager == null) {
Zone.root.handleUncaughtError(error, stackTrace);
return;
}
manager.onUncaughtZoneError(error, stackTrace);
}
The binding and manager stay in the bootstrap owner. Widgets, Presenters, UseCases, repositories, and providers do not receive them.
Implement application-owned presentation
final class ApplicationErrorPresentationDelegate
implements FlutterErrorPresentationDelegate {
const ApplicationErrorPresentationDelegate();
@override
Future<void> present(
BuildContext? context,
ErrorIncident incident,
ErrorPresentationDirective directive,
) async {
if (context == null || directive == ErrorPresentationDirective.none) {
return;
}
await showDialog<void>(
context: context,
barrierDismissible: directive != ErrorPresentationDirective.blocking,
builder: (context) => const AlertDialog(
title: Text('Something went wrong'),
content: Text('Try the operation again.'),
),
);
}
}
The delegate receives the raw application-local Incident because it may choose presentation from category, severity, and policy. Do not display exception text, stack traces, or arbitrary context fields to users.
Exercise the framework path
FlutterError.reportError(
FlutterErrorDetails(
exception: StateError('Example framework failure'),
stack: StackTrace.current,
library: 'catalog example',
context: ErrorDescription('while handling the example action'),
),
);
This uses Flutter's normal reporting API. FlutterErrorManagerBinding receives it through FlutterError.onError; the screen itself does not receive the manager.
Ownership during shutdown
Long-running mobile applications often end by process termination, but tests, desktop shells, embedded Flutter hosts, and controlled restarts have a real shutdown sequence:
binding.detach();
await manager.flush();
await manager.close();
Detach first so a global callback cannot submit a new incident while the manager closes. Store the binding because it owns the captured previous handlers and can restore them safely.
Avoid duplicate reporting
The default previous-handler policy preserves normal Flutter diagnostics. If a previous FlutterError.onError already sends errors to a remote crash service, decide which integration owns external reporting. Preserving two remote reporters without a deduplication plan can upload the same failure twice.