首页 文章

如何处理不需要的小部件构建?

提问于
浏览
3

由于各种原因,有时会再次调用我的小部件的 build 方法 .

我知道这是因为父母更新了 . 但这会导致不良影响 . 导致问题的典型情况是以这种方式使用 FutureBuilder

@override
Widget build(BuildContext context) {
  return FutureBuilder(
    future: httpCall(),
    builder: (context, snapshot) {
      // create some layout here
    },
  );
}

在此示例中,如果再次调用构建方法,则会触发另一个http请求 . 这是不受欢迎的 .

考虑到这一点,如何处理不需要的构建?有什么方法可以阻止构建调用?

1 回答

  • 9

    构建方法的设计应该是纯粹的/没有副作用的 . 这是因为许多外部因素可以触发新的小部件构建,例如:

    • 路由弹出/推送,用于输入/输出动画

    • 屏幕调整大小,通常是由于键盘外观或方向更改

    • 父窗口小部件重新创建其子窗口

    • 小部件依赖于( Class.of(context) 模式)更改的InheritedWidget

    This means that the build method should not trigger an http call or modify any state .


    这与问题有什么关系?

    你面临的问题是你的构建方法有副作用/不纯,造成无关的构建调用麻烦 .

    您应该使构建方法保持纯粹,而不是阻止构建调用,以便可以随时调用它而不会产生影响 .

    对于您的示例,您将窗口小部件转换为 StatefulWidget 然后将该HTTP调用解压缩到 StateinitState

    class Example extends StatefulWidget {
      @override
      _ExampleState createState() => _ExampleState();
    }
    
    class _ExampleState extends State<Example> {
      Future<int> future;
    
      @override
      void initState() {
        future = Future.value(42);
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return FutureBuilder(
          future: future,
          builder: (context, snapshot) {
            // create some layout here
          },
        );
      }
    }
    

    • 也可以制作一个能够重建的小部件,而不必强迫其子代进行构建 .

    当小部件的实例保持不变时;有目的地颤动不会重建孩子 . 这意味着您可以缓存窗口小部件树的部分以防止不必要的重建 .

    最简单的方法是使用dart const 构造函数:

    @override
    Widget build(BuildContext context) {
      return const DecoratedBox(
        decoration: BoxDecoration(),
        child: Text("Hello World"),
      );
    }
    

    感谢 const 关键字,即使构建被调用数百次, DecoratedBox 的实例也将保持不变 .

    但您可以手动获得相同的结果:

    @override
    Widget build(BuildContext context) {
      final subtree = MyWidget(
        child: Text("Hello World")
      );
    
      return StreamBuilder<String>(
        stream: stream,
        initialData: "Foo",
        builder: (context, snapshot) {
          return Column(
            children: <Widget>[
              Text(snapshot.data),
              subtree,
            ],
          );
        },
      );
    }
    

    在此示例中,当StreamBuilder收到新值的通知时,即使StreamBuilder / Column执行,也不会重建 subtree . 这是因为由于闭包, MyWidget 的实例没有改变 .

    这种模式在动画中经常使用 . 典型用户是 AnimatedBuilder 和所有*转换,例如 AlignTransition .

    你也可以将 subtree 存储到你的类的一个字段中,尽管不太推荐,因为它会破坏热重载 .

相关问题