catalogue
preface
The fluent framework can catch errors during runtime, including construction, layout and drawing.
All Flutter errors are called back to the method FlutterError.onError Capture. By default, the FlutterError.dumpErrorToConsole Method, as indicated by the method name, dumps the error to the current device log. When the application is run from the IDE, the inspector overrides this method, and the error is also sent to the console of the IDE. You can check the error object in the console.
When an error occurs during build, the callback function ErrorWidget.builder Will be called to generate a new widget to replace the widget that failed to build. By default, an error page with a red background will be displayed in debug mode, and a blank page with a gray background will be displayed in release mode.
If errors occur when there is no Flutter callback on the call stack (it can be understood that flutterror.onerror can only capture the errors of the main thread, while the errors of other asynchronous threads need to be captured by the zone), they are generated by the zone in the occurrence area Zone handle. Zone By default, only errors are printed and nothing else is done.
These callback methods can be overridden, usually in void main() Method.
Let's see how to deal with it.
Capture Flutter error
Rewrite the onError of flutterror as follows:
import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; void main() { FlutterError.onError = (FlutterErrorDetails details) { FlutterError.dumpErrorToConsole(details); if (kReleaseMode) ... //Handle online errors, such as statistical upload }; runApp(MyApp()); }
Above, we have rewritten flutterror.onerror to capture errors. The first line of code is to display errors to the console, so that I can easily see errors on the console during development. The following code is to further handle errors in the online environment, such as statistical upload.
Custom ErrorWidget
As we know above, an error page will be displayed by default when an error occurs during construction, but this page is very unfriendly. We can customize an error page. Define a custom error widget to display when the builder fails to build the widget. Please use MaterialApp.builder.
class MyApp extends StatelessWidget { ... @override Widget build(BuildContext context) { return MaterialApp( ... builder: (BuildContext context, Widget widget) { Widget error = Text('...rendering error...'); if (widget is Scaffold || widget is Navigator) error = Scaffold(body: Center(child: error)); ErrorWidget.builder = (FlutterErrorDetails errorDetails) => error; return widget; }, ); } }
In the builder under App, customize an error page and assign it to ErrorWidget.builder. In this way, a friendly page can be displayed when errors occur again.
Unable to capture errors
Suppose an onPressed callback calls an asynchronous method, such as MethodChannel.invokeMethod (or other plugin methods):
OutlinedButton( child: Text('Click me!'), onPressed: () async { final channel = const MethodChannel('crashy-custom-channel'); await channel.invokeMethod('blah'); }, ),
If invokeMethod throws an error, it will not be passed to flutterror.onerror, but directly enter the Zone of runApp.
If you want to catch such errors, use runZonedGuarded. The code is as follows:
import 'dart:async'; void main() { runZonedGuarded(() { runApp(MyApp()); }, (Object error, StackTrace stack) { ... //Processing error }); }
Note that if your application invokes the WidgetsFlutterBinding.ensureInitialized() method in runApp to do some initialization operations (such as Firebase.initializeApp()), you must call WidgetsFlutterBinding.ensureInitialized() in runZonedGuarded:
runZonedGuarded(() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); runApp(MyApp()); }
If 'WidgetsFlutterBinding.ensureInitialized()' is called externally, the error will not be caught.
Complete code
If you want to deal with all the above problems, the code is as follows: