preface
The Flutter framework can catch errors during runtime, including during build, layout, and drawing.
All Flutter errors are caught by the FlutterError. OnError callback method. . By default, will be called FlutterError dumpErrorToConsole method, as the method name says, the error dump to the current equipment log. When the application is run from the IDE, the inspector rewrites the method and the error is sent to the IDE console, where the offending object can be checked.
When an error occurs during a build, the errorWidget. Builder callback is called to generate a new widget to replace the failed build widget. By default, debug mode displays an error page with a red background, and Release mode displays a blank page with a gray background.
If errors occur without a Flutter callback on the call stack (here understood as FlutterError. OnError can only catch errors of the main thread, whereas other asynchronous thread errors need to be caught by the Zone), they are handled by the Zone of the region in which they occur. A Zone by default prints an error and does nothing else.
Each of these callback methods can be overridden, usually in the void main() method.
So let’s see how to do that.
Capture Flutter error
Overwrite FlutterError onError 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 statistics upload
};
runApp(MyApp());
}
Copy the code
Above we overwrite FlutterError. OnError so that we can catch the error. The first line of code displays the error to the console so that I can easily see the error on the console when I develop. The following code is used to further handle errors in an online environment, such as statistical uploads.
Custom ErrorWidget
As we know above, an error page is displayed by default when errors occur during build time, but this page is not very friendly, we can customize an error page. Define a custom error widget to display when the Builder fails to build the widget, use the 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;
returnwidget; }); }}Copy the code
Create a custom error page in App Builder and assign it to errorWidget. builder. This way you can display a friendly page when an error occurs again.
An error that cannot be caught
Suppose that a onPressed callback with asynchronous method, such as MethodChannel. InvokeMethod (or other plugin method) :
OutlinedButton(
child: Text('Click me! '),
onPressed: () async {
final channel = const MethodChannel('crashy-custom-channel');
await channel.invokeMethod('blah'); },),Copy the code
If invokeMethod throws an error, it is not passed to FlutterError. OnError, but directly into the Zone of runApp.
If you want to catch such errors, use runZonedGuarded. The code is as follows:
import 'dart:async';
voidmain() { runZonedGuarded(() { runApp(MyApp()); },Object error, StackTrace stack) {
... // Processing error
});
}
Copy the code
Please note that if your application calls in runApp WidgetsFlutterBinding. The ensureInitialized () method to do some initialization (for example Firebase. InitializeApp ()), You must call in runZonedGuarded WidgetsFlutterBinding. The ensureInitialized () :
runZonedGuarded(() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
Copy the code
If WidgetsFlutterBinding. The ensureInitialized () on the external call, mistakes will be caught.
The complete code
To handle all of the above, the code looks like this:
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() {
runZonedGuarded(() async{ WidgetsFlutterBinding.ensureInitialized(); .awaitmyErrorsHandler.initialize(); FlutterError.onError = (FlutterErrorDetails details) { FlutterError.dumpErrorToConsole(details); myErrorsHandler.onError(details); }; runApp(MyApp()); },Object error, StackTrace stack) {
myErrorsHandler.onError(error, stack);
});
}
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;
returnwidget; }); }}Copy the code
“RunZonedGuarded” will handle asynchronous errors first and “FlutterError. OnError” will help. For example, “statistics uploading”, the system will use a customized myErrorsHandler. Then you need to define a friendly error page in your app.