Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
339 lines (237 sloc) 14.8 KB
title short-title description
Flutter 应用性能优化最佳实践
最佳实践
确保 Flutter 应用的性能。

Generally, Flutter applications are performant by default, so you only need to avoid common pitfalls to get excellent performance instead of needing to micro-optimize with complicated profiling tools. These best recommendations will help you write the most performant Flutter app possible.

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

Best practices

最佳实践

How do you design a Flutter app to most efficiently render your scenes? In particular, how do you ensure that the painting code generated by the framework is as efficient as possible? Here are a few things to consider when designing your app:

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

Controlling build() cost

控制 build() 方法的耗时

  • Avoid repetitive and costly work in build() methods since build() can be invoked frequently when ancestor Widgets rebuild.

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

  • Avoid overly large single Widgets with a large build() function. Split them into different Widgets based on encapsulation but also on how they change:

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

    • When setState() is called on a State, all descendent widgets will rebuild. Therefore, localize the setState() call to the part of the subtree whose UI actually needs to change. Avoid calling setState() high up in the tree if the change is contained to a small part of the tree.

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

    • The traversal to rebuild all descendents stops when the same instance of the child widget as the previous frame is re-encountered. This technique is heavily used in inside the framework for optimizing animations where the animation doesn't affect the child subtree. See the TransitionBuilder pattern and the SlideTransition, which uses this principle to avoid rebuilding its descendents when animating.

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

Also see:

另见:

Apply effects only when needed

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

Use effects carefully, as they can be expensive. Some of them invoke saveLayer() behind the scenes, which can be an expensive operation.

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

{{site.alert.secondary}}

Why is savelayer expensive?

为什么 saveLayer 代价很大?

Calling saveLayer() allocates an offscreen buffer. Drawing content into the offscreen buffer might trigger render target switches that are particularly slow in older GPUs.

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

{{site.alert.end}}

Some general rules when applying specific effects:

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

  • Use the Opacity widget only when necessary. See the Transparent image section in the Opacity API page for an example of applying opacity directly to an image, which is faster than using the Opacity widget.

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

  • Clipping doesn’t call saveLayer() (unless explicitly requested with Clip.antiAliasWithSaveLayer) so these operations aren’t as expensive as Opacity, but clipping is still costly, so use with caution. By default, clipping is disabled (Clip.none), so you must explicitly enable it when needed.

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

Other widgets that might trigger saveLayer() and are potentially costly:

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

  • ShaderMask

  • ColorFilter

  • Chip—might cause call to saveLayer() if disabledColorAlpha != 0xff

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

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

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

Ways to avoid calls to saveLayer():

避免调用 saveLayer() 的方式:

  • To implement fading in an image, consider using the FadeInImage widget, which applies a gradual opacity using the GPU’s fragment shader. For more information, see the Opacity docs.

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

  • To create a rectangle with rounded corners, instead of applying a clipping rectangle, consider using the borderRadius property offered by many of the widget classes.

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

Render grids and lists lazily

对列表和网格列表懒加载

Use the lazy methods, with callbacks, when building large grids or lists. That way only the visible portion of the screen is built at startup time.

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

Also see:

请参阅:

Build and display frames in 16ms

在 16ms 内渲染完成每一帧

Since there are two separate threads for building and rendering, you have 16ms for building, and 16ms for rendering on a 60Hz display. If latency is a concern, build and display a frame in 16ms or less. Note that means built in 8ms or less, and rendered in 8ms or less, for a total of 16ms or less. If missing frames (jankyness) is a concern, then 16ms for each of the build and render stages is OK.

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

If your frames are rendering in well under 16ms total in profile mode, you likely don’t have to worry about performance even if some performance pitfalls apply, but you should still aim to build and render a frame as fast as possible. Why?

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

  • Lowering the frame render time below 16ms might not make a visual difference, but it improves battery life and thermal issues.

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

  • It might run fine on your device, but consider performance for the lowest device you are targeting.

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

  • When 120fps devices become widely available, you’ll want to render frames in under 8ms (total) in order to provide the smoothest experience.

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

If you are wondering why 60fps leads to a smooth visual experience, see the video Why 60fps?

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

Pitfalls

陷阱

If you need to tune your app’s performance, or perhaps the UI isn't as smooth as you expect, the Flutter plugin for your IDE can help. In the Flutter Performance window, enable the Show widget rebuild information check box. This feature helps you detect when frames are being rendered and displayed in more than 16ms. Where possible, the plugin provides a link to a relevant tip.

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

The following behaviors might negatively impact your app's performance.

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

  • Avoid using the Opacity widget, and particularly avoid it in an animation. Use AnimatedOpacity or FadeInImage instead. For more information, see Performance considerations for opacity animation.

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

  • When using an AnimatedBuilder, avoid putting a subtree in the builder function that builds widgets that don’t depend on the animation. This subtree is rebuilt for every tick of the animation. Instead, build that part of the subtree once and pass it as a child to the AnimatedBuilder. For more information, see Performance optimizations.

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

  • Avoid clipping in an animation. If possible, pre-clip the image before animating it.

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

  • Avoid using constructors with a concrete List of children (such as Column() or ListView()) if most of the children are not visible on screen to avoid the build cost.

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

Resources

For more performance info, see the following resources:

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

You can’t perform that action at this time.