Flutter视图渲染过程三棵树------Widget、Element、RenderObject介绍

本文详细阐述了Widget的不可变性、StatelessWidget和StatefulWidget的区别,以及Element的可变性和其在Widget更新过程中的角色。重点介绍了Element的更新机制,包括canUpdate方法、slot的概念和在重建Element时的状态更新。此外,还涉及RenderObject在布局和绘制中的作用,以及RenderObjectWidget的职责划分。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Widget

widget存储视图的配置信息,没有状态所以是不可变的,每个绘制周期(帧率),widget都不会刷新,而是会被重建。

StatelessWidget和StatefulWidget

 - 
 - 
 - 怎样判断是需要更新element,还是替换element

abstract class Widget extends DiagnosticableTree {
  const Widget({ this.key });
  final Key key;
  ···
  static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
  }

  • element只更新widget里的配置信息,若 canUpdate 方法返回 true 说明不需要替换 Element,直接更新 Widget 就可以了。
  • element替换,而我们给 Widget 一个 key 之后,canUpdate 方法将会比较两个 Widget 的 runtimeType 以及 key。并返回 false。(这里 runtimeType 相同,key 不同)
    此时 RenderObjectElement 会用新 Widget 的 key 在老 Element 列表里面查找,找到匹配的则会更新 Element 的位置并更新对应 renderObject 的位置,对于这个例子来讲就是交换了 Element 的位置并交换了对应 renderObject 的位置。
    • 比较范围
      为了提升性能 Flutter 的比较算法(diff)是有范围的,它并不是对第一个 StatefulWidget 进行比较,而是对某一个层级的 Widget 进行比较。
      -slot:slot 能够描述子级在其父级列表中的位置。多子部件 Widget 例如 Row,Column 都为它的子级提供了一系列 slot。在调用 Element.updateChild 的时候有一个细节,若新老 Widget 的实例相同,注意这里是实例相同而不是类型相同, slot 不同的时候,Flutter 所做的仅仅是更新 slot,也就给他换个位置。因 为 Widget 是不可变的,实例相同意味着显示的配置相同,所以要做的仅仅是挪个地方而已。
abstract class Element extends DiagnosticableTree implements BuildContext {
···
  dynamic get slot => _slot;
  dynamic _slot;
···
 
  Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
    ···
    if (child != null) {
      if (child.widget == newWidget) {
        if (child.slot != newSlot)
          updateSlotForChild(child, newSlot);
        return child;
      }
      if (Widget.canUpdate(child.widget, newWidget)) {
        if (child.slot != newSlot)
          updateSlotForChild(child, newSlot);
        child.update(newWidget);
        assert(child.widget == newWidget);
        assert(() {
          child.owner._debugElementWasRebuilt(child);
          return true;
        }());
        return child;
      }
      deactivateChild(child);
      assert(child._parent == null);
    }
    return inflateWidget(newWidget, newSlot);
  }

  • 重建element,状态也被更新。

Element

Element 是 Widget 的一个实例化对象,具有状态,是可变的,同时持有 Widget 和 RenderObject。
因为 Widget 具有不可变性,但 Element 却是可变的。实际上,Element 树这一层将 Widget 树的变化做了抽象(Diff),可以只将真正需要修改的部分同步到真实的 RenderObject 树中,最大程度降低对真实渲染视图的修改,提高渲染效率,而不是销毁整个RenderObject树重建。

RenderObject

布局和绘制在 RenderObject 中完成,Flutter 采用深度优先机制遍历渲染对象树,确定树中各个对象的位置和尺寸,并把它们绘制到不同的图层上。绘制完毕后,合成和渲染的工作则交给 Skia 搞定。

RenderObjectWidget

RenderObjectWidget 是一个抽象类。我们通过源码可以看到,这个类中同时拥有创建 Element、RenderObject,以及更新 RenderObject 的方法。

abstract class RenderObjectWidget extends Widget {
  
  RenderObjectElement createElement();
  
  RenderObject createRenderObject(BuildContext context);
  
  void updateRenderObject(BuildContext context, covariant RenderObject renderObject) { }
  ...
}

布局和绘制

abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget {
  ...
  void layout(Constraints constraints, { bool parentUsesSize = false }) {...}
  
  void paint(PaintingContext context, Offset offset) { }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值