定义
Widget
:存放渲染内容、它只是一个配置数据结构,创建是非常轻量的,在页面刷新的过程中随时会重建。
其中
Widget
根据功能可以分为三类:
- 组合类
StatelessWidget/StatefulWidget
:比如我们常见的Container
就是一个组合类的控件,它主要负责组合封装多个其他负责绘制的原子组件。- 代理类
inheritedwidget
:它的父类是ProxyWidget
,顾名思义。简单来说,InheritedWidget
的作用是向它的子Widget
有效地传播和分享数据,当InheritedWidget
作为一个Parent Widget
时,它下面的Widget tree
的所有Widget
都可以去和InheritedWidget
发生数据传递和交互。当数据发生改变时,一部分控件需要rebuild
,另外的控件不需要rebuild
的时候,可以使用InheritedWidget
。- 绘制类
RenderObjectWidget
:RenderObjectWidget
会负责创建出负责实际渲染的RenderObject
对象,RenderObject
负责实际的layout()
和paint()
。后期有空,我会手动写一个RenderObject
。
Element
: 是分离 WidgetTree
和真正的渲染对象的中间层,可以看成flutter的骨架。 Widget
用来描述对应的Element
属性,同时持有Widget
和RenderObject
,存放上下文信息,通过它来遍历视图树,支撑UI
结构。
其中
Element
可以根据功能分成两大类:
- 组合类
ComponentElement
:主要包括如下StatelessElement / StatefulElement / ProxyElement
子类;其中各Element
都是与Widget
对应的。- 绘制类
RenderObjectElement
:RenderObjectElement
对应的是RenderObjectWidget
。
RenderObject
用于应用界面的布局和绘制,负责真正的渲染,保存了元素的大小,布局等信息。
当应用启动时Flutter
会遍历并创建所有的Widget
形成Widget
树,通过调用Widget
上的createElement()
方法创建每个Element
对象,形成Element
树。最后调用Element
的createRenderObject()
方法创建每个渲染对象,形成一个RenderObject
树。
Widget => Element
如何通过Widget更新Element:
Element/ComponentElement
:
单个Element
时候通过updateChild
方法更新;
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
//如果newWidget是null,并且old child非null,直接deactivateChild
if (newWidget == null) {
if (child != null)
deactivateChild(child);
return null;
}
final Element newChild;
if (child != null) {
//新旧widget相同的情况
if (child.widget == newWidget) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
newChild = child;
} else if (Widget.canUpdate(child.widget, newWidget)) {
//可以update的情况,也就是runtimetype和key相同
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
child.update(newWidget);
newChild = child;
} else {
//其他情况,移除旧的,重新inflateWidget新的widget,会创建element
deactivateChild(child);
newChild = inflateWidget(newWidget, newSlot);
}
} else {
//old child是null,这里直接inflate新的widget,会创建element
newChild = inflateWidget(newWidget, newSlot);
}
return newChild;
}
大致归纳一下,如下表:
标题 | newWidget == null | newWidget != null |
---|---|---|
child == null | Returns null. | Returns new [Element]. |
child != null | Old child is removed, returns null. | Old child updated if possible, returns child or new [Element]. |
RenderObjectElement
:
多个Element
时候通过updateChildren
方法更新;
List<Element> updateChildren(List<Element> oldChildren, List<Widget> newWidgets, { Set<Element>? forgottenChildren }) {
//如果forgottenChildren包含当前child,表示它已经在oldChild中,返回null
Element? replaceWithNullIfForgotten(Element child) {
return forgottenChildren != null && forgottenChildren.contains(child) ? null : child;
}
int newChildrenTop = 0;
int oldChildrenTop = 0;
int newChildrenBottom = newWidgets.length - 1;
int oldChildrenBottom = oldChildren.length - 1;
final List<Element> newChildren = oldChildren.length == newWidgets.length ?
oldChildren : List<Element>.filled(newWidgets.length, _NullElement.instance, growable: false);
Element? previousChild;
// 从列表的顶部向下更新Element
while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
final Element? oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenTop]);
final Widget newWidget = newWidgets[newChildrenTop];
if (oldChild == null || !Widget.canUpdate(oldChild.widget, newWidget))
break;
final Element newChild = updateChild(oldChild, newWidget, IndexedSlot<Element?>(newChildrenTop, previousChild))!;
newChildren[newChildrenTop] = newChild;
previousChild = newChild;
newChildrenTop += 1;
oldChildrenTop += 1;
}
// 从列表底部开始扫描Element,但不更新
while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
final Element? oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenBottom]);
final Widget newWidget = newWidgets[newChildrenBottom];
assert(oldChild == null || oldChild._lifecycleState == _ElementLifecycle.active);
if (oldChild == null || !Widget.canUpdate(oldChild.widget, newWidget))
break;
oldChildrenBottom -= 1;
newChildrenBottom -= 1;
}
// 扫描旧的子Element列表里面中间的子Element,保存Widget有Key的Element到oldKeyChildren,其他的失效
final bool haveOldChildren = oldChildrenTop <= oldChildrenBottom;
Map<Key, Element>? oldKeyedChildren;
if (haveOldChildren) {
oldKeyedChildren = <Key, Element>{};
while (oldChildrenTop <= oldChildrenBottom) {
final Element? oldChild = replaceWithNullIfForgotten(oldChildren[oldChildrenTop]);
if (oldChild != null) {
if (oldChild.widget.key != null)
oldKeyedChildren[oldChild.widget.key!] = oldChild;
else
deactivateChild(oldChild);
}
oldChildrenTop += 1;
}
}
// 根据Widget的Key更新oldKeyChildren中的Element。
while (newChildrenTop <= newChildrenBottom) {
Element? oldChild;
final Widget newWidget = newWidgets[newChildrenTop];
if (haveOldChildren) {
final Key? key = newWidget.key;
if (key != null) {
oldChild = oldKeyedChildren![key];
if (oldChild != null) {
if (Widget.canUpdate(oldChild.widget, newWidget)) {
// we found a match!
// remove it from oldKeyedChildren so we don't unsync it later
oldKeyedChildren.remove(key);
} else {
// Not a match, let's pretend we didn't see it for now.
oldChild = null;
}
}
}
}
final Element newChild = updateChild(oldChild, newWidget, IndexedSlot<Element?>(newChildrenTop, previousChild))!;
newChildren[newChildrenTop] = newChild;
previousChild = newChild;
newChildrenTop += 1;
}
// 整个列表扫描结束
newChildrenBottom = newWidgets.length - 1;
oldChildrenBottom = oldChildren.length - 1;
// 更新底部的Element
while ((oldChildrenTop <= oldChildrenBottom) && (newChildrenTop <= newChildrenBottom)) {
final Element oldChild = oldChildren[oldChildrenTop];
final Widget newWidget = newWidgets[newChildrenTop];
final Element newChild = updateChild(oldChild, newWidget, IndexedSlot<Element?>(newChildrenTop, previousChild))!;
newChildren[newChildrenTop] = newChild;
previousChild = newChild;
newChildrenTop += 1;
oldChildrenTop += 1;
}
// 清除旧子Element列表中其他所有剩余Element
if (haveOldChildren && oldKeyedChildren!.isNotEmpty) {
for (final Element oldChild in oldKeyedChildren.values) {
if (forgottenChildren == null || !forgottenChildren.contains(oldChild))
deactivateChild(oldChild);
}
}
return newChildren;
}
该函数的主要职责如下:
- 复用能复用的子节点,并调用updateChild对子节点进行更新。
- 对不能更新的子节点,调用deactivateChild对该子节点进行失效。
我们注意到updateChild
如果old child
是空或者无法update
就需要inflateWidget
;
我们从源码看看创建方法inflateWidget():
Element inflateWidget(Widget newWidget, Object? newSlot) {
final Key? key = newWidget.key;
if (key is GlobalKey) {
final Element? newChild = _retakeInactiveElement(key, newWidget);
if (newChild != null) {
newChild._activateWithParent(this, newSlot);
final Element? updatedChild = updateChild(newChild, newWidget, newSlot);
return updatedChild!;
}
}
//创建子Element
final Element newChild = newWidget.createElement();
//将child挂载到当前element
newChild.mount(this, newSlot);
//返回child element
return newChild;
}
逻辑非常简单,创建一个element
,然后mount
到当前element
。
RenderObject
RenderObjectWidget
会调用createElement
,创建出RenderObjectElement
,
然后RenderObjectWidget
会调用createRenderObject
,创建出RenderObject
,
然后通过[RenderObjectElement.mount]
方法,将RenderObject
挂载为RenderObjectElement._renderObject
属性。从此可以看出,Element
是连接Widget
和RenderObject
的桥梁。