一、Element就是Widget在UI树具体位置的一个实例化对象,大多数Element只有唯一的renderObject
,但还有一些Element会有多个子节点,如继承自RenderObjectElement
的一些类,比如MultiChildRenderObjectElement
。
二、所有Element的RenderObject构成一棵树,称之为”Render Tree“即”渲染树“。总结一下,可以认为Flutter的UI系统包含三棵树:Widget树、Element树、渲染树。他们的依赖关系是:Element树根据Widget树生成,而渲染树又依赖于Element树。
三、重点看一下Element,Element的生命周期如下:
- Framework 调用
Widget.createElement
创建一个Element实例,记为element
- Framework 调用
element.mount(parentElement,newSlot)
,mount方法中首先调用element
所对应Widget的createRenderObject
方法创建与element
相关联的RenderObject对象,然后调用element.attachRenderObject
方法将element.renderObject
添加到渲染树中插槽指定的位置(这一步不是必须的,一般发生在Element树结构发生变化时才需要重新添加)。插入到渲染树后的element
就处于“active”状态,处于“active”状态后就可以显示在屏幕上了(可以隐藏)。 - 当有父Widget的配置数据改变时,同时其
State.build
返回的Widget结构与之前不同,此时就需要重新构建对应的Element树。为了进行Element复用,在Element重新构建前会先尝试是否可以复用旧树上相同位置的element,element节点在更新前都会调用其对应Widget的canUpdate
方法,如果返回true
,则复用旧Element,旧的Element会使用新Widget配置数据更新,反之则会创建一个新的Element。Widget.canUpdate
主要是判断newWidget
与oldWidget
的runtimeType
和key
是否同时相等,如果同时相等就返回true
,否则就会返回false
。根据这个原理,当需要强制更新一个Widget时,可以通过指定不同的Key来避免复用。 - 当有祖先Element决定要移除
element
时(如Widget树结构发生了变化,导致element
对应的Widget被移除),这时该祖先Element就会调用deactivateChild
方法来移除它,移除后element.renderObject
也会被从渲染树中移除,然后Framework会调用element.deactivate
方法,这时element
状态变为“inactive”状态。 - “inactive”态的element将不会再显示到屏幕。为了避免在一次动画执行过程中反复创建、移除某个特定element,“inactive”态的element在当前动画最后一帧结束前都会保留,如果在动画执行结束后它还未能重新变成“active”状态,Framework就会调用其
unmount
方法将其彻底移除,这时element的状态为defunct
,它将永远不会再被插入到树中。 - 如果
element
要重新插入到Element树的其他位置,如element
或element
的祖先拥有一个GlobalKey(用于全局复用元素),那么Framework会先将element从现有位置移除,然后再调用其activate
方法,并将其renderObject
重新attach到渲染树。