前言
在上一篇文章《Flutter混合开发:Android中如何启动Flutter》中我们学习了如果在Android项目中使用Flutter,在使用过程中经常会遇到各种错误,那么如何处理这些错误?
Flutter 框架可以捕获运行期间的错误,包括构建期间、布局期间和绘制期间。
关于Flutter错误的处理包含三个部分:
- onError:处理主线程发生的错误
- ErrorWidget:错误发生时的展示页面
- Zone:处理其他异步线程中(onError无法处理)的错误
下面来看看如何处理。
主线程错误
所有 Flutter 主线程的错误均会被回调方法 FlutterError.onError 捕获。默认情况下,会调用 FlutterError.dumpErrorToConsole 方法,正如方法名表示的那样,将错误转储到当前的设备日志中。当从 IDE 运行应用时,检查器重写了该方法,错误也被发送到 IDE 的控制台,可以在控制台中检查出错的对象。
所以捕获这部分错误只需要重写FlutterError的onError即可,如下:
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() {
FlutterError.onError = (FlutterErrorDetails details) {
FlutterError.dumpErrorToConsole(details);
if (kReleaseMode)
... //处理线上错误,如统计上传
};
runApp(MyApp());
}
上面我们重写了FlutterError.onError,这样就可以捕获到错误,第一行代码就是将error展示到控制台,这样我开发时就会在控制台很方便的看到错误。下面代码就是在线上环境下,对错误进一步处理,比如统计上传。
默认错误页面
当构建期间发生错误时,回调函数 ErrorWidget.builder 会被调用,来生成一个新的 widget,用来代替构建失败的 widget。默认情况,debug 模式下会显示一个红色背景的错误页面, release 模式下会展示一个灰色背景的空白页面。如下:
上面我们知道,构建时发生错误会默认展示一个错误页面,但是这个页面很不友好,我们可以自定义一个错误页面。定义一个自定义的 error widget,以当 builder 构建 widget 失败时显示,请使用 MaterialApp.builder。
class MyApp extends StatelessWidget {
...
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;
},
);
}
}
在App下的builder中,自定义一个error页面,然后赋值给ErrorWidget.builder即可。这样再出现错误的时候就可以展示一个友好的页面,提示用户重启或者升级应用等。
异步线程错误
如果在调用堆栈上没有 Flutter 回调的情况下发生错误(这里可以理解为FlutterError.onError仅仅可以捕获主线程的错误,而其他异步线程的错误则需要Zone来捕获),它们由发生区域的 Zone 处理。 Zone 在默认情况下仅会打印错误,而不会执行其他任何操作。
假设一个 onPressed 回调调用了异步方法,例如 MethodChannel.invokeMethod (或者其他 plugin 的方法):
OutlinedButton(
child: Text('Click me!'),
onPressed: () async {
final channel = const MethodChannel('crashy-custom-channel');
await channel.invokeMethod('blah');
},
),
如果 invokeMethod 抛出了错误,它不会传递至 FlutterError.onError,而是直接进入 runApp 的 Zone。
如果你想捕获这样的错误,请使用 runZonedGuarded。代码如下:
import 'dart:async';
void main() {
runZonedGuarded(() {
runApp(MyApp());
}, (Object error, StackTrace stack) {
... //处理错误
});
}
请注意,如果你的应用在 runApp 中调用了 WidgetsFlutterBinding.ensureInitialized() 方法来进行一些初始化操作(例如 Firebase.initializeApp()),则必须在 runZonedGuarded 中调用 WidgetsFlutterBinding.ensureInitialized():
runZonedGuarded(() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
如果 WidgetsFlutterBinding.ensureInitialized() 在外部调用,错误将不会被捕获到。
完整解决方案
所以flutter中完整的处理错误流程实际上是分三步的
- 先通过runZonedGuarded处理异步错误
- 再通过FlutterError.onError处理
(runZonedGuarded和onError捕获的这些错误可以通过一个我们自定义的MyErrorsHandler类来集中处理即可,比如统计上传等。) - 最后还需要自定义一个友好的错误页面来取代默认错误页面。
完整代码如下:
文章来源:https://www.toymoban.com/news/detail-400017.html
总结
可以看到再处理Flutter中的Error的时候,我们要分情况进行处理,主线程和异步线程的错误通过不同的方式进行捕获,但是处理可以统一。另外为了更友好的展示,可以重新定义一个错误页面来替换默认的错误页面,这样当出错的时候会显示一个很友好的页面来提示用户。文章来源地址https://www.toymoban.com/news/detail-400017.html
到了这里,关于Flutter开发:Error的捕获及处理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!