????????对于Flutter的性能优化,Google官方在frameWork层做了很多优化,包括渲染机制中的diff算法,因此Flutter的性能是非常强大的。那么做Flutter的性能优化,我们只需要关注widget层的性能优化,以确保我们的flutter项目能在60fps的屏幕上丝丝润滑,甚至是120fps的屏幕上。我对flutter的性能优化分为以下几点:
1.widget build()方法避免执行重复耗时的非必要操作
????????避免在widget或者state的build() 方法中进行重复且耗时的非必要工作,因为当父 widget 重建时,子 widget 的?build() ?方法会被频繁地调用。因此确保非必要的耗时工作不放在build()方法中。
2.控制widget setState()的重建范围
- 在StatefulWidget中调用setState()会引起该widget的重建,会调用state的build()方法。当一个页面只有一个StatefulWidget,把全部widget的状态都放在这个StatefulWidget,并且该StatefulWidget为页面最顶端的父widget时,setState方法会让整个页面的widget重建。因此,将一个页面中的widget进行多个StatefulWidget的状态划分,每个StatefulWidget只负责自己的状态维护,将大大缩小flutter页面绘制范围。
3.控制widget重建次数
- 不会改变的widget使用const,如Text、Icon、Image等,这样可以复用这部分widget,不会导致widget重建
child: const Text(
'登录',
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
fontWeight: FontWeight.w400),
), - 动画使用AnimatedBuilder时,将不需要动的子widget赋值给child参数,builder方法中使用该child,可以做到复用子widget,以避免在动画过程中重建其后代 widget。
AnimatedBuilder(
animation: animation,
child: 子widget,//替换成你的子widget
builder: (_, Widget? child) {
return 需要动画的widget(//替换成你的widget
value: animation.value,
child: child!
);
}
), - 使用CustomPaint自定义组件的时候,使用重写shouldRepaint方法,返回false即不重绘,true为重绘,我们可以根据条件返回true,减少自定义组件的重绘次数
4.尽量避免saveLayer操作
? ??saveLayer方法是Flutter框架中最重量的操作之一。更新屏幕时这个方法很有用,但它可能使应用变慢,如果不是必须的话,应该避免使用这个方法。即便没有显式地调用saveLayer,也可能在其他操作中间接调用了该方法。有两个方法可以检查页面是否使用saveLayer,1.可以使用在MeterialApp中时使用checkerboardOffscreenLayers属性开关来检查当前界面是否使用了saveLayer,打开开关之后,运行应用并检查是否有图像的轮廓闪烁。如果有新的帧渲染的话,容器就会闪烁;2.使用flutter screenshot --type=skia --observatory-url=这里填timeline的观察台地址,生成skp文件,再上传到 https://debugger.skia.org/,就可详细分析页面中的saveLayer的调用,推荐该方法,第一种方法有的saveLayer无法检查出来。
????在官网中可以看到,以下组件会触发saveLayer,应尽量避免使用,寻找其他代替。透明度(Opacity)、裁剪(clipping)、阴影(shadows)以及文字(Text )
5.使用懒加载、按需加载模型Slive
? ? ? ? 滚动列表中SingleChileScrollView不支持Slive,会直接加载整个子widget,如果滑动部分很长,请使用ListView、gridView等支持按需加载模型的列表,并且使用ListView.builder或者ListView.separated加载子项。
6.使用RepaintBoundary
? ? ? ??使用RepaintBoundary,给页面设置重绘范围,将提高我们的性能。比如页面滑动不需要重绘动画,使用RepaintBoundary包住我们的动画widget,页面滑动将不会导致动画重绘。还可以用该widget包住我们的图片,做图片缓存,ListView里面就使用了RepaintBoundary来包住item,缓存item。
7.复用Element
? ? ? ? element tree是flutter三棵树之一,我个人把他当作渲染树的manager,widget内部有个canUpdate方法。
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
? ? ? ? 在setState方法调用后,页面会刷新,widget tree会重建,element内部的diff算法会根据对应widget去比较旧widget(oldWidget)和新widget(newWidget),如果他们的runtimeType(类型)和key相同,则会复用该element节点,不会重建,这将节约很多性能。因此我们在写widget tree时,尽量减少widget类型的改变和整体结构的改变,比如使用Visibility控件替换if/else;对于key,在一个多节点的列表中,请给每个节点一个唯一key,这样可以做到复用element。
8.耗时任务放多线程
? ? ? ? flutter是单线程模型,其中的异步方法future并没有开启新的线程,而是依靠事件循环机制,将异步方法放到了优先级低的Event queue队列,同步方法执行完再执行异步方法。所以如果把非常耗时的任务放到异步方法中,比如加密解密、文件读写、下载大文件,有可能会阻塞单线程,造成app卡顿。因此我们可以使用isolate,这个是一个真正的隔离线程,内存不共享,flutter为我们提供了compute方法来便捷使用isolate,将耗时方法交给isolate,将不影响主线程的性能。
|