参考:官方文档
控制build()消耗
- 避免在build方法中进行重复和高消耗的工作,因为build方法可能会被频繁的调用(当父组件、祖先组件rebuild时,当前组件的build也会被调用)。
- 尝试将大组件拆分为多个小组件。当一个组件的setState被调用时,它的所有子孙组件也会rebuild。因此如果只是某一个子组件发生了变化,那就尽量只调用该子组件的setState,而不要去调用其父组件或祖先组件的setState。
- 缓存没有变化的子树(If a subtree does not change, cache the widget that represents that subtree and re-use it each time it can be used)。参考下面AnimatedBuilder的做法。
- 尽可能使用const组件(这样做与缓存一个组件并复用它是等价的)。(Use
const
widgets where possible. (This is equivalent to caching a widget and re-using it.)) - 尽量不要改变组件树的结构或者组件树中组件的类型。它们会造成整个组件树的重新布局和渲染。
避免使用代价昂贵的效果
减少使用Opacity组件
直接绘制带有透明度的图片或颜色,比在组件外层套一个Opacity组件要更高效。下面例子展示了如何直接绘制一个带有50%透明度的图片:
Image.network(
'https://raw.githubusercontent.com/flutter/blend_mode_destination.jpeg',
color: Color.fromRGBO(255, 255, 255, 0.5),
colorBlendMode: BlendMode.modulate
)
或者使用AnimatedOpacity、FadeInImage组件替代。
减少使用Clip组件
大部分圆角效果可以通过borderRadius
来实现,并不是非要用Clip组件。
列表懒加载
使用ListView.builder
(当item滚入屏幕时才创建item):
final List<String> entries = <String>['A', 'B', 'C'];
final List<int> colorCodes = <int>[600, 500, 100];
ListView.builder(
padding: const EdgeInsets.all(8),
itemCount: entries.length,
itemBuilder: (BuildContext context, int index) {
return Container(
height: 50,
color: Colors.amber[colorCodes[index]],
child: Center(child: Text('Entry ${entries[index]}')),
);
}
);
而不是ListView-children(会立刻创建所有的item):
ListView(
padding: const EdgeInsets.all(8),
children: <Widget>[
Container(
height: 50,
color: Colors.amber[600],
child: const Center(child: Text('Entry A')),
),
Container(
height: 50,
color: Colors.amber[500],
child: const Center(child: Text('Entry B')),
),
Container(
height: 50,
color: Colors.amber[100],
child: const Center(child: Text('Entry C')),
),
],
)
16ms
Since there are two separate threads for building and rendering, you have 16ms for building, and 16ms for rendering on a 60Hz display.
在Android Studio中提供了性能分析工具“Flutter Performance”,可以查看每一帧的绘制时间和build()方法的调用次数等信息。
使用AnimatedBuilder
如果你的build方法中包含一个不依赖于动画的子树,那么更高效的做法是只build此子树一次,而不是在动画的每一帧都build它。
通过child参数将预构建(pre-built)的子树传递给AnimatedBuilder,然后AnimatedBuilder就会在渲染每一帧动画时将此子树传递给builder方法(从而避免了在渲染每一帧动画时都要去构建子树):
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
@override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget>
with TickerProviderStateMixin {
AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 10),
vsync: this,
)..repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
child: Container(
width: 200.0,
height: 200.0,
color: Colors.green,
child: const Center(
child: Text('Whee!'),
),
),
// 每个动画帧会调用此builder方法
builder: (BuildContext context, Widget child) {
return Transform.rotate(
angle: _controller.value * 2.0 * math.pi,
child: child,
);
},
);
}
}