Flutter中减少构建的几种tips

有几种方法能尽量减少build的次数

一、 尽量减少构建方法及其创建的widget所传递创建的节点数量

比如我们想要定位一个组件的话,考虑使用Align 或者CustomSingleChildLayout,而不是通过组合Row Column、Padding、SizedBox等组件的方式。

与其考虑使用ContainerDecoration的方式来实现想要的图形效果,可以考虑使用CustomPaint组件

二、尽可能的使用const组件,构造函数尽可能的使用const修饰

这是因为const构造函数能让Flutter在构建组件时效率更高,如果widget的配置信息在整个应用生命周期内没有变化,Flutter就可以重用该实例而不是每次构建时都创建一个新的,这样可以减少内存的使用,也减少垃圾回收的压力。

dart 复制代码
// GOOD
class Page extends StatelessWidget {
 const Page({super.key});

  @override
  Widget build(BuildContext context) {
    return const Placeholder();
  }
}
// BAD
class Page extends StatelessWidget {
// 尽量使用const修饰构造函数
  Page({super.key});

  @override
  Widget build(BuildContext context) {
    return const Placeholder();
  }
}

三、如果组件由于InheritedWidget可能会频繁的rebuild,可以考虑将组件重构为多个组件,将有可能重构的widget放到叶子结点。

四、如果子树不会改变,可以缓存代表该子树的widget,并在每次使用时重用它。

我们可以将该widget分配给一个final修饰的状态变量,并在build方法中重用它。 重用widget比创建一个新的(配置相同的)widget高效的多。另一种缓存策略是将widget的可变部分提取到接受child参数的StatefulWidget组件中

五、尽量避免创建的子树的深度或改变子树中任何widget的类型。

这是因为改变子树的深度需要重建、布局和绘制整那个子树,而仅改变属性将对渲染树进行最小的改变。 比如

dart 复制代码
// GOOD
  @override
  Widget build(BuildContext context) {
    final child = Container();
    return IgnorePointer(
      ignoring:ignoring,
      child: child,
    );
  }
  
// BAD
 @override
  Widget build(BuildContext context) {
    final child = Container();
    if(ignoring) {
      return child;
    }
    return IgnorePointer(
      child: child,
    );
  }

如果必须要改变深度的话,可以考虑将子树中的公共部分包装在有GlobalKey的widget中,这样就可以在stateful widget中保持一致。(如果没有合适的widget设置key,可以使用KeyedSubtree widget)

六、当有一部分UI可以重复使用的时候,优先考虑封装成一个单独的widget,而不是通过辅助方法。

这是因为,如果通过方法构建一个widget,当调用setState时,build整体都会重新运行,Flutter会完全重构该方法返回的widget,这样非必要的UI重建,可能会导致element树和render object树这类的树结构浪费CPU时间。尤其是在执行动画的时候。

如果是一个Widget的话,Flutter就能够有效的仅重新渲染确实需要更新的部分。而且,如果该widget是 const 的话,Flutter将能够缩短大部分的rebuild工作。

而且辅助方法可能会意外的持有过时的context,比如

dart 复制代码
class MyWidget extends StatelessWidget {
  const MyWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Builder(builder: (innerContext) {
      return IconButton(
        onPressed: () {
          Theme.of(context)...
        },
        icon: const Icon(Icons.favorite),
      );
    });
  }
}

在Builder组件中我们context定义了不同的名称innerContext,在其嵌套代码中就有可能使用旧有而不稳定的context,而如果使用封装widget的话就能避免这样的错误:

dart 复制代码
class MyWidget extends StatelessWidget {
  const MyWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Builder(builder: (innerContext) {
      return MyIconButton();
    });
  }
}

class MyIconButton extends StatelessWidget {
  const MyIconButton({super.key});

  @override
  Widget build(BuildContext context) {
    return IconButton(
      onPressed: () {
        Theme.of(context)...
      },
      icon: const Icon(Icons.favorite),
    );
  }
}

这样widget就能访问唯一且正确的context了。

总的来说,官方还是推荐使用widget来拆解庞大的build方法的。

方法还是widget其实也引起了很多的讨论,可以看下stackoverflow的讨论。这里的高赞列举了几个使用类的好处:

  1. 性能优化:

    • 使用const构造函数可以创建不变的组件,这可以帮助Flutter框架在需要时避免不必要的重建。
    • 类可以实现更精细的重建控制,比如通过StatefulWidgetsetState方法或者使用Provider等状态管理解决方案。
  2. 资源管理:

    • 类可以确保在组件树中切换布局时,正确地释放旧布局所使用的资源。相比之下,函数可能会意外地重用之前的状态。
  3. 热重载:

    • 类可以确保热重载(hot-reload)功能正确工作。使用函数可能会破坏showDialog和类似功能的热重载能力。
  4. 工具集成:

    • 类组件会被Flutter DevTools中的Widget Inspector识别并显示在组件树中,这有助于开发者理解屏幕上显示的内容。
    • 可以重写debugFillProperties

参考链接:

  1. api.flutter.dev/flutter/wid...
  2. api.flutter.dev/flutter/wid...
  3. stackoverflow.com/questions/5...
相关推荐
欧阳天风2 分钟前
js实现鼠标横向滚动
开发语言·前端·javascript
局i34 分钟前
Vue 指令详解:v-for、v-if、v-show 与 {{}} 的妙用
前端·javascript·vue.js
码界奇点1 小时前
Java Web学习 第15篇jQuery从入门到精通的万字深度解析
java·前端·学习·jquery
小鑫同学1 小时前
Alias Assistant:新一代 macOS Shell 别名管理解决方案
前端·前端工程化
꒰ঌ小武໒꒱1 小时前
RuoYi-Vue 前端环境搭建与部署完整教程
前端·javascript·vue.js·nginx
名字越长技术越强2 小时前
前端之相对路径
前端
望道同学2 小时前
PMP/信息系统项目管理师 9 张 思维导图【考试必备】
前端·后端·程序员
局i3 小时前
Vue 中 v-text 与 v-html 的区别:文本渲染与 HTML 解析的抉择
前端·javascript·vue.js
菜鸟冲锋号4 小时前
问题:增量关联(实时同步新数据) 这个场景中,如果hudi_pay 变更了一条数据,hudi_order_pay_join 结果的数据会跟着变化吗
服务器·前端·数据库
贩卖黄昏的熊4 小时前
typescript 快速入门
开发语言·前端·javascript·typescript·ecmascript·es6