美文网首页
flutter应用性能优化最佳实践

flutter应用性能优化最佳实践

作者: 某非著名程序员 | 来源:发表于2025-05-18 14:35 被阅读0次

文章参考:https://docs.flutter.cn/perf/best-practices#pitfalls

  1. 控制 build() 方法的耗时

    1. 避免在 build() 方法中进行重复且耗时的工作,因为当父 widget 重建时,子 Wdiget 的 build() 方法会被频繁地调用。

      • 网络请求 fetchData()

      • 频繁解析JSON jsonDecode(hugeJsonString);

      • 复杂计算,频繁创建大对象和集合 List.generate(10000, (index) => index);

    2. 避免在一个超长的 build() 方法中返回一个过于庞大的 widget。把它们分拆成不同的 widget,并进行封装

      • 局部刷新:当在 State 对象上调用 setState()时,所有后代 widget 都将重建。因此, setState() 的调用转移到其 UI 实际需要更改的 widget 子树部分。如果改变的部分仅包含在 widget 树的一小部分中,请避免在 widget 树的更高层级中调用 setState()

        • 将状态推送到叶子节点。例如,如果你的页面上有一个滴答作响的时钟,与其将状态放在页面顶部,并在时钟每次滴答作响时重建整个页面,不如创建一个专门的时钟小部件,使其仅更新自身。
      • Child缓存:当重新遇到与前一帧相同的子 widget 实例时,将停止遍历。这种技术在框架内部大量使用,用于优化动画不影响子树的动画。请参阅 [TransitionBuilder](https://api.flutter-io.cn/flutter/widgets/TransitionBuilder.html) 模式,理解把Child缓存起来。

        • 如果子树没有变化,则缓存代表该子树的 Widget,并在每次可以使用时重用它。为此,请将 Widget 分配给final状态变量,并在 build 方法中重用它。重用 Widget 比创建一个新的(但配置相同的)Widget 效率更高。另一种缓存策略是将 Widget 的可变部分提取到 接受 child 参数的StatefulWidget中。
      • 尽可能使用const小部件。(这相当于缓存小部件并重复使用。)这将让 Flutter 的 widget 重建时间大幅缩短。要自动提醒使用 const

      • 在构建可复用的 UI 代码时,最好使用 [StatelessWidget](https://api.flutter-io.cn/flutter/widgets/StatelessWidget-class.html) 而不是函数。

      • 最小化重建有状态小部件

        • 尽量减少 build 方法及其创建的任何 Widget 所传递创建的节点数量。理想情况下,有状态 Widget 只会创建一个 Widget,并且该 Widget 应该是一个RenderObjectWidget。(当然,这并不总是可行的,但 Widget 越接近理想状态,效率就越高。)

        • 避免更改任何已创建子树的深度,或更改子树中任何控件的类型。例如,与其返回子控件本身或包装在IgnorePointer中的子控件,不如始终将子控件包装在IgnorePointer中并控制IgnorePointer.ignoring 属性。这是因为更改子树的深度需要重建、布局和绘制整个子树,而仅更改属性则对渲染树的更改尽可能少(例如,对于IgnorePointer,根本不需要布局或重绘)。

        • 如果由于某种原因必须更改深度,请考虑将子树的公共部分包装到具有GlobalKey的 Widget 中,该 GlobalKey 在有状态 Widget 的整个生命周期内保持一致。

  2. 谨慎使用 saveLayer()

    1. 为什么代价会大?触发了离屏渲染

    2. 为什么时候需要?在运行时,如果你需要动态地显示各种形状效果(例如),每个形状都有一定地透明度,可能(或可能不)重叠,那么你几乎必须使用 saveLayer()

    3. 尽量减少 saveLayer 的调用

  3. 尽量减少使用不透明度和裁剪

    1. 使用透明的颜色比透明的Opacity更快:

      • 例如,Container(color: Color.fromRGBO(255, 0, 0, 0.5))比快得多Opacity(opacity: 0.5, child: Container(color: Colors.red))
    2. Clipping 不会调用 saveLayer() (除非明确使用 Clip.antiAliasWithSaveLayer),因此这些操作没有 Opacity 那么耗时,但仍然很耗时,所以请谨慎使用。

    3. 能不用 [Opacity](https://api.flutter-io.cn/flutter/widgets/Opacity-class.html) widget,就尽量不要用。有关将透明度直接应用于图像的示例,请查看 Transparent image,这比使用 Opacity widget 更快。

    4. 要在图像中实现淡入淡出,请考虑使用 [FadeInImage](https://api.flutter-io.cn/flutter/widgets/FadeInImage-class.html) widget,该 widget 使用 GPU 的片段着色器应用渐变不透明度。

    5. 要创建带圆角的矩形,而不是裁剪矩形来达到圆角的效果,请考虑使用很多 widget 都提供的 borderRadius 属性。

    6. 陷进:

      • 避免使用 Opacity widget,尤其是在动画中避免使用。可以使用 AnimatedOpacityFadeInImage 代替该操作。

      • 使用 AnimatedBuilder 时,请避免在不依赖于动画的 widget 的构造方法中构建 widget 树,不然,动画的每次变动都会重建这个 widget 树,应当将这部分子树作为 child 传递给 AnimatedBuilder,从而只构建一次。更多内容

      • 避免在动画中裁剪,尽可能的在动画开始之前预先裁剪图像。

      • 避免在 Widget 对象上重写 operator ==。虽然这看起来有助于避免不必要的重建,但在实践中,它实际上损害了性能,因为这是 O(N²) 的行为。比较 widget 的属性可能比重建 widget 更加有效,也能更少改变 widget 的配置。即使在这种情况下,最好还要缓存 widget,因为哪怕有一次对 operator == 进行覆盖也会导致全面性能的下降,编译器也会因此不再认为调用总是静态的

  4. 谨慎使用网格列表和列表

    1. 懒加载:如果大多数 children widget 在屏幕上不可见,请避免使用返回具体列表的构造函数(例如 Column()ListView()),以避免构建成本。使用ListView.build()

    2. 不要使用shrinkWrap:当列表数据超过100条,就会卡顿。内部组件列表从 ListView 改为 SliverList,用 SliverChildBuilderDelegate 委托

  5. 避免内部传递

    1. 不要先算子widget的大小,再去计算父组件的高度。例如轮询计算高度

      1. 例如,你想要所有单元格都具有或大或小的效果(或类似需要轮询所有单元格的计算)时,就会发生内部传递。

      2. 例如,考虑一个大型的 卡片 网格列表时。一个网格列表应该有统一大小的单元格,所以布局代码执行了一次传递,从网格列表的根部开始(在 widget 树中),要求网格列表中的 每个 卡片(不仅仅是可见的卡片)来返回 内部 尺寸—假设没有任何限制,widget 更喜欢这样的尺寸。有了这些信息,底层框架就确定了一个统一的单元格尺寸,并再次重新访问所有的网格单元,告诉每个卡片应该使用什么尺寸。

  6. 隔离重绘区域:自定义的绘制使用RepaintBoundary包裹

相关文章

网友评论

      本文标题:flutter应用性能优化最佳实践

      本文链接:https://www.haomeiwen.com/subject/snnnijtx.html