# Flutter 应用性能优化最佳实践

> 确保 Flutter 应用的性能。



:::note

To learn how to use the **Performance View**
(part of Flutter DevTools)
for debugging performance issues,
see [Using the Performance view][].

了解如何使用 **性能视图**（Flutter DevTools 的一部分）
进行性能问题排查，请查看 [使用性能视图 (Performance view)][Using the Performance view]。

:::

[Using the Performance view]: /tools/devtools/performance


Generally, Flutter applications are performant by default,
so you only need to avoid common pitfalls to get excellent
performance. These best practice recommendations will help you
write the most performant Flutter app possible.

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

:::note

If you are writing web apps in Flutter, you might be interested
in a series of articles, written by the Flutter Material team,
after they modified the [Flutter Gallery][] app to make it more
performant on the web:

如果你在用 Flutter 编写 Web 应用，你可能会对下面的系列文章感兴趣，
他们由 Flutter Material 团队撰写，记录了对 [Flutter Gallery][] 应用的修改，
使其在 Web 上的性能更好:

* [Optimizing performance in Flutter web apps with tree
  shaking and deferred loading][web-perf-1]

  [通过 tree shaking 和延迟加载来优化 Flutter Web 应用的性能 
  (Optimizing performance in Flutter web apps with 
  tree shaking and deferred loading)][web-perf-1]

* [Improving perceived performance with image placeholders,
  precaching, and disabled navigation transitions][web-perf-2]

  [通过使用图像占位符、预缓存和禁用导航效果来提高性能 
  (Improving perceived performance with image placeholders, 
  precaching, and disabled navigation transitions)][web-perf-2]

* [Building performant Flutter widgets][web-perf-3]

  [高效构建 Flutter widgets (Building performant Flutter widgets)][web-perf-3]

:::

[Flutter Gallery]: https://flutter-gallery-archive.web.app
[web-perf-1]: https://blog.flutter.dev/optimizing-performance-in-flutter-web-apps-with-tree-shaking-and-deferred-loading-535fbe3cd674
[web-perf-2]: https://blog.flutter.dev/improving-perceived-performance-with-image-placeholders-precaching-and-disabled-navigation-6b3601087a2b
[web-perf-3]: https://blog.flutter.dev/building-performant-flutter-widgets-3b2558aa08fa

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?
Some rendering and layout operations are known
to be slow, but can't always be avoided.
They should be used thoughtfully,
following the guidance below.

如何设计一个能最有效地渲染页面的 Flutter 应用程序？
特别是如何确保底层框架生成的绘图代码尽可能高效？
你需要经过谨慎地思考之后再使用那些已知性能很低的渲染和布局操作。
请遵循下面的指导。

## Minimize expensive operations

## 尽量减少消耗资源的操作

Some operations are more expensive than others,
meaning that they consume more resources.
Obviously, you want to only use these operations
when necessary. How you design and implement your
app's UI can have a big impact on how efficiently it runs.

有些操作特别消耗资源。
很显然，你只想在必要地时候使用这些操作。
在这种情况下，如何设计和实现应用的 UI，对运行效率有很大影响。

### Control build() cost

### 控制 build() 方法的耗时

Here are some things to keep in mind when designing your UI:

以下是在设计你的 UI 时的一些建议：

* 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` object,
    all descendent widgets 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 inside the framework for optimizing
    animations where the animation doesn't affect the child subtree.
    See the [`TransitionBuilder`][] pattern and
    the [source code for `SlideTransition`][],
    which uses this principle to avoid rebuilding its
    descendents when animating.
    ("Same instance" is evaluated using `operator ==`,
    but see the pitfalls section at the end of this page
    for advice on when to avoid overriding `operator ==`.)

    当重新遇到与前一帧相同的子 widget 实例时，将停止遍历。
    这种技术在框架内部大量使用，用于优化动画不影响子树的动画。
    请参阅 [`TransitionBuilder`][] 模式和遵循此原则的 
    [SlideTransition 代码][source code for `SlideTransition`]，
    以避免在动画过程中重建其后代 widget。
    （“相同的实例”是使用 `operator ==` 进行评估的，
    但是请参阅本页面末尾的陷阱部分，了解有关何时避免覆盖 `operator ==` 的建议。）

  * Use `const` constructors on widgets as much as possible,
    since they allow Flutter to short-circuit most
    of the rebuild work. To be automatically reminded
    to use `const` when possible, enable the
    recommended lints from the [`flutter_lints`][] package.
    For more information, check out the
    [`flutter_lints` migration guide][].

    请尽可能地在 widget 上使用 `const` 构造函数，
    这将让 Flutter 的 widget 重建时间大幅缩短。
    要自动提醒使用 `const`，请启用 [`flutter_lints`][] package 中推荐的 lints。
    了解更多信息，请查看 [`flutter_lints` 迁移指南][`flutter_lints` migration guide].

  * To create reusable pieces of UIs,
    prefer using a [`StatelessWidget`][]
    rather than a function.

    在构建可复用的 UI 代码时，
    最好使用 [`StatelessWidget`][] 而不是函数。

For more information, check out:

了解更多信息，请查看：

* [Performance considerations][],
  part of the [`StatefulWidget`][] API doc

  [`StatefulWidget`][] API 文档中的 [Performance considerations][] 部分。

* [Widgets vs helper methods][],
  a video from the official Flutter YouTube
  channel that explains why widgets
  (especially widgets with `const` constructors)
  are more performant than functions.

  [Widget 与 helper 方法][Widgets vs helper methods]，
  Flutter 关于为什么使用 widget 是更好的做法的解析视频。
  （特别是使用 `const` 构造的 widget）

[`flutter_lints`]: https://pub-web.flutter-io.cn/packages/flutter_lints
[`flutter_lints` migration guide]: /release/breaking-changes/flutter-lints-package#migration-guide
[Performance considerations]: https://api.flutter-io.cn/flutter/widgets/StatefulWidget-class.html#performance-considerations
[source code for `SlideTransition`]: https://github.com/flutter/flutter/blob/main/packages/flutter/lib/src/widgets/transitions.dart#L168
[`StatefulWidget`]: https://api.flutter-io.cn/flutter/widgets/StatefulWidget-class.html
[`StatelessWidget`]: https://api.flutter-io.cn/flutter/widgets/StatelessWidget-class.html
[`TransitionBuilder`]: https://api.flutter-io.cn/flutter/widgets/TransitionBuilder.html
[Widgets vs helper methods]: https://www.youtube.com/watch?v=IOyq-eTRhvo

---

### Use StringBuffer for efficient string building

When you need to build a string from multiple parts, especially inside a loop,
using the `+` operator can be inefficient because it creates a new `String`
object on each concatenation. A better approach is to use `StringBuffer`,
which collects all the strings and concatenates them only once, when you call
`toString()`.

<YouTubeEmbed id="xSsFtDY-nOw" title="StringBuffer (Technique of the Week)"></YouTubeEmbed>

---

### Use saveLayer() thoughtfully

### 谨慎使用 saveLayer()

Some Flutter code uses `saveLayer()`, an expensive operation,
to implement various visual effects in the UI.
Even if your code doesn't explicitly call `saveLayer()`,
other widgets or packages that you use might call it behind the scenes.
Perhaps your app is calling `saveLayer()` more than necessary;
excessive calls to `saveLayer()` can cause jank.

一些 Flutter 代码调用了性能代价很大的 `saveLayer()` 方法来实现 UI 中的各种视觉效果。
你使用的其他 widget 或者 package 可能会在幕后调用它。
也许你的应用程序会大量调用 `saveLayer()`；
过多调用 `saveLayer()` 会导致卡顿。

#### Why is saveLayer expensive?

#### 为什么 saveLayer 代价很大？

Calling `saveLayer()` allocates an offscreen buffer
and drawing content into the offscreen buffer might
trigger a render target switch.
The GPU wants to run like a firehose,
and a render target switch forces the GPU
to redirect that stream temporarily and then
direct it back again. On mobile GPUs this is
particularly disruptive to rendering throughput.

调用 `saveLayer()` 会开辟一片离屏缓冲区
并将内容绘制到离屏缓冲区可能会触发渲染目标切换。
GPU 希望直达目标地运行，
但渲染目标迫使 GPU 暂时重定向到该数据流，然后又直接把它切回来。
这样会对渲染吞吐量造成特别大的破坏性。

#### When is saveLayer required?

#### 什么时候需要 saveLayer？

At runtime, if you need to dynamically display various shapes
coming from a server (for example), each with some transparency,
that might (or might not) overlap,
then you pretty much have to use `saveLayer()`.

在运行时，如果你需要动态地显示各种形状效果（例如），
每个形状都有一定地透明度，可能（或可能不）重叠，
那么你几乎必须使用 `saveLayer()`。

#### Debugging calls to saveLayer

#### 调试 saveLayer 的调用

How can you tell how often your app calls `saveLayer()`,
either directly or indirectly?
The `saveLayer()` method triggers
an event on the [DevTools timeline][]; learn when
your scene uses `saveLayer` by checking the
`PerformanceOverlayLayer.checkerboardOffscreenLayers`
switch in the [DevTools Performance view][].

你如何才能知道应用程序调用 `saveLayer()` 的频率，直接还是间接调用？
`saveLayer()` 方法会触发 [DevTools timeline][] 上的一个事件；
通过检查 [DevTools 性能视图 (DevTools Performance view)][DevTools Performance view]
中的 `PerformanceOverlayLayer.checkerboardOffscreenLayers` 来了解何时在使用 `saveLayer`。

[DevTools timeline]: /tools/devtools/performance#timeline-events-tab

#### Minimizing calls to saveLayer

#### 尽量减少 saveLayer 的调用

Can you avoid calls to `saveLayer`?
It might require rethinking of how you
create your visual effects:

你能避免对 `saveLayer` 的调用吗？
这可能需要你重新思考如何创建视觉效果：

* If the calls are coming from _your_ code, can you
  reduce or eliminate them?
  For example, perhaps your UI overlaps two shapes,
  each having non-zero transparency:

  如果这些调用是来自于 **你** 的代码，你能减少或消除它们吗？
  例如，也许你的用户界面重叠了两个形状，每个形状都有非零的透明度：

  * If they always overlap in the same amount,
    in the same way, with the same transparency,
    you can precalculate what this overlapped,
    semi-transparent object looks like, cache it,
    and use that instead of calling `saveLayer()`.
    This works with any static shape you can precalculate.

    如果它们总是以相同的数量、相同的方式、相同的透明度进行重叠，
    你就可以预先计算出这个重叠的、半透明物体的样子，将其缓存起来，
    然后用它代替调用 `saveLayer()`。
    这适用于任何你可以预先计算的静态形状。

  * Can you refactor your painting logic to avoid
    overlaps altogether?

    你可以换一种绘画形式以完全避免重叠吗？



* If the calls are coming from a package that you don't own,
  contact the package owner and ask why
  these calls are necessary. Can they be reduced or
  eliminated? If not, you might need to find another
  package, or write your own.

  如果这些调用不是来自你的 package，
  请联系 package 的所有者并询问为什么需要使用这些调用。
  能否减少或消除它们？
  如果不能，你可能需要寻找其他作者的 package，或自己编写。

:::note package 的所有者请注意
<!-- Note to package owners -->

As a best practice, consider providing documentation
for when `saveLayer` might be necessary for your package,
how it might be avoided, and when it can't be avoided.

最好的做法是在文档中说明你的 package 什么时候会用到 `saveLayer`，
如何避免它，以及什么时候无法避免。

:::

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

其他会触发 `saveLayer()` 的 widget，也有潜在的性能代价：

* [`ShaderMask`][]
* [`ColorFilter`][]
* [`Chip`][]&mdash;might trigger a call to `saveLayer()` if
  `disabledColorAlpha != 0xff`

  [`Chip`][]&mdash;
  当 `disabledColorAlpha != 0xff` 的时候，会调用 `saveLayer()`

* [`Text`][]&mdash;might trigger a call to `saveLayer()`
  if there's an `overflowShader`

  [`Text`][]&mdash;
  当有 `overflowShader` 时，会调用`saveLayer()`

[`Chip`]: https://api.flutter-io.cn/flutter/material/Chip-class.html
[`ColorFilter`]: https://api.flutter-io.cn/flutter/dart-ui/ColorFilter-class.html
[`FadeInImage`]: https://api.flutter-io.cn/flutter/widgets/FadeInImage-class.html
[`Opacity`]: https://api.flutter-io.cn/flutter/widgets/Opacity-class.html
[`ShaderMask`]: https://api.flutter-io.cn/flutter/widgets/ShaderMask-class.html
[`Text`]: https://api.flutter-io.cn/flutter/widgets/Text-class.html
[Transparent image]: https://api.flutter-io.cn/flutter/widgets/Opacity-class.html#transparent-image

---

### Minimize use of opacity and clipping

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

Opacity is another expensive operation, as is clipping.
Here are some tips you might find to be useful:

不透明度和裁剪都是特别消耗资源的操作。
这里有一些建议也许对你有帮助：

* 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 更快。

* Instead of wrapping simple shapes or text
  in an `Opacity` widget, it's usually faster to
  just draw them with a semitransparent color.
  (Though this only works if there are no overlapping
  bits in the to-be-drawn shape.)

  与其将简单的形状或文本包裹在一个 `Opacity` widget 中，
  不如用半透明的颜色来绘制它们会更快。
  （这仅在要画的形状中没有重叠的部分时有效）。

* 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, check out the [`Opacity`][] docs.

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

* **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` 那么耗时，但仍然很耗时，所以请谨慎使用。

* 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` 属性。

---

### Implement grids and lists thoughtfully

### 谨慎使用网格列表和列表

How your grids and lists are implemented
might be causing performance problems for your app.
This section describes an important best
practice when creating grids and lists,
and how to determine whether your app uses
excessive layout passes.

你的网格列表和列表的实现方式可能会给你的应用程序带来性能问题。
本节介绍了创建网格列表和列表时的一个重要的最佳实现方式，
以及如何确定你的应用程序是否使用了过多的布局传递。

#### Be lazy!

#### 懒加载！

When building a large grid or list,
use the lazy builder methods, with callbacks.
That ensures that only the visible portion of the
screen is built at startup time.

当构建一个大型网格列表或列表时，
使用带有回调的懒惰构建器方法。
这可以确保在启动时只构建屏幕的可见部分。

For more information and examples, check out:

了解更多的信息和示例，请查看：

* [Working with long lists][]

  [长列表的处理][Working with long lists]

* [Creating a `ListView` that loads one page at a time][]
  a community article by AbdulRahman AlHamali

  来自社区的 AbdulRahman AlHamali 撰写的文章
  [Creating a `ListView` that loads one page at a time][]

* [`ListView.builder`][] API

[Creating a `ListView` that loads one page at a time]: https://medium.com/saugo360/flutter-creating-a-listview-that-loads-one-page-at-a-time-c5c91b6fabd3
[`ListView.builder`]: https://api.flutter-io.cn/flutter/widgets/ListView/ListView.builder.html
[Working with long lists]: /cookbook/lists/long-lists

#### Avoid intrinsics

#### 避免内部传递

For information on how intrinsic passes might be causing
problems with your grids and lists, see the next section.

关于内部传递可能导致你的网格列表和列表出现问题的信息，见下一节。

---

### Minimize layout passes caused by intrinsic operations

### 尽量减少由内部操作引起的布局传递

If you've done much Flutter programming, you are
probably familiar with [how layout and constraints work][]
when creating your UI. You might even have memorized Flutter's
basic layout rule: **Constraints go down. Sizes go up.
Parent sets position.**

如果你经常使用 Flutter 编程，
你可能已经熟悉了在创建 UI 时 [布局和约束是如何工作的][how layout and constraints work]。
你甚至可能已经记住了 Flutter 的基本布局规则：<br/>
**首先，上层 widget 向下层 widget 传递约束条件；**<br/>
**然后，下层 widget 向上层 widget 传递大小信息；**<br/>
**最后，上层 widget 决定下层 widget 的位置。**<br/>

For some widgets, particularly grids and lists,
the layout process can be expensive.
Flutter strives to perform just one layout pass
over the widgets but, sometimes,
a second pass (called an _intrinsic pass_) is needed,
and that can slow performance.

对于一些 widget，特别是网格列表和列表，
布局过程中性能代价很大。
Flutter 尽可能在 widget 上只执行一次布局传递，
但有时需要再次传递（称为 **内部传递**），
这可能会降低性能。

#### What is an intrinsic pass?

#### 什么是内部传递？

An intrinsic pass happens when, for example,
you want all cells to have the size
of the biggest or smallest cell (or some
similar calculation that requires polling all cells).

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

For example, consider a large grid of `Card`s.
A grid should have uniformly sized cells,
so the layout code performs a pass,
starting from the root of the grid (in the widget tree),
asking **each** card in the grid (not just the
visible cards) to return
its _intrinsic_ size&mdash;the size
that the widget prefers, assuming no constraints.
With this information,
the framework determines a uniform cell size,
and re-visits all grid cells a second time,
telling each card what size to use.

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

#### Debugging intrinsic passes

#### 调试内部传递

To determine whether you have excessive intrinsic passes,
enable the **[Track layouts option][]**
in DevTools (disabled by default),
and look at the app's [stack trace][]
to learn how many layout passes were performed.
Once you enable tracking, intrinsic timeline events
are labeled as '$runtimeType intrinsics'.

要确认你是否有过多的内部传递，
请在 DevTools 中启用 **[布局跟踪 (Track layouts option)][Track layouts option]** （默认情况下是禁用的），
并查看应用程序的 [堆栈跟踪 (stack trace)][stack trace] 以了解执行了多少次布局传递。
一旦你启用跟踪，内部的时间线事件将被标记为 "$runtimeType intrinsics"。

#### Avoiding intrinsic passes

#### 避免内部传递

You have a couple options for avoiding the intrinsic pass:

你有几个选择来避免内部传递：

* Set the cells to a fixed size up front.

  将单元格设置为固定大小。

* Choose a particular cell to be the
  "anchor" cell&mdash;all cells will be
  sized relative to this cell.
  Write a custom [`RenderObject`][] that
  positions the child anchor first and then lays
  out the other children around it.

  选择一个特定的单元格作为 “锚” &mdash;所有单元格的尺寸都将相对于这个单元格。
  编写一个自定义的 [`渲染对象 (RenderObject)`][`RenderObject`]，
  首先定位子锚点，然后在其周围布置其他子单元。

To dive even deeper into how layout works,
check out the [layout and rendering][]
section in the [Flutter architectural overview][].

要更深入地了解布局的工作原理，
请查看 [Flutter 架构概览][Flutter architectural overview] 中的 [布局和渲染][layout and rendering]。

[Flutter architectural overview]: /resources/architectural-overview
[how layout and constraints work]: /ui/layout/constraints
[layout and rendering]: /resources/architectural-overview#layout-and-rendering
[stack trace]: /tools/devtools/cpu-profiler#flame-chart
[Track layouts option]: /tools/devtools/performance#track-layouts

---

### 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.

由于构建和渲染有两个独立的线程，因此构建时间为 16ms，60Hz 显示器上渲染时间为 16ms。
如果需要考虑延迟，就要在 16ms **或更短** 的时间内构建和显示帧。
请注意，这意味着构建需要少于 8ms，渲染也需要少于 8ms，总计 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 构建][profile mode] 状态下，
每一帧渲染时间低于 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.

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

* As 120fps devices become more 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,
check out the video [Why 60fps?][]

如果你想弄明白为什么 60fps 会带来平滑的视觉体验，
请看视频 [60fps 是啥意思？][Why 60fps?]

[profile mode]: /testing/build-modes#profile
[Why 60fps?]: https://www.bilibili.com/video/BV1b441157sA/

## Pitfalls

## 陷阱

If you need to tune your app's performance,
or perhaps the UI isn't as smooth as you expect,
the [DevTools Performance view][] can help!

如果你需要改善应用程序的性能，
或者 UI 流畅度没达到你的预期，
那么 [DevTools 性能视图 (DevTools Performance view)][DevTools Performance view] 可以帮到你！

Also, the Flutter plugin for your IDE might
be useful. 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.

另外，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, check out
  [Performance considerations for opacity animation][].

  避免使用 `Opacity` widget，尤其是在动画中避免使用。
  可以使用 `AnimatedOpacity` 或 `FadeInImage` 代替该操作。
  更多信息，请参阅
  [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,
  check out [Performance optimizations][].

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

* 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.

* Avoid overriding `operator ==` on `Widget` objects.
  While it might seem like it would help by avoiding unnecessary rebuilds,
  in practice it hurts performance because it results in O(N²) behavior.
  The only exception to this rule is leaf widgets (widgets with no children),
  in the specific case where comparing the properties of the widget
  is likely to be significantly more efficient than rebuilding the widget
  and where the widget will rarely change configuration.
  Even in such cases,
  it is generally preferable to rely on caching the widgets,
  because even one override of `operator ==`
  can result in across-the-board performance degradation
  as the compiler can no longer assume that the call is always static.

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

* Avoid overriding `operator ==` on `Widget` objects.
  While it may seem like it would help by avoiding unnecessary rebuilds,
  in practice it hurts performance because it results in O(N²) behavior.
  The only exception to this rule is leaf widgets (widgets with no children),
  in the specific case where comparing the properties of the widget
  is likely to be significantly more efficient than rebuilding the widget
  and where the widget will rarely change configuration.
  Even in such cases,
  it is generally preferable to rely on caching the widgets,
  because even one override of `operator ==`
  can result in across-the-board performance degradation
  as the compiler can no longer assume that the call is always static.

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

## Resources

## 参考资料

For more performance info, check out the following resources:

了解更多性能信息，请参阅以下资源：

* [Performance optimizations][] in the AnimatedBuilder API page

  AnimatedBuilder API 页面的 [Performance optimizations][] 部分；

* [Performance considerations for opacity animation][]
  in the Opacity API page

  Opacity API 页面的 [Performance considerations for opacity animation][] 部分；

* [Child elements' lifecycle][] and how to load them efficiently,
  in the ListView API page

  ListView API 页面中 [Child elements' lifecycle][]，以及如何高效加载元素；

* [Performance considerations][] of a `StatefulWidget`
* [Best practices for optimizing Flutter web loading speed][best-practices-medium]

  `StatefulWidget` 的 [Performance considerations][] API 文档。

[Child elements' lifecycle]: https://api.flutter-io.cn/flutter/widgets/ListView-class.html#child-elements-lifecycle
[`CustomPainter`]: https://api.flutter-io.cn/flutter/rendering/CustomPainter-class.html
[DevTools Performance view]: /tools/devtools/performance
[Performance optimizations]: https://api.flutter-io.cn/flutter/widgets/AnimatedBuilder-class.html#performance-optimizations
[Performance considerations for opacity animation]: https://api.flutter-io.cn/flutter/widgets/Opacity-class.html#performance-considerations-for-opacity-animation
[`RenderObject`]: https://api.flutter-io.cn/flutter/rendering/RenderObject-class.html
[best-practices-medium]: https://blog.flutter.dev/best-practices-for-optimizing-flutter-web-loading-speed-7cc0df14ce5c

