Flutter exception catching

Flutter framework exception capture

The Flutter framework captures exceptions in many key methods for us.
When our layout goes out of bounds or out of specification, Flutter will automatically pop up an error interface. For example,
image.png
although there is an error in the code, it will not cause the APP to crash. Flutter will help us catch the exception.

In Flutter, Widget will create the corresponding Element, StatefulWidget will create StatefulElement, and StatelessWidget will create StatelessElement. Both StatefulElement and StatelessElement are ComponentElement.

  /// Calls the [StatelessWidget.build] method of the [StatelessWidget] object
  /// (for stateless widgets) or the [State.build] method of the [State] object
  /// (for stateful widgets) and then updates the widget tree.
  ///
  /// Called automatically during [mount] to generate the first build, and by
  /// [rebuild] when the element needs updating.
  @override
  @pragma('vm:notify-debugger-on-exception')
  void performRebuild() {
    Widget? built;
    try {
      ...
      //执行build方法
      built = build();
      ...
      debugWidgetBuilderValue(widget, built);
    } catch (e, stack) {
      _debugDoingBuild = false;
      built = ErrorWidget.builder(
        _reportException(
          ErrorDescription('building $this'),
          e,
          stack,
          informationCollector: () => <DiagnosticsNode>[
            if (kDebugMode)
              DiagnosticsDebugCreator(DebugCreator(this)),
          ],
        ),
      );
    } finally {
      // We delay marking the element as clean until after calling build() so
      // that attempts to markNeedsBuild() during build() will be ignored.
      super.performRebuild(); // clears the "dirty" flag
    }
    try {
      _child = updateChild(_child, built, slot);
      assert(_child != null);
    } catch (e, stack) {
      built = ErrorWidget.builder(
        _reportException(
          ErrorDescription('building $this'),
          e,
          stack,
          informationCollector: () => <DiagnosticsNode>[
            if (kDebugMode)
              DiagnosticsDebugCreator(DebugCreator(this)),
          ],
        ),
      );
      _child = updateChild(null, built, slot);
    }
  }

The ComponentElement-performRebuild method is used to call the build method of StatelessWidget or State and update the Widget tree.
Use try-catch to catch exceptions. When an exception is caught, create an ErrorWidget pop-up prompt.
ErrorWidget.builder accepts a FlutterErrorDetails returned by the _debugReportException method for exception display.

FlutterErrorDetails _reportException(
  DiagnosticsNode context,
  Object exception,
  StackTrace? stack, {
  InformationCollector? informationCollector,
}) {
  //构建异常详情对象
  final FlutterErrorDetails details = FlutterErrorDetails(
    exception: exception,
    stack: stack,
    library: 'widgets library',
    context: context,
    informationCollector: informationCollector,
  );
  //报告异常
  FlutterError.reportError(details);
  return details;
}

Report exceptions through the FlutterError.reportError method

  static void reportError(FlutterErrorDetails details) {
    onError?.call(details); //调用了onError回调
  }

onError is a static property of FlutterError, which has a default processing method dumpErrorToConsole.
If we want to report exceptions ourselves, we only need to provide a custom error handling callback.

  FlutterError.onError = (FlutterErrorDetails details) {
    reportError(details);
  };

Other exception catching

Synchronous exception catching

Synchronous exceptions can be caught through try/catch

  // 使用 try-catch 捕获同步异常
  try {
    throw StateError("xxx");
  } catch (e, stack) {
    print(e);
  }

Asynchronous exception catching

Asynchronous exceptions cannot be caught by try/catch

try {
    Future.delayed(Duration(seconds: 1)).then((e) => Future.error("xxx"));
} catch (e) {
    print(e)
}

To catch asynchronous exceptions use the catchError statement provided by Future:

// 使用 catchError 捕获异步异常
Future.delayed(Duration(seconds: 1))
    .then((e) => Future.error("xxx"))
    .catchError((e)=>print(e));

Is there a way to monitor both synchronization and asynchronous exceptions?

Zone & PlatformDispatcher

Zone represents the environment range of a code execution. Different Zone code contexts are different and do not affect each other. Similar to a code execution sandbox, different sandboxes are isolated. The sandbox can capture, intercept or modify some code behaviors. For example, Zone can capture log output, timer creation, and microtask scheduling behaviors. Zone can also capture Catch all unhandled exceptions.

  runZonedGuarded(() => runApp(MyApp()), (error, stack) {
    print(error);
  });

This is no longer officially recommended, and PlatformDispatcher is more recommended.

  PlatformDispatcher.instance.onError = (error, stack) {
    print(error);
    return true;
  };

Catch exceptions and report them

void main() {
  var onError = FlutterError.onError; //先将 onerror 保存起来
  FlutterError.onError = (FlutterErrorDetails details) {
    onError?.call(details); //调用默认的onError处理
    reportError(details);
  };
  PlatformDispatcher.instance.onError = (error, stack) {
    reportError(error);
    return true;
  };
  runApp(const MyApp());
}

Reference documentation:
https://flutter.cn/docs/testing/errors

Guess you like

Origin blog.csdn.net/yuantian_shenhai/article/details/132895196