Flutter 应用性能优化最佳实践

通常来说,Flutter 技术构建的应用程序在默认情况下都是高性能的。所以你只需要避开常见的陷阱,就可以获得优异的性能,而不需要使用复杂的分析工具对细节做优化。这些最佳建议将帮助你编写性能最佳的 Flutter 应用程序。

最佳实践

如何设计一个能最有效地渲染页面的 Flutter 应用程序?特别是如何确保底层框架生成的绘图代码尽可能高效?这里有几件需要你在设计应用时考虑的事情:

控制 build() 方法的耗时

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

  • 避免在一个超长的 build() 方法中返回一个过于庞大的 widget。把他们分拆成不同的 widget,并进行封装,另外他们要这样改变:

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

    • 当重新遇到与前一帧相同的子 widget 实例时,将停止遍历。这种技术在框架内部大量使用,用于优化动画不影响子树的动画。请参阅 [TransitionBuilder][] 模式和使用此原则的 [SlideTransition][],以避免在动画过程中重建其后代 widget。

另见:

仅当需要的时候才应用效果

由于代价很大,请谨慎使用效果。一些效果的背后调用了性能代价很大的 saveLayer() 方法。

为什么 saveLayer 代价很大?

调用 saveLayer() 会开辟一片离屏缓冲区。将内容绘制到离屏缓冲区可能会触发渲染目标切换,这些切换在较早期的 GPU 中特别慢。

一些在使用效果时的通用规则:

  • 能不用 Opacity widget,就尽量不要用。有关将透明度直接应用于图像的示例,请参见 Transparent image,这比使用 Opacity widget 更快。

  • Clipping 不会调用 saveLayer()(除非明确使用 Clip.antiAliasWithSaveLayer),因此这些操作没有 Opacity 那么耗时,但仍然很耗时,所以请谨慎使用。

其他会触发 saveLayer() 的 widget,可能也会代价高昂。

  • ShaderMask
  • ColorFilter
  • [Chip][]—当 disabledColorAlpha != 0xff 的时候,会调用 saveLayer()

  • Text—might cause call to saveLayer() if there’s an overflowShader

[Text][]— 当有 overflowShader 时,会调用saveLayer()

避免调用 saveLayer() 的方式:

  • 要在图像中实现淡入淡出,请考虑使用 FadeInImage widget,该 widget 使用 GPU 的片段着色器应用渐变不透明度。了解更多详情,请参见 Opacity 文档。

  • 要创建带圆角的矩形,而不是应用剪切矩形,请考虑使用很多 widget 都提供的 borderRadius属性。

对列表和网格列表懒加载

在构建大型网格或列表时,使用带有回调的惰性方法。这样,只有屏幕的可见部分是在开始时构建的。

请参阅:

在 16ms 内渲染完成每一帧

由于构建和渲染有两个独立的线程,因此构建时间为 16ms,60Hz 显示器上渲染时间为 16ms。如果需要考虑延迟,就要在 16ms 或更短 的时间内构建和显示帧。请注意,这意味着构建需要少于 8ms,渲染也需要少于 8ms,总计 16ms 或更短。如果需要考虑丢帧(jankyness),那么每个构建和渲染阶段的 16ms 都可以。

如果在 profile 构建 状态下,每一帧渲染时间低于 16ms,你可能不必担心性能问题以及一些性能陷阱,但仍然应该致力于尽可能快地渲染每一帧。为什么?

  • 将帧渲染时间降低到 16ms 以下可能在视觉上看不出来什么变化,但可以延长电池寿命以及避免发热问题。

  • 可能在你当前测试设备上运行良好,但请考虑在应用所支持的最低端设备上的情况。

  • 当 120fps 的设备普及之后,便需要在 8ms 之内完成每一帧的渲染来保证流畅平滑的体验。

如果你想弄明白为什么 60fps 会带来平滑的视觉体验,请看视频 Why 60fps?

陷阱

如果你需要调整应用程序的性能,或者 UI 顺畅度没达到你的预期,那么 IDE 的 Flutter plugin 可以提供帮助。在 Flutter Performance 窗口中,勾选 Show widget rebuild information 复选框。此功能可帮助你检测帧的渲染和显示时间是否超过 16ms。在可能的情况下,插件提供指向相关提示的链接。

以下行为可能会对您应用的性能产生负面影响。

  • 避免使用 Opacity widget,尤其是在动画中避免使用。请用 AnimatedOpacityFadeInImage 进行代替。更多信息,请参阅Performance considerations for opacity animation

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

  • 避免在动画中剪裁。如果可能,请在动画开始之前预先剪切图像。

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

Resources

要了解更多性能信息,请参见以下资源: