定义 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:
Element/ComponentElement: 单个Element时候通过updateChild方法更新;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 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方法更新;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 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():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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的桥梁。