在实际的Flutter开发中,能够发现编辑器AS会提示在组件之前加上const关键字,
这是由于Flutter2之后,多了一个linter规则,prefer_const_constructors,官方主张首选运用const来实例化常量结构函数。
那const效果是什么?并且在功能方面对整个app有多大的提升?
一、Const的效果
const 是 constant 的缩写,原意是不变的,不易改变的意思,包括C++、go中都有此关键字,相同的,在Flutter中也是表示不变的意思。具体来看看下面的代码。
Row(
children: [
Image(image: NetworkImage('https://www.6hu.cc/storage/2023/02/1676915306-115c89dbff48ef4.jpg')),
Text("$_counter")
],
);
这是一个水平布局,内部排列了一个Image和Text,留意这个Text的是有一个动态的值_counter。
为了能够更新_counter,必定要调用setState() 办法。咱们都知道,假如调用setState() ,那么整个Row包括Image和Text都会主动递归重建。每调用一次,父widget和子widget都会重建一次,那么在杂乱的UI和事务场景下,就加深了app的不稳定性。
这就是为什么在开发中,要尽量在小的规模去运用setState,防止不必要的重建任务。为了优化这个问题,官方就更新出了const关键字,被const润饰的widget,就代表永远不会被重建。
比方在上述代码中Image是不可变的,Text是可变的,那么在Image之间加上const润饰,当调用setState() 时,只会更新Text,Image不会被重新构建。
Row(
children: [
const Image(image: NetworkImage('https://www.6hu.cc/storage/2023/02/1676915306-115c89dbff48ef4.jpg')),
Text("$_counter")
],
);
二、功能剖析
2.1 widget rebuild状况
DevTools供给了一个查询widget rebuild状况的东西,在 Widget rebuild stats 中勾选 Track widget rebuilds 来查看 widget 的重建信息。重建信息包括 Widget 名字、源码方位、上一帧中重建次数、当时页面中重建次数。
在每个widget之前都有一个小图标,
- 黄色旋转圆圈 – 重建次数过多
- 灰色圆圈 – 未重建
- 灰色旋转圆圈 – 重建
为了进行const比照,咱们以上面代码为例,
Row(
children: [
const Image(image: NetworkImage('https://www.6hu.cc/storage/2023/02/1676915306-115c89dbff48ef4.jpg')),
Text("$_counter")
],
);
在Image前加上const,Text则不加,当调用setState时,观察两个widget的状况。
清楚的发现,没加const的Image widget前面的圆圈在旋转,则表示Image在重建,且重建次数+1。
2.2 内存占用
关于内存,DevTool相同供给了内存剖析东西Memory,接下来结合事例进行剖析。
在项目中新建两个类,内部不做额外的动作,
void _buildConstObject(){
const ConstObject();
}
void _buildConstObjectNot(){
ConstObjectNot();
}
其中ConstObject 加上const润饰,ConstObjectNot则不进行润饰,在触发build时,两个目标一起进行1000次的创立,
void _doBuild(){
for(var i = 0; i< 1000;i++){
_buildConstObject();
_buildConstObjectNot();
}
}
翻开内存剖析东西,能够发现未加Const润饰的ConstObjectNot创立了1000个目标,所占用内存约16k,而加了const的ConstObject则能够忽略不计。
留意这儿ConstObjectNot和ConstObject内部是没有做任何widget创立的,假如在实际杂乱的项目中,未运用const,内存将成倍增加。
2.3 流畅性
在DevTool中翻开performance overlay, 在app顶部就会呈现功能图层,这两张图表显现的是运用的耗时信息。假如 UI 产生了卡顿(跳帧),这些图表能够协助剖析运用中卡顿,每一张图表都代表当时线程的最近 300 帧体现。
如上图,第一张图归于raster 线程的功能状况即GPU功能,第二张图显现的UI线程功能体现。
当中垂直的绿色条条代表的是当时帧。每一帧都应该在 1/60 秒(大约 16 ms)内创立并显现。假如有一帧超时(任意图画)而无法显现,就导致了卡顿,图表之一就会展示出来一个赤色竖条。假如是在 UI 图表呈现了赤色竖条,则标明 Dart 代码耗费了大量资源。而假如赤色竖条是在 GPU 图表呈现的,意味着场景太杂乱导致无法快速烘托。
为了验证流畅性,咱们开启了一个动画,动画在规定时间内进行重复性的扩大缩小动作,且分为两个场景,一个场景是在所有widget以及目标前加上const润饰,另外一个场景则什么都不做,比照查看每帧的耗时。
class AnLogo extends AnimatedWidget {
static final _opacityTween = Tween<double>(begin: 0.1, end: 1.0);
static final _sizeTween = Tween<double>(begin: 0.0, end: 300.0);
const AnLogo({Key? key, required Animation<double> animation})
: super(key: key, listenable: animation);
@override
Widget build(BuildContext context) {
Animation<double> animation1 = listenable as Animation<double>;
return Scaffold(
appBar: AppBar(
title: const Text("动画"),
),
body: Center(
child: Opacity(
opacity: _opacityTween.evaluate(animation1),
child: Container(
margin: const EdgeInsets.symmetric(vertical: 10.0),
height: _sizeTween.evaluate(animation1),
width: _sizeTween.evaluate(animation1),
child: Image.asset("images/ic_1.jpeg"),
),
),
),
);
}
}
no const | const |
---|---|
no const | const |
---|---|
GPU帧率:
GPU | |
---|---|
no const均匀最大耗时/帧 | 9.9ms/frame |
const均匀最大耗时/帧 | 7.6ms/frame |
UI线程帧率:
UI线程 | |
---|---|
no const均匀最大耗时/帧 | 7.8ms/frame |
const均匀最大耗时/帧 | 7.1ms/frame |
从试验结果上看,没有加const的GPU帧率均匀最大到达9.9ms/帧,而加了const的GPU帧率比之降低了约2.3ms;UI帧率(CPU)加const与不加const相差不大,约0.7ms。
三、总结
从上面的测试看,不管是内存占用仍是流畅性,添加const润饰的功能都是优于未添加const润饰的功能,const减少了组件的重建以及目标的创立,进行flutter开发时,在合适的时机去运用const以减少不必要的开支。
引荐阅读:
Flutter实战项目开源
Flutter: ‘ const ‘结构函数的功能剖析