Flutter中的RenderObjectElement与RenderObjectWidget

级别: ★☆☆☆☆
标签:「Flutter」「RenderObjectElement 」「RenderObjectWidget」
作者: 沐灵洛
审校: QiShare团队


RenderObjectWidgetRenderObjectElement提供配置信息。
RenderObjectElement包装了RenderObjectRenderObject为应用程序提供真正的渲染。

RenderObjectWidget是什么?

RenderObjectWidgetRenderObjectElement的配置信息。
RenderObjectWidget是个抽象类。

abstract class RenderObjectWidget extends Widget {                                                                     
                                     
  const RenderObjectWidget({ Key key }) : super(key: key);                                                             
                                                                                                                       
  /// RenderObjectWidgets always inflate to a [RenderObjectElement] subclass.                                          
  @override                                                                                                            
  RenderObjectElement createElement();                                                                                 
                                                                                                                       
  /// 使用`RenderObjectWidget`信息,
  ///创建一个`RenderObjectWidget`表示的`RenderObject`实例。
  ///创建时机:
  ///`[RenderObjectElement.mount]`方法中使用`RenderObjectElement`创建。
///挂载时,调用关联的此`widget`创建其对应的`RenderObject`                                                  
  @protected                                                                                                           
  RenderObject createRenderObject(BuildContext context);                                                               
                                                                                                                       
  /// 复制此[RenderObjectWidget]描述的配置到给定的[RenderObject],
  ///此`RenderObject`类型将与此`RenderObjectWidget`的
 ///[createRenderObject]返回的`RenderObject`类型相同。                                                                                                                                                                                
  /// 调用时机:[RenderObjectElement.update]                                                
  @protected                                                                                                           
  void updateRenderObject(BuildContext context, covariant RenderObject renderObject) { }                               
                                                                                                                       
  ///此`widget`前一个关联的`RenderObject`已经从树中移除。
  ///此处的`RenderObject`是其的一个副本。                                                       
  @protected                                                                                                           
  void didUnmountRenderObject(covariant RenderObject renderObject) { }                                                 
}                                                                                                                      

RenderObjectElement是什么?

使用RenderObjectWidget作为其配置的ElementRenderObjectElement对象关联了一个处于渲染树中的RenderObject组件;RenderObject 主要处理一些固定的操作,如:布局、绘制和 Hit testing。 与ComponentElement一样RenderObjectElement也是抽象类,不同的是ComponentElement不会直接创建RenderObject,而是间接通过创建其他Element创建RenderObject

RenderObjectElement主要有三个系统的子类,分别处理renderObject作为child时的不同情况。

  • LeafRenderObjectElement:叶子渲染对象对应的元素,处理没有childrenrenderObject
  • SingleChildRenderObjectElement:处理只有单个childrenderObject
  • MultiChildRenderObjectElement: 处理有多个children的渲染对象

有时RenderObjectchild模型更复杂一些,比如多维数组的形式,则可能需要基于RenderObjectElement实现一个新的子类。

RenderObjectElement对象花费大量时间充当widgetrenderObject之间的中介。为使此更易于处理,大多数RenderObjectElement子类都覆盖了这些getter ,以便它们返回元素期望的特定类型,例如:

class FooElement extends RenderObjectElement {                                       
                                                                                    
 @override                                                                          
 Foo get widget => super.widget; 
                                                   
@override                                                                          
RenderFoo get renderObject => super.renderObject;                                  
                                                                          
}                                                                                    

系统常用组件与RenderObjectElement:

常用组件 Widget(父级) Element
Flex/Wrap/Flow/Stack MultiChildRenderObjectWidget MultiChildRenderObjectElement
RawImage(Imaget)/ErrorWidget LeafRenderObjectWidget LeafRenderObjectElement
Offstage/SizedBox/Align/Padding SingleChildRenderObjectWidget SingleChildRenderObjectElement

RenderObjectElement中的插槽(Slots)

如果一个「element」有多个子element, 每个子element都对应一个子renderObject ,该子renderObject应该作为此「element」renderObjectchild被添加到树中。

然而,此element的最直接(immediate)的children,在生成对应的renderObjects时, 可能最终用于生成实际renderObjectschildren已经不是最初的最直接的children(简言之,子element在生成最终对应的renderObject时,可能已经发生了改变)。

比如: StatelessElement (StatelessWidgetelement) 仅与其child (由StatelessWidget.build返回的element)对应的任何RenderObject对应。

因此,为每个child分配了一个_slot_令牌(token)。这是一个对RenderObjectElement节点私有的标识符。当最终生成RenderObject的后代时,并准备好将其附加到该节点的渲染对象时,它将该_slot_令牌(token)传递回该节点,并允许该节点快速地标记出应当将子渲染对象相对于其他对象放置在父渲染对象的何处。

更新children

element生命的早期,framework 调用mount方法,这个方法会为每个child调用updateChild,传入此child,传入新的widget,以及此child的新的slot,从而获得子element的列表。

Element updateChild(Element child, Widget newWidget, dynamic newSlot)

随后,framework 将调用update方法,此方法最终会通过rebuild方法使得RenderObjectElement为每个child调用再次调用updateChild,并传入在mount或上一次运行update时(以最近发生的为准)获得的Element,与新的Widgetslot共同这为RenderObjectElement对象提供新的Element对象的列表。

在可能的情况下,update方法会尝试将elements关联的旧widgets与新的widgets匹配。例如,旧widgets之一和新widgets之一有相同的keyruntimeType,它们应该是匹配的。并且旧的element应该使用新的widget进行更新(并且slot也会更新到新widget的新位置上)。children调用updateChild应该按照它们的逻辑顺序。

  • build阶段动态确定children
    childwidget可能来自回调,而非element关联的widget

  • layout阶段动态确定children
    如果要在布局时生成widget,则需在update方法不起作用时生成它们:element的渲染对象的布局此时尚未开始。相反,update方法可以将渲染对象标记为需要的布局(RenderObject.markNeedsLayout),然后渲染对象的RenderObject.performLayout方法可以回调到element让它生成的widget相应地调用updateChild
    为了使渲染对象在布局期间调用element,它必须使用RenderObject.invokeLayoutCallback。要使element在其update方法之外调用updateChild,它必须使用BuildOwner.buildScope。与在布局期间进行构建时相比,framework在正常操作中提供的检查要多得多。因此,使用layout-time build语义创建widget时应格外小心。

  • building时处理错误
    如果element调用builder函数为其子代获取widget,则可能会发生该builder内引发了异常。应该使用FlutterError.reportError捕获并报告此类异常。如果需要child,但是builder以这种方式失败了,则可以使用ErrorWidget的实例代替。

Detaching children

使用GlobalKey时,有可能在更新此元素之前由另一个元素主动删除该孩子。(特别是,以有GlobalKeywidget为根对应的element移动到在build阶段处理的更早的元素时)发生这种情况时,该elementforgetChild方法将使用引用受影响的子元素。RenderObjectElement子类的forgetChild方法必须从child列表中删除该child元素,以便在下次updatechild时,不考虑已删除的child。出于性能原因,如果有很多元素,则可以通过将它们存储在Set中而不是主动更改child列表的本地记录和所有插槽的标识来更快地跟踪哪些元素被遗忘了具体的可以参考MultiChildRenderObjectElement的实现。

保持渲染树

一旦后代生成渲染对象,它将调用insertChildRenderObject。如果后代的slot更改了identity,将调用moveChildRenderObject。如果后代消失了,它将调用removeChildRenderObject
这三个方法应相应地更新渲染树,并分别将给定的子渲染对象与该element自己的渲染对象相连接(attaching),移动和分离(detaching)。

Flutter中的StatelessWidget及其生命周期
Flutter中的Widget
Flutter中的Element(下篇)
Flutter中的Element(上篇)
iOS 解决 [NSURL fileURLWithPath:] 对 # 的编码问题
Xcode 调整导航目录字体大小b
Swift 5.1 (21) - 泛型
Swift 5.1 (20) - 协议
Swift 5.1 (19) - 扩展
Swift 5.1 (18) - 嵌套类型
浅谈编译过程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268