Code前端首页关于Code前端联系我们

Flutter App开发:错误页面定制教程

terry 2年前 (2023-09-23) 阅读数 76 #移动小程序

在开发Flutter时,肯定会遇到Flutter错误页面,它可以让我们清楚地知道开发时代码中存在异常。但是,如果发布的APP出现异常,那么这个错误页面就非常不友好了。事实上,这个错误页面是可以定制的。本文告诉你如何自定义错误页面!

0x01 Flutter错误页面

这是我们经常看到的错误页面:Flutter app开发:错误页面自定义教程

0x02自定义Flutter错误页面

如果你想让Flutter错误页面显示为自定义页面,只需设置❀错误 的 Builder 就可以了。代码如下:

ErrorWidget.builder = (FlutterErrorDetails flutterErrorDetails){
        print(flutterErrorDetails.toString());
        return Center(
          child: Text("Flutter 走神了"),
          );
    };
复制代码

ErrorWidget.builder 返回一个当 Flutter 遇到错误时将显示的 widget。下图是我们自定义的错误页面,比Flutter的友好多了: Flutter app开发:错误页面自定义教程

0x03 github

本文涉及的代码:github.com/koudle/GDG_…

github地址:github.com/koudle /GDG_…

0x04 ErrorWidget源码分析

ErrorWidget ErrorWidget的构造函数参数为异常对象,然后返回一个内容为异常消息信息的RenderBox。我们在Flutter的错误页面中看到的就是这个RenderBox。

class ErrorWidget extends LeafRenderObjectWidget {
  /// 创建一个显示error message的Widget,exception是构造函数的参数。
  ErrorWidget(Object exception) : message = _stringify(exception),
      super(key: UniqueKey());

  //ErrorWidgetBuilder.builder的默认设置是ErrorWidget,我们可以设置成自己的
  static ErrorWidgetBuilder builder = (FlutterErrorDetails details) => ErrorWidget(details.exception);

  /// The message to display.
  final String message;

  //将exception对象转换成string
  static String _stringify(Object exception) {
    try {
      return exception.toString();
    } catch (e) { } // ignore: empty_catches
    return 'Error';
  }
  
  //返回一个内容是exception message信息的RenderBox
  @override
  RenderBox createRenderObject(BuildContext context) => RenderErrorBox(message);

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(StringProperty('message', message, quoted: false));
  }
}
复制代码

0x05 调用ErrorWidget的源码分析

之前看了ErrorWidget的源码,直接创建了一个widget。那么 ErrorWidget 是在哪里调用和显示的呢?代码中有三个地方调用了ErrorWidget。这三个地方有一个共同点,就是在构建widget的过程中,如果出现异常,就会返回ErrorWidget显示。具体源码如下:

  1. ComponentElement(framework.dart)
@override
  void performRebuild() {
    assert(() {
      if (debugProfileBuildsEnabled)
        Timeline.startSync('${widget.runtimeType}',  arguments: timelineWhitelistArguments);
      return true;
    }());

    assert(_debugSetAllowIgnoredCallsToMarkNeedsBuild(true));
    Widget built;
    try {
      built = build();
      debugWidgetBuilderValue(widget, built);
    } catch (e, stack) {
      built = ErrorWidget.builder(_debugReportException('building $this', e, stack));
    } finally {
      // We delay marking the element as clean until after calling build() so
      // that attempts to markNeedsBuild() during build() will be ignored.
      _dirty = false;
      assert(_debugSetAllowIgnoredCallsToMarkNeedsBuild(false));
    }
    try {
      _child = updateChild(_child, built, slot);
      assert(_child != null);
    } catch (e, stack) {
      built = ErrorWidget.builder(_debugReportException('building $this', e, stack));
      _child = updateChild(null, built, slot);
    }

    assert(() {
      if (debugProfileBuildsEnabled)
        Timeline.finishSync();
      return true;
    }());
  }
复制代码
  1. RenderObjectToWidgetElement(binding.dart)
  void _rebuild() {
    try {
      _child = updateChild(_child, widget.child, _rootChildSlot);
      assert(_child != null);
    } catch (exception, stack) {
      final FlutterErrorDetails details = FlutterErrorDetails(
        exception: exception,
        stack: stack,
        library: 'widgets library',
        context: 'attaching to the render tree'
      );
      FlutterError.reportError(details);
      final Widget error = ErrorWidget.builder(details);
      _child = updateChild(null, error, _rootChildSlot);
    }
  }
复制代码
  1. _LayoutBuilderElement(layout_builder.dart) oEN:A 链接:https://juejin.im/ post /5c3f1cf66fb9a049d519c 4cb
    来源:掘金
    版权属于作者。如需商业转载,请联系求作者授权。非商业转载请来源。

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门