Uncaught Exception Secrets, Zones

How do we process uncaught exceptions to well-behaved extensions? It’s a process where we discover an uncaught exception then change code to account for it; and, Zones has a role to play in this process.
Isolates and Zones, Oh My
It’s not true that Dart is single-threaded. What is true is that one thread is in an isolate. For example, the main function starts out in a Zone.root in a single isolate:
So what happens when we create another Zone in a child of main? Yes, we get a separate isolate which means that if we have an uncaught exception it does not take down the main function of the app as that is in a separate isolate.
The gist is if we have the right zone set up then we are able to debug our app by running it to see the uncaught exception show up in the IDE-console-log then re-write some code to handle that case and run the app again and see that exception disappear.
Using Zones
First, we set up an error handler to catch flutter exceptions:
FlutterError.onError = (FlutterErrorDetails details) async {if (isInDebugMode) {// In development mode simply print to console.FlutterError.dumpErrorToConsole(details);} else {// In production mode report to the application zone to report to// app exceptions provider. We do not need this in Profile mode.// ignore: no-empty-blockif (isInReleaseMode) {// FlutterError class has something not changed as far as null safety// so I just assume we do not have a stack trace but still want the// detail of the exception.// ignore: cast_nullable_to_non_nullableZone.current.handleUncaughtError(// ignore: cast_nullable_to_non_nullabledetails.exception,// ignore: cast_nullable_to_non_nullabledetails.stack as StackTrace,);}
}
};We put a Zone.current call so that the error is passed to the Zone uncaught-handler, as all Flutter Framework errors will be uncaught exceptions.
Now, remember the main is in its own Zone.root isolate, so we need to set up an inner Zone for our runApp call:
runZonedGuarded<Future<Null>>(() async {//Since we start another zone we need to//ensure that SkyEngine has fully loaded Flutter// and it needs to be called here to enable grabbing the errorsWidgetsFlutterBinding.ensureInitialized();// Service and other initializations heremyLogger.info('app initialized');runApp(const MyApp());},
// ignore: no-empty-block(Object error, StackTrace stack) {log('$error.runtimeType $stack');},
zoneSpecification: ZoneSpecification(// Intercept all print callsprint: (self, parent, zone, line) async {// Include a timestamp and the name of the Appfinal messageToLog = "[${DateTime.now()}] $myAppTitle $line $zone";// Also print the message in the "Debug Console"// but it's ony an info message and contains no// privacy prohibited stuffparent.print(zone, messageToLog);},
),
);We use the ZoneSpecification to redefine the console-dump(it’s print) to format the console output. And this is the full code:
