当前位置: 首页 > news >正文

Flutter渲染优化之RepaintBoundary

前言

了解Flutter的同学应该都或多或少知道Flutter中的三棵树(Widget,Element,RenderObject),其中RenderObject负责绘制逻辑,RenderObject中的paint方法类似于Android中View的Draw方法

Flutter状态更新

Flutter中按有无状态更新可分为两类:StatelessWidget(无状态)和StatefulWidget(有状态),StatefulWidget中创建一个State,在State内部调用setState,等到下一次Vsycn信号过来就会重建更新状态了。

Flutter的渲染流程

在Flutter三棵树中Widget和Element的节点是一一对应,而RenderObject是少于或等于Widget的数量的。当Widget是RenderObjectWidget的派生类的时候才有对应的RenderObject。Element和RenderObject在某些条件下是可以复用的,

Flutter渲染流程

渲染的耗时包括两部分

  • 构建流水线任务
  • 绘制逻辑

从上图可以看出Flutter绘制一帧的任务会先构建三棵树然后再去绘制,因为Element复用的原因所以页面刷新的时候Widget和Element的生命方法并不会重复调用(解决构建耗时性能问题),但是在不使用RepaintBoundary的情况下RenderObject中的paint方法会被频繁调用,接下来我们学习一下Flutter是怎么提升绘制性能的。

RepaintBoundary

RepaintBoundary是集继承
SingleChildRenderObjectWidget,也属于RenderObjectWidget的派生类,所以RepaintBoundary也会有对应的RenderObject。

class RepaintBoundary extends SingleChildRenderObjectWidget {
  const RepaintBoundary({ Key? key, Widget? child }) : super(key: key, child: child);

  factory RepaintBoundary.wrap(Widget child, int childIndex) {
    final Key key = child.key != null ? ValueKey<Key>(child.key!) : ValueKey<int>(childIndex);
    return RepaintBoundary(key: key, child: child);
  }

  static List<RepaintBoundary> wrapAll(List<Widget> widgets) => <RepaintBoundary>[
    for (int i = 0; i < widgets.length; ++i) RepaintBoundary.wrap(widgets[i], i),
  ];

  
  RenderRepaintBoundary createRenderObject(BuildContext context) => RenderRepaintBoundary();
}

RepaintBoundary中创建的RenderObject是RenderRepaintBoundary,下面是RenderRepaintBoundary的代码

class RenderRepaintBoundary extends RenderProxyBox {
  RenderRepaintBoundary({ RenderBox? child }) : super(child);

  //isRepaintBoundary默认是返回false,RenderRepaintBoundary中返回的是true
  
  bool get isRepaintBoundary => true;
}

isRepaintBoundary在RenderObject中默认是返回false,RenderRepaintBoundary中返回的是true

RenderObject中isRepaintBoundary的作用

当RenderObject中isRepaintBoundary返回时true时当前节点的RenderObject(以及子节点)的绘制会在新创建Layer完成,这样就和其他Layer做了隔离,因为Layer是可以复用的,这样帧刷新的时候就不需要把每个RenderObject的paint方法都执行一遍。

核心代码如下:

void paintChild(RenderObject child, Offset offset) {
  //1,isRepaintBoundary = true
  if (child.isRepaintBoundary) {
    //2,结束当前layer的绘制
    stopRecordingIfNeeded();
    //3,
    _compositeChild(child, offset);
  } else {
    child._paintWithContext(this, offset);
  }
}

//3,合成child
void _compositeChild(RenderObject child, Offset offset) {
  // Create a layer for our child, and paint the child into it.
  if (child._needsPaint) {
    //4,如果child需要被绘制(_needsPaint=true代表当前节点或者当前节点子孩子被PipelineOwer标记出需要被重绘)
    repaintCompositedChild(child, debugAlsoPaintedParent: true);
  } else {
  }
  
  final OffsetLayer childOffsetLayer = child._layer! as OffsetLayer;
  childOffsetLayer.offset = offset;
  appendLayer(child._layer!);
}

从上述流程可以看出当isRepaintBoundary=false时,就会触发paint的方法,我们假设下图所有RenderObject的isRepaintBoundary=false且其中RenderObject4被标记需要刷新

如果RenderObject4的上一级父节点就是isRepaintBoundary=true,那么流程就如下

综上流程分析,假设场景是RenderObject4的绘制很耗但是是刷新不频繁,RenderObject5,RenderObject6,RenderObject7的刷新很频繁,我们使用RepaintBoundary对RenderObject4对应的Widget包一层这样可以缩短渲染时绘制阶段的耗时从而降低卡顿问题。

使用到RepaintBoundary的地方

在Flutter framework中的有些Widget就使用到RepaintBoundary了

Flowl

流式布局每个child都是独立的layer渲染

Flow({
  Key? key,
  required this.delegate,
  List<Widget> children = const <Widget>[],
  this.clipBehavior = Clip.hardEdge,
}) : assert(delegate != null),
     assert(clipBehavior != null),
     super(key: key, children: RepaintBoundary.wrapAll(children));
SliverChildBuilderDelegate

SliverChildBuilderDelegate这个是ListView.builder的时候内部会创建SliverChildBuilderDelegate,列表大量item彼此之间独立layer渲染


Widget? build(BuildContext context, int index) {
  assert(builder != null);

  if (addRepaintBoundaries)
    child = RepaintBoundary(child: child);
  if (addSemanticIndexes) {
    final int? semanticIndex = semanticIndexCallback(child, index);
    if (semanticIndex != null)
      child = IndexedSemantics(index: semanticIndex + semanticIndexOffset, child: child);
  }
  if (addAutomaticKeepAlives)
    child = AutomaticKeepAlive(child: child);
  return KeyedSubtree(child: child, key: key);
}
总结

以上是对RepaintBoundary的作用分析,希望通过此篇文章帮助到大家提升的对Flutter渲染机制的认识。在实际开发中,我们可以结合AppUploader这样的iOS开发助手工具来更好地管理应用性能,AppUploader提供了便捷的性能分析功能,帮助开发者快速定位和解决渲染性能问题,让Flutter应用运行更加流畅。

相关文章:

  • MacOS安装软件及运行时,提示“已损坏无法打开”的解决方案
  • 自定义汇编语言(Custom Assembly Language) 和 Unix Git
  • 中级:Maven面试题精讲
  • Deepseek API+Python 测试用例一键生成与导出 V1.0.6(加入分块策略,返回更完整可靠)
  • Profibus DP 转 ModbusTCP 网关:工业自动化的桥梁
  • 自制AirTag定位器成品使用教程(支持安卓/鸿蒙/iOS/PC等所有系统查看定位器的位置)
  • theos工具来编译xcode的swiftUI项目为ipa文件
  • 【11408】考研英语长难句解析策略:三步断开与简化法,快速提升阅读得分
  • 什么是嵌入模型Embedding Models?
  • 抓包工具之whistle
  • 8个实用销售工具
  • MATLAB基本操作
  • 卢曼卡片盒笔记法介绍 Introduction to the Zettelkasten Method
  • OpenIPC开源FPV之Adaptive-Link信号干扰
  • 硬盘加密安全
  • springBoot统一响应类型3.5版本
  • 计算机视觉入门:从像素到理解的旅程
  • C# 窗体应用(.FET Framework) 线程操作方法
  • spring boot 整合redis
  • JAVA设计模式之适配器模式《太白金星有点烦》
  • 中山网站建设包括哪些/下载百度网盘
  • 北京网站建设价格低/今天刚刚发生的重大新闻
  • 深圳有哪些大公司总部/cpu优化软件
  • 数字校园建设专题网站/网络营销品牌有哪些
  • 传奇sf 新开网站/站长工具国色天香
  • 网站建设学多久/十堰seo优化