Flutter三棵树
Widgets树
对应Widget build()函数中构建的对象,整棵树每次build时都会被重新构建,由于是纯dart对象,所以创建起来很快速。
Widget由用户构建,声明式的描述组件布局、状态,相当于组件的配置。
Element树
每个Widget都有一个createElement()函数,用来创建对应的Element对象。Element树主要作为Widgets树和RenderObject树的中介,因为Widget会频繁的进行重建,Element树用来维持属性,保持组件在树种的位置等信息,同时持有Widget和RenderObject双方引用。
Widget和Element是一对一的,但Element和RenderObject并不是,只有RenderObjectElement才会持有RednerObject的实例,真正的代表一个可绘制对象。
RenderObject树
每个RenderObject类似于安卓中的View,每个对象都对应一个被绘制在屏幕上的组件。每次组件更新时都会进行根据属性判断RenderObject是否需要重绘,如果不需要直接复用,避免重绘。
Widget、Element、RenderObject
下面是Widget的继承体系,我们组常使用的是StatelessWidget和StatefulWidget,RenderObjectWidget才是组件库的核心,负责提供可绘制的组件,如:文字、颜色、布局等
不同类型组件创建的Element也不一样,Element继承体系,
组件重建过程
drawFrame()->BuildOwner.buildScope()->
setState流程
//State.setState
void setState(VoidCallback fn) {
//assert...
final Object? result = fn() as dynamic;
// assert,保证result不是future,等价于: result is not Future
_element!.markNeedsBuild();
}
void markNeedsBuild() {
if (_lifecycleState != _ElementLifecycle.active)
return;
//assert...
if (dirty)
return;
_dirty = true;
owner!.scheduleBuildFor(this);
}
//BuildOwner
void scheduleBuildFor(Element element) {
//assert...
if (element._inDirtyList) {
//assert...
_dirtyElementsNeedsResorting = true;
return;
}
if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
_scheduledFlushDirtyElements = true;
onBuildScheduled!();
}
_dirtyElements.add(element);
element._inDirtyList = true;
//assert...
}
略去assert函数,setState函数很简单,调用回调函数,将组件标记为需要重建。等待下次引擎调用drawFrame时会通过owner.buildScope()来重建对应的组件树。
drawFrame
负责绘制需要更新的组件,由
//WidgetsBinding.drawFrame
void drawFrame() {
//assert...
//...
try {
if (renderViewElement != null)
buildOwner!.buildScope(renderViewElement!);
//这里对应RenderBinding.drawFrame();
super.drawFrame();
buildOwner!.finalizeTree();
} finally {
//assert...
}
//...
}
//RendingBinding.drawFrame
void drawFrame() {
assert(renderView != null);
//重新layout
pipelineOwner.flushLayout();
//重新合成
pipelineOwner.flushCompositingBits();
//重绘组件
pipelineOwner.flushPaint();
if (sendFramesToEngine) {
renderView.compositeFrame(); // this sends the bits to the GPU
pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
_firstFrameSent = true;
}
}
PipelineOwner中有_nodesNeedingLayout、_nodesNeedingCompositingBitsUpdate、_nodesNeedingPaint三个队列分别保存下一次绘制前需要重绘的布局和组件,下次绘制时调用drawFrame,上面的三个方法分别对应三个队列的处理。
buildScope重建Element树
buildScope会扫描dirtyList中的元素,调用rebuild()来重新构建。
void buildScope(Element context, [ VoidCallback? callback ]) {
if (callback == null && _dirtyElements.isEmpty)
return;
//assert...
Timeline.startSync('Build', arguments: timelineArgumentsIndicatingLandmarkEvent);
try {
_scheduledFlushDirtyElements = true;
if (callback != null) {
//assert...
_dirtyElementsNeedsResorting = false;
try {
//执行回调
callback();
} finally {
//assert...
}
}
_dirtyElements.sort(Element. _sort);
_dirtyElementsNeedsResorting = false;
int dirtyCount = _dirtyElements.length;
int index = 0;
while (index < dirtyCount) {
//assert...
try {
//调用element的rebuild
_dirtyElements[index].rebuild();
} catch (e, stack) {
//...
}
index += 1;
if (dirtyCount < _dirtyElements.length || _dirtyElementsNeedsResorting!) {
_dirtyElements.sort(Element. _sort);
_dirtyElementsNeedsResorting = false;
dirtyCount = _dirtyElements.length;
while (index > 0 && _dirtyElements[index - 1].dirty) {
// It is possible for previously dirty but inactive widgets to move right in the list.
// We therefore have to move the index left in the list to account for this.
// We don't know how many could have moved. However, we do know that the only possible
// change to the list is that nodes that were previously to the left of the index have
// now moved to be to the right of the right-most cleaned node, and we do know that
// all the clean nodes were to the left of the index. So we move the index left
// until just after the right-most clean node.
index -= 1;
}
}
}
//assert...
} finally {
for (final Element element in _dirtyElements) {
assert(element._inDirtyList);
element._inDirtyList = false;
}
_dirtyElements.clear();
_scheduledFlushDirtyElements = false;
_dirtyElementsNeedsResorting = null;
Timeline.finishSync();
//assert...
}
//assert...
}
Element的rebuild过程
rebuild直接调用对应element的performRebuild方法,该方法是一个抽象方法,不同的element实现方式不一样,我们只关注三个:ComponentElement、StatefulElement、RenderObjectElement
void rebuild() {
//assert...
//不可见和非标记为dirty的组件不会重新build
if (_lifecycleState != _ElementLifecycle.active || !_dirty)
return;
//assert...
performRebuild();
//assert...
}
StatefulElement
重建过程和ComponentElement差不读,只不过做了一些判断调用了
void performRebuild() {
if (_didChangeDependencies) {
state.didChangeDependencies();
_didChangeDependencies = false;
}
//ComponentElement.performRebuild
super.performRebuild();
}
ComponentElement
void performRebuild() {
//...
Widget? built;
try {
//assert...
//调用build方法,重新构建组件
built = build();
//assert...
} catch (e, stack) {
//...
} finally {
// We delay marking the element as clean until after calling build() so
// that attempts to markNeedsBuild() during build() will be ignored.
_dirty = false;
//assert...
}
try {
_child = updateChild(_child, built, slot);
//assert...
} catch (e, stack) {
//assert...
_child = updateChild(null, built, slot);
}
//...
}
updateChild方法根据child和newWidget的参数是否为null采取不一样的功能。
- child != null, newWidget = null,卸载原组件
- child = null, newWidget != null, 创建新的element,用newWidget去配置element
- child != null, newWidget != null,更新组件
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
if (newWidget == null) {
if (child != null)
//卸载组件
deactivateChild(child);
return null;
}
final Element newChild;
if (child != null) {
bool hasSameSuperclass = true;
//assert...
if (hasSameSuperclass && child.widget == newWidget) {
//如果新组件和原组件一样
//位置不一样时更新
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
//直接复制
newChild = child;
} else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
//更新组件
child.update(newWidget);
//assert...
newChild = child;
} else {
deactivateChild(child);
//assert...
newChild = inflateWidget(newWidget, newSlot);
}
} else {
newChild = inflateWidget(newWidget, newSlot);
}
//assert...
return newChild;
}
//Widget.canUpdate
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
更新组件时还要用Widget.canUpdate(child.widget, newWidget)去判断是否可以更新,其方法很简单,就是判断一下类型和Key;
其中最关键的在于Element.update方法,其会对子组件进行更新:
void update(covariant Widget newWidget) {
//assert...
_widget = newWidget;
}
默认就是一个简单的组件赋值,但是子类会进行重写。
StatelessElement.update:
无状态组件就是更新自身后再rebuild子组件,一直更新组件树。
void update(StatelessWidget newWidget) {
super.update(newWidget);
assert(widget == newWidget);
_dirty = true;
//rebuild子组件
rebuild();
}
StatefulElement.update:
这里就是官方文档说的,StatefulWidget每次修改会保持state对象,只重建Widget。
void update(StatefulWidget newWidget) {
super.update(newWidget);
assert(widget == newWidget);
final StatefulWidget oldWidget = state._widget!;
// We mark ourselves as dirty before calling didUpdateWidget to
// let authors call setState from within didUpdateWidget without triggering
// asserts.
_dirty = true;
//保持state不变,重新修改widget
state._widget = widget as StatefulWidget;
try {
_debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic;
//assert
} finally {
_debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
}
//build子组件
rebuild();
}
RenderObjectElement.update:
ComponentElement只是对其它Element的组合,本身不代表实际渲染的组件,所有更新时只要更新子组件即可。
而RenderObjectElement代表一个真正需要绘制的Element。更新时就要判断该RenderObject是否需要重新绘制,也就是我们说的diff过程,下面会详细说。
void update(covariant RenderObjectWidget newWidget) {
//注意,这里的super是Element
super.update(newWidget);
//assert...
//关键
widget.updateRenderObject(this, renderObject);
//asert...
_dirty = false;
}
子类还会对该方法进行扩展,如:SingleChildRenderObjectElement还继续会更新子组件。
void update(SingleChildRenderObjectWidget newWidget) {
super.update(newWidget);
assert(widget == newWidget);
_child = updateChild(_child, widget.child, null);
}
其中的关键在于updateRenderObject,下面会说。
RenderObjectElement:
RenderObjectElement的performRebuild
void performRebuild() {
//assert...
widget.updateRenderObject(this, renderObject);
//assert...
_dirty = false;
}
注意,这里还调用了widget.updateRenderObject(),这也是一个抽象方法,不同的子类根据自身实现,这里才是进行diff的关键。
我们以Padding为例:
//padding继承自SingleChildRenderObjectWidget
class Padding extends SingleChildRenderObjectWidget
void updateRenderObject(BuildContext context, RenderPadding renderObject) {
//用自身widget的配置去更新renderObject
renderObject
..padding = padding
..textDirection = Directionality. maybeOf (context);
}
这里不仅仅是简单的赋值,RenderPadding的set方法如下:
如果padding的值改变,就将自身标记为需要重绘,添加到PipelineOwner的
_nodesNeedingLayout中,类似的还有_nodesNeedingPaint队列。下次drawFrame时会重绘这些组件。
set padding(EdgeInsetsGeometry value) {
assert(value != null);
assert(value.isNonNegative);
//如果一致直接返回
if (_padding == value)
return;
_padding = value;
//标记为需要更新
_markNeedResolution();
}
void _markNeedResolution() {
_resolvedPadding = null;
markNeedsLayout();
}
void markNeedsLayout() {
if ((_cachedBaselines != null && _cachedBaselines!.isNotEmpty) ||
(_cachedIntrinsicDimensions != null && _cachedIntrinsicDimensions!.isNotEmpty) ||
(_cachedDryLayoutSizes != null && _cachedDryLayoutSizes!.isNotEmpty)) {
// If we have cached data, then someone must have used our data.
// Since the parent will shortly be marked dirty, we can forget that they
// used the baseline and/or intrinsic dimensions. If they use them again,
// then we'll fill the cache again, and if we get dirty again, we'll
// notify them again.
_cachedBaselines?.clear();
_cachedIntrinsicDimensions?.clear();
_cachedDryLayoutSizes?.clear();
if (parent is RenderObject) {
markParentNeedsLayout();
return;
}
}
super.markNeedsLayout();
}
//RenderObject.markNeedsLayout
void markNeedsLayout() {
assert(_debugCanPerformMutations);
if (_needsLayout) {
assert(_debugSubtreeRelayoutRootAlreadyMarkedNeedsLayout());
return;
}
assert(_relayoutBoundary != null);
if (_relayoutBoundary != this) {
markParentNeedsLayout();
} else {
_needsLayout = true;
if (owner != null) {
assert(() {
if (debugPrintMarkNeedsLayoutStacks)
debugPrintStack(label: 'markNeedsLayout() called for $this');
return true;
}());
//添加到需要重绘的队列中
owner!._nodesNeedingLayout.add(this);
//通知有RenderObject需要重绘
owner!.requestVisualUpdate();
}
}
}