The difference between Dart and Flutter is that Flutter catches all unhandled exceptions and just prints them out (including the stack trace). This is on purpose and the secret is that Flutter registers a callback to the static class function FlutterError.onError and the default implementation is the function FlutterError.presentError. Some more details on the official docs: Handling errors in Flutter
Disable terminating on fatal errors in Dart
It's possible to disable terminating the app/script on fatal errors by calling Isolate.setErrorsFatal with false. For example, to keep async code running even if an unhandled exception is thrown just do this.
void main () {
Isolate.current.setErrorsFatal(false); // <- Here
Future(() async {
await Future.delayed(Duration(seconds: 10));
print("TimeOut");
}).timeout(Duration(seconds: 5));
Future(() async {
await Future.delayed(Duration(seconds: 15));
print("AAAAAAA");
});
}
And the output is going to be:
TimeOut
AAAAAAA
Dart with the same behaviour as Flutter
The following code simulates the same thing Flutter does in Dart with runZonedGuarded:
import 'dart:async';
void main(List<String> args) {
runZonedGuarded(program, errorHandler);
}
void program() {
Future(() async {
await Future.delayed(Duration(seconds: 10));
print("TimeOut");
}).timeout(Duration(seconds: 5));
Future(() async {
await Future.delayed(Duration(seconds: 15));
print("AAAAAAA");
});
}
void errorHandler(Object error, StackTrace stack) {
print('Unhandled exception:\n$error');
}
The output is going to be the following in Dart:
Unhandled exception:
TimeoutException after 0:00:05.000000: Future not completed
TimeOut
AAAAAAA
Flutter with the same behaviour as Dart
On the other hand, it's possible to do the same thing in Flutter as Dart does. I.e. kills the app on whenever unhandled exception. It's just a matter of using the runZonedGuarded again:
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
void main() {
runZonedGuarded(() async {
runApp(const MyApp());
}, (Object error, StackTrace stack) {
print('Unhandled exception:\n$error');
exit(1);
});
}
void program() {
Future(() async {
await Future.delayed(Duration(seconds: 10));
print("TimeOut");
}).timeout(Duration(seconds: 5));
Future(() async {
await Future.delayed(Duration(seconds: 15));
print("AAAAAAA");
});
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
program();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
builder: (context, child) => const Scaffold(
body: Center(child: Text('The app is going to exit in 5 seconds...')),
),
);
}
}
The output is going to be the following:
flutter: Unhandled exception:
TimeoutException after 0:00:05.000000: Future not completed
Lost connection to device.