Boundary

Boundary 是 Flutter 的一个新 widget,它接管 FlutterError.onError 和 ErrorWidget.builder,使它们可以组合和作用域化。

如果您曾想过将错误报告仅应用于 widget 树的特定部分,或者发现实现“糟糕”/加载屏幕很困难,那么这个库非常适合您。

安装

为了让 Boundary 生效,必须先调用 setupBoundary

这可以在您的 main 函数中完成,如下所示

void main() {
  setupBoundary();
  runApp(MyApp());
}

出于测试目的,setupBoundary 返回一个函数以恢复设置
到它们的默认行为

testWidgets('mytest', (tester) async {
  final restore = setupBoundary();

  await tester.pumpWidget(
    Boundary(
      fallback: (_, __) => Container(),
      child: Text('foo', textDirection: TextDirection.ltr),
    )
  );

  // necessary call before any `expect`, otherwise the test framework will throw
  restore();

  expect(find.text('foo'), findsOneWidget);
});

原理

错误报告和备用 UI 现在通过一个通用的 widget 来表示

Boundary

当这个 widget 插入到 widget 树中时,它能够捕获异常
来自其后代(仅后代),然后创建一个备用 UI。

这是一个典型的例子

Scaffold(
  appBar: AppBar(title: const Text('hello')),
  body: Boundary(
    fallbackBuilder: (context, error) {
      return const Center(child: Text('Oops'));
    },
    child: Container(
      color: Colors.red,
      padding: const EdgeInsets.all(50),
      child: Builder(builder: (_) {
        // a descendant somethow failed
        throw 42;
      }),
    ),
  ),
);

其渲染效果如下

screenshot

请注意,即使有一个 Container 带有 padding 和红色背景
作为 Boundary 的子项,“糟糕”屏幕不显示任何这些内容

fallbackBuilder 返回的 widget 在一个完全不同的 widget 树中。

但是,失败的子树(Container -> Builder)也没有从树中移除!
它的状态得以保留,并且只是被移出屏幕,直到它成功重建。

这一点通过 以下示例 得到证明,该示例展示了 Boundary 如何使用
来显示 widget 树中更深的 FutureBuilder 的加载/错误屏幕
– 而无需引用 Future

Boundary(
  fallbackBuilder: (_, error) {
    // doesn't have the reference on the Future, but
    // is still able to display loading/error state
    if (error is Loading) {
      return const Center(child: CircularProgressIndicator());
    } else if (error is NotFoundError) {
      return const NotFoundScreen();
    } else {
      return const OopsScreen();
    }
  },
  child: SubtreeThatHasAFutureBuilder(),
)

future builder example

常见问题

如何移除备用屏幕

一旦抛出异常,就会显示备用屏幕。但您可能想要
在某个时候停止显示该备用项。

要实现这一点,只需重建失败的 widget,使其不再抛出异常
即可。这将自动移除备用屏幕。

如果 fallbackBuilder 中出现异常会怎样?

如果在 fallbackBuilder 中出现异常,则该异常会被传播
到下一个 Boundary,直到没有为止。

Boundary(
  fallbackBuilder: (_, err) => Text(err.toString()),
  child: Boundary(
    fallbackBuilder: (_, err) {
      print(err);
      throw err;
    },
    child: Builder(builder: (_) {
      throw 42;
    })
  )
)

使用前面的片段,这将在控制台首先打印 42,然后
在屏幕上渲染一个带有“42”的 Text

GitHub

https://github.com/rrousselGit/boundary