1. 动画与微使命阶段:
首要是处理动画及履行一系列微使命。详细是在SchedulerBinding中的handleBeginFrame函数中完成。
void handleBeginFrame(DurationrawTimeStamp) {
...
try {
//TRANSIENTFRAMECALLBACKS
_schedulerPhase= SchedulerPhase.transientCallbacks;
//切换为transientCallbacks阶段
final Map<int,_FrameCallbackEntry>callbacks=_transientCallbacks;
//清空已注册的回调函数
_transientCallbacks= <int,_FrameCallbackEntry>{};
//遍历一切注册的回调办法
callbacks.forEach((intid,_FrameCallbackEntrycallbackEntry) {
if (!_removedIds.contains(id))
//履行现已注册的回调函数
_invokeFrameCallback(callbackEntry.callback,_currentFrameTimeStamp,callbackEntry.debugStack);
});
_removedIds.clear();
} finally {
//切换为midFrameMicrotasks阶段
_schedulerPhase= SchedulerPhase.midFrameMicrotasks;
}
}
2. 构建阶段(build):
在该阶段首要是从头构建符号为“脏”的Widget节点及将需求更新的RenderObject目标符号为“脏”。
当handleBeginFrame函数履行结束后,就会履行handleDrawFrame函数,该函数在SchedulerBinding目标初始化时会与Window相相关,所以除第一次需求主动调用外,其他时候皆是经过Window来调用该函数。
void handleDrawFrame() {
assert(_schedulerPhase== SchedulerPhase.midFrameMicrotasks);
Timeline.finishSync(); //endthe"Animate"phase
try {
//耐久帧回调,该回调会一向存在,不会移除
_schedulerPhase= SchedulerPhase.persistentCallbacks;
for (FrameCallbackcallbackin_persistentCallbacks)
_invokeFrameCallback(callback,_currentFrameTimeStamp);
//当时帧制作完成回调
_schedulerPhase= SchedulerPhase.postFrameCallbacks;
final List<FrameCallback>localPostFrameCallbacks=
List<FrameCallback>.from(_postFrameCallbacks);
_postFrameCallbacks.clear();
//当履行这儿时,代表当时帧现已制作结束
for (FrameCallbackcallbackinlocalPostFrameCallbacks)
_invokeFrameCallback(callback,_currentFrameTimeStamp);
} finally {
//进入空闲状况
_schedulerPhase= SchedulerPhase.idle;
Timeline.finishSync(); //endtheFrame
profile(() {
_profileFrameStopwatch.stop();
_profileFramePostEvent();
});
_currentFrameTimeStamp= null;
}
}
这儿要点关注耐久帧回调,该回调也是UI制作的要害函数,是在RendererBinding目标初始化时注册的。首要函数:persistentCallbacks
mixinRendererBindingonBindingBase,ServicesBinding,SchedulerBinding,GestureBinding,SemanticsBinding,HitTestable{
@override
voidinitInstances(){
super.initInstances();
...
//注册耐久帧回调
addPersistentFrameCallback(_handlePersistentFrameCallback);
}
void_handlePersistentFrameCallback(DurationtimeStamp){
//制作帧
drawFrame();
}
//制作帧
voiddrawFrame(){
//对Widget进行丈量、布局
pipelineOwner.flushLayout();
pipelineOwner.flushCompositingBits();
//对Widget进行制作
pipelineOwner.flushPaint();
//发送数据给GPU
renderView.compositeFrame();//thissendsthebitstotheGPU
pipelineOwner.flushSemantics();//thisalsosendsthesemanticstotheOS.
}
}
在WidgetsBinding中重写了drawFrame函数。在该函数中会创立新的Widget目标替换旧的Widget目标并将不需求的Element节点从树中移除。
@override
void drawFrame() {
...
try {
//假如根结点存在,就从头构建Widget
if (renderViewElement!= null)
buildOwner.buildScope(renderViewElement);
//调用RendererBinding中的drawFrame函数
super.drawFrame();
//移除不再运用的Element节点
buildOwner.finalizeTree();
} finally {...}
...
}
2.1. 从头buildWidget目标:
void buildScope(Elementcontext, [ VoidCallbackcallback]) {
if (callback== null &&_dirtyElements.isEmpty)
return;
try {
//“脏”节点列表需求从头排序
_scheduledFlushDirtyElements= true;
...
//将符号为“脏”的Element节点依据深度进行排序
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting= false;
//符号为“脏”的Element节点数量
intdirtyCount=_dirtyElements.length;
intindex= 0;
//遍历“脏”节点
while (index<dirtyCount) {
try {
//从头构建Widget,及是否复用当时Element
_dirtyElements[index].rebuild();
} catch (e,stack) {
...
}
index+= 1;
//当_dirtyElements调集中的“脏”节点还未处理结束时,又增加了新的“脏”节点
if (dirtyCount<_dirtyElements.length||_dirtyElementsNeedsResorting) {
//依据“脏”节点的深度进行排序
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting= false;
dirtyCount=_dirtyElements.length;
//假如当时节点的深度比新参加的“脏”节点深度要深,则需求将处理坐标指向新参加的“脏”节点
while (index> 0 &&_dirtyElements[index- 1].dirty) {
index-= 1;
}
}
}
} finally {
//铲除_dirtyElements中一切节点的“脏”状况
for (Elementelementin_dirtyElements) {
element._inDirtyList= false;
}
_dirtyElements.clear();
_scheduledFlushDirtyElements= false;
_dirtyElementsNeedsResorting= null;
Timeline.finishSync();
}
}
_dirtyElements是一个调集,存储了一切符号为“脏”的节点。在对其中的“脏”节点进行处理时,需求首要对调集中的“脏”节点进行排序,其排序规矩如下。
●假如“脏”节点的深度不同,则按照深度进行升序排序
●假如“脏”节点的深度相同,则会将“脏”节点放在调集的右侧,“洁净”节点则在在调集的左侧。
在排序完成后,就要遍历该调集,对其中的“脏”节点进行处理。在这儿调用的是rebuild函数,经过该函数,会从头创立“脏”节点下的一切Widget目标,并依据新的Widget目标来判别是否需求重用Element目标。一般只要不是增删Widget,Element目标都会被重用,然后也就会重用RenderObject目标。因为Widget是一个十分轻量级的数据结构,所以在UI更新时做到了把功能损耗降到最低。
假如_dirtyElements中的“脏”节点还未处理结束,就又新增了“脏”节点,那么这时候就会从头排序,确保_dirtyElements调集的左侧永远是“洁净”节点,右侧永远是“脏”节点。
因为rebuild函数比较重要,这儿就要点介绍一下该函数,在rebuild函数中会调用performRebuild函数,该函数是一个抽象函数,在其子类完成,而符号为“脏”的Element都是StatefulElement。所以就来StatefulElement或许其父类中查找performRebuild函数。
abstract class ComponentElement extends Element {
...
@override
void performRebuild() {
Widgetbuilt;
try {
//从头创立新的`Widget`目标
built= build();
debugWidgetBuilderValue(widget,built);
} catch (e,stack) {
//当构建Widget目标犯错时展示的默许页面,能够修正该页面来使异常界面更友爱的显示
built= ErrorWidget.builder(_debugReportException('building$this',e,stack));
} finally {
//铲除“脏”符号
_dirty= false;
}
try {
//更新子Element对应的Widget目标
_child= updateChild(_child,built,slot);
assert(_child!= null);
} catch (e,stack) {
//当构建Widget目标犯错时展示的默许页面
built= ErrorWidget.builder(_debugReportException('building$this',e,stack));
_child= updateChild(null,built,slot);
}
}
}
performRebuild函数做的事很简单,便是创立新的Widget目标来替换旧的目标。上面的build函数调用的便是State类中的build函数,然后再调用Element的updateChild函数,便是更新Element对应的Widget目标。而在updateChild函数中又会调用子Element的update函数,然后调用子Element的performRebuild,然后在调用子Element的updateChild、update函数。以此类推,然后更新其一切子Element的Widget目标。
最后便是调用叶子节点的updateRenderObject函数来更新RenderObject。在更新RenderObject目标时,会依据状况来对需求从头布局及从头制作的RenderObject目标进行符号。然后等待下一次的Vsync信号时来从头布局及制作UI。
2.2. 符号RenderObject:
关于RenderObject目标,能够经过markNeedsLayout及markNeedsPaint来符号是否需求从头布局及从头制作。但在当时阶段只会调用markNeedsLayout来符号需求从头布局的RenderObject目标,鄙人一阶段才会符号需求从头制作的RenderObject,所以先来看markNeedsLayout函数。
void markNeedsLayout() {
...
//判别布局鸿沟是否是是当时RenderObject目标
if (_relayoutBoundary!= this) {
markParentNeedsLayout();
} else {
_needsLayout= true;
if (owner!= null) {
//符号当时RenderObject及其子RenderObject目标需求从头布局
//将当时`RenderObject`增加到调集中。
owner._nodesNeedingLayout.add(this);
owner.requestVisualUpdate();
}
}
}
@protected
void markParentNeedsLayout() {
_needsLayout= true;
final RenderObjectparent= this.parent;
if (!_doingThisLayoutWithCallback) {
//调用父类的markNeedsLayout函数
parent.markNeedsLayout();
} else {
assert(parent._debugDoingThisLayout);
}
assert(parent== this.parent);
}
markNeedsLayout函数的代码完成很简单,便是不断遍历父RenderObject目标,然后找到布局鸿沟的RenderObject目标,并将该RenderObject目标增加到调集_nodesNeedingLayout中,然后鄙人一阶段就从该目标开端布局。
在这儿有个“布局鸿沟”的概念,在Flutter中,能够给恣意节点设置布局鸿沟,即当鸿沟内的任何目标发生从头布局时,不会影响鸿沟外的目标,反之亦然。
在从头构建build函数及符号RenderObject完成后,就进入下一阶段,开端布局。
3. 布局阶段:
核算Widget的巨细及方位的确认。
在该阶段,会确认每个组件的巨细及方位,相当于Android中的onMeasure+onLayout函数所完成的功能。假如是第一次调用该函数,该阶段就会遍历一切的组件,来确认其巨细及方位;不然该阶段就会遍历布局鸿沟内的一切组件,来确认其巨细及方位。
当上一阶段中的buildOwner.buildScope(renderViewElement)函数履行结束后,就会调用RendererBinding的drawFrame函数,该函数完成十分简练。
//制作帧
void drawFrame() {
//对指定组件及其子组件进行巨细丈量及方位确认
pipelineOwner.flushLayout();
pipelineOwner.flushCompositingBits();
pipelineOwner.flushPaint();
renderView.compositeFrame();
pipelineOwner.flushSemantics(); }
其中flushLayout便是进行组件的巨细及方位确认,在该函数中会遍历调集_nodesNeedingLayout并调用调集中每个目标的_layoutWithoutResize函数。
void flushLayout() {
try {
while (_nodesNeedingLayout.isNotEmpty) {
final List<RenderObject>dirtyNodes=_nodesNeedingLayout;
_nodesNeedingLayout= <RenderObject>[];
for (RenderObjectnodeindirtyNodes..sort((RenderObjecta, RenderObjectb) =>a.depth-b.depth)) {
//调用RenderObject目标的_layoutWithoutResize函数
if (node._needsLayout&&node.owner== this)
node._layoutWithoutResize();
}
}
} finally {...}
}
_layoutWithoutResize函数是私有的,所以不存在重写的问题。那么就直接来看该函数。
void _layoutWithoutResize() {
try {
performLayout();
markNeedsSemanticsUpdate();
} catch (e,stack) {...}
_needsLayout= false;
markNeedsPaint();
}
_layoutWithoutResize函数很简单,就直接调用了performLayout函数。
当然,RenderObject目标的size也不是随意确认的,因为在调用RenderObject的layout函数时,会传递一个继承自Constraints的目标。该目标是一个布局束缚,由父传给子,子会依据该目标来决定自己的巨细。
3.1. 符号RenderObject
当巨细及方位确认后,就又会对RenderObject进行一次符号,这次跟上一阶段的符号迥然不同,但这次是符号可制作的RenderObject目标,然后在后边对这些目标进行从头制作。符号可制作的RenderObject目标是经过markNeedsPaint函数来完成的,代码如下。
void markNeedsPaint() {
if (_needsPaint)
return;
_needsPaint= true;
if (isRepaintBoundary) {
//符号需求从头制作的RenderObject目标
//需求制作当时图层
if (owner!= null) {
owner._nodesNeedingPaint.add(this);
owner.requestVisualUpdate();
}
} else if (parentis RenderObject) {
//没有自己的图层,与父类共用同一图层
final RenderObjectparent= this.parent;
//遍历其父RenderObject目标
parent.markNeedsPaint();
} else {
//当是RenderView时,需求自己创立新的图层
if (owner!= null)
owner.requestVisualUpdate();
}
}
markNeedsPaint函数中涉及到了一个“重绘鸿沟”的概念。在进入和走出重绘鸿沟时,Flutter会强制切换新的图层,这样就能够防止鸿沟表里的相互影响。当然重绘鸿沟也能够在任何节点手动设置,可是一般不需求咱们来完成,Flutter提供的控件默许会在需求设置的当地主动设置。
4. compositingBits阶段:
重绘之前的预处理操作,检查RenderObject是否需求重绘。
在组件的巨细及方位确认后,就会进入当时阶段。该阶段首要是做一件事,便是将RenderObject树上新增及删去的RenderObject目标符号为“脏”,方便鄙人一阶段对这些RenderObject目标进行重绘。详细代码完成是在flushCompositingBits函数中,该函数在Layout阶段后当即调用。
void flushCompositingBits() {
...
//将RenderObject目标按照深度进行排序
_nodesNeedingCompositingBitsUpdate.sort((RenderObjecta, RenderObjectb) =>a.depth-b.depth);
for (RenderObjectnodein_nodesNeedingCompositingBitsUpdate) {
if (node._needsCompositingBitsUpdate&&node.owner== this)
//将RenderObject目标及其子目标符号为“脏”
node._updateCompositingBits();
}
_nodesNeedingCompositingBitsUpdate.clear();
...
}
nodesNeedingCompositingBitsUpdate是一个调集,只要RenderObject目标的_needsCompositing为true时,才会增加到该调集中。在RenderObject目标创立时,_needsCompositing的值会依据isRepaintBoundary及alwaysNeedsCompositing来共同判别。
RenderObject() {
//isRepaintBoundary决定当时RenderObject是否与父RenderObject分开制作,默许为false,其值在当时目标的生命周期内无法修正。也便是判别当时目标是否是制作鸿沟
//alwaysNeedsCompositing为true表明当时RenderObject会一向重绘,如视频播映,默许为false
_needsCompositing=isRepaintBoundary||alwaysNeedsCompositing;
}
然后在向树中增加或许删去RenderObject目标时会调用adoptChild及dropChild函数,而这两个函数都会调用markNeedsCompositingBitsUpdate函数,也就在markNeedsCompositingBitsUpdate函数内完成了将当时目标增加到调集中的操作。
//向树中增加当时节点
@override
void adoptChild(RenderObjectchild) {
setupParentData(child);
markNeedsLayout();
//将当时目标的_needsCompositingBitsUpdate值标为true
markNeedsCompositingBitsUpdate();
markNeedsSemanticsUpdate();
super.adoptChild(child);
}
//从树中移除当时节点
@override
void dropChild(RenderObjectchild) {
child._cleanRelayoutBoundary();
child.parentData.detach();
child.parentData= null;
super.dropChild(child);
markNeedsLayout();
//将当时目标的_needsCompositingBitsUpdate值标为true
markNeedsCompositingBitsUpdate();
markNeedsSemanticsUpdate();
}
//
void markNeedsCompositingBitsUpdate() {
if (_needsCompositingBitsUpdate)
return;
_needsCompositingBitsUpdate= true;
if (parentis RenderObject) {
final RenderObjectparent= this.parent;
if (parent._needsCompositingBitsUpdate)
return;
if (!isRepaintBoundary&& !parent.isRepaintBoundary) {
parent.markNeedsCompositingBitsUpdate();
return;
}
}
//将当时目标或许其父目标增加到_nodesNeedingCompositingBitsUpdate调集中
if (owner!= null)
owner._nodesNeedingCompositingBitsUpdate.add(this);
}
这样就会在调用flushCompositingBits函数时,就会调用_updateCompositingBits函数来判别是否将这些目标及子目标符号为“脏”,然后鄙人一阶段进行制作。
void _updateCompositingBits() {
//表明现已处理过,
if (!_needsCompositingBitsUpdate)
return;
finalboololdNeedsCompositing=_needsCompositing;
_needsCompositing= false;
//访问其子目标
visitChildren((RenderObjectchild) {
child._updateCompositingBits();
if (child.needsCompositing)
_needsCompositing= true;
});
//假如是制作鸿沟或许需求一向重绘
if (isRepaintBoundary||alwaysNeedsCompositing)
_needsCompositing= true;
if (oldNeedsCompositing!=_needsCompositing) {
//将当时目标符号为“脏”,
markNeedsPaint();
}
_needsCompositingBitsUpdate= false;
}
5. 制作阶段:
依据Widget巨细及方位来制作UI。
经过调用flushPaint函数就能够重绘现已符号的“脏”RenderObject目标及其子目标。
void flushPaint() {
try {
final List<RenderObject>dirtyNodes=_nodesNeedingPaint;
_nodesNeedingPaint= <RenderObject>[];
//依据节点深度进行排序
for (RenderObjectnodeindirtyNodes..sort((RenderObjecta, RenderObjectb) =>b.depth-a.depth)) {
if (node._needsPaint&&node.owner== this) {
//当时目标是否与layer进行相关
if (node._layer.attached) {
//在图层上制作UI
PaintingContext.repaintCompositedChild(node);
} else {
//越过UI制作,但当时节点为“脏”的状况不会改动
node._skippedPaintingOnLayer();
}
}
}
} finally {}
}
flushPaint函数中,每次遍历“脏”RenderObject目标时,都会进行一次排序,防止重复制作。然后在判别当时目标是否与Layer进行相关,假如没有相关,则无法进行制作,但不会铲除“脏”符号。下面来看repaintCompositedChild函数的完成。
static void repaintCompositedChild(RenderObjectchild, {bool
_repaintCompositedChild(
child,
debugAlsoPaintedParent:debugAlsoPaintedParent,
);
}
static void _repaintCompositedChild(
RenderObjectchild, {
booldebugAlsoPaintedParent= false,
PaintingContextchildContext,
}) {
//拿到Layer目标
OffsetLayerchildLayer=child._layer;
if (childLayer== null) {
//创立新的Layer目标
child._layer=childLayer= OffsetLayer();
} else {
//移除Layer目标的后继节点
childLayer.removeAllChildren();
}
//创立context目标
childContext??= PaintingContext(child._layer,child.paintBounds);
//调用paint函数开端制作
child._paintWithContext(childContext, Offset.zero);
childContext.stopRecordingIfNeeded();
}
5.1. Layer
在该函数中首要是对Layer目标的处理,然后调用_paintWithContext函数,在_paintWithContext函数中就会调用paint这个函数,然后完成UI的制作。至此,就完成了UI的制作,下面再来看一个被疏忽的目标——Layer。
Layer是“图层”意思,在Flutter中是最简单被疏忽但又无比重要的一个类。它十分贴近底层,能够很简单的看到调用Native办法。
Layer跟其他三棵树相同,也是一棵树,有“脏”状况的符号、更新等操作。不同的是,Layer是一个双链表结构,在每个Layer目标中都会指向其前置节点与后置节点(叶子Layer的后置节点为null)。
abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
//返回父节点
@override
ContainerLayer getparent=> super.parent;
//当时节点状况,为true表明当时节点是“脏”数据。需求重绘
bool_needsAddToScene= true;
//将当时节点符号为“脏”
@protected
@visibleForTesting
void markNeedsAddToScene() {
//Alreadymarked.Short-circuit.
if (_needsAddToScene) {
return;
}
_needsAddToScene= true;
}
@protected
boolgetalwaysNeedsAddToScene=> false;
//这个是一个十分重要的东西,首要用于节点数据的缓存。存储当时节点的烘托数据,假如当时节点不需求更新,就直接拿存储的数据运用。
@protected
ui.EngineLayer getengineLayer=>_engineLayer;
//更改当时节点的数据
@protected
set engineLayer(ui.EngineLayervalue) {
_engineLayer=value;
if (parent!= null && !parent.alwaysNeedsAddToScene) {
//将父节点符号需求更新的状况
parent.markNeedsAddToScene();
}
}
}
ui.EngineLayer_engineLayer;
//更新当时节点状况,假如_needsAddToScene为true,则将当时节点符号为“脏”
@protected
@visibleForTesting
void updateSubtreeNeedsAddToScene() {
_needsAddToScene=_needsAddToScene||alwaysNeedsAddToScene;
}
//指向后置节点
Layer getnextSibling=>_nextSibling;
Layer_nextSibling;
//指向前置节点
Layer getpreviousSibling=>_previousSibling;
Layer_previousSibling;
//将子节点从Layer树中移除
@override
void dropChild(AbstractNodechild) {
if (!alwaysNeedsAddToScene) {
markNeedsAddToScene();
}
super.dropChild(child);
}
//将当时节点增加到Layer树中
@override
void adoptChild(AbstractNodechild) {
if (!alwaysNeedsAddToScene) {
markNeedsAddToScene();
}
super.adoptChild(child);
}
//将当时节点从Layer树中移除
@mustCallSuper
void remove() {
parent?._removeChild(this);
}
void addToScene(ui.SceneBuilderbuilder, [ OffsetlayerOffset= Offset.zero]);
void _addToSceneWithRetainedRendering(ui.SceneBuilderbuilder) {
//运用当时节点的缓存的数据
if (!_needsAddToScene&&_engineLayer!= null) {
builder.addRetained(_engineLayer);
return;
}
addToScene(builder);
//将当时节点符号为“洁净”的
_needsAddToScene= false;
}
}
5.2. Layer节点的增加
previousSibling与nextSibling分别是Layer的前置节点与后置节点,当向Layer树中增加Layer节点时,也会将当时Layer设置为父节点的后置节点,父节点设置为当时节点的前置节点。这样,就形成了一颗树。
classContainerLayerextendsLayer{
...
//将当时节点及其链表上的一切子节点都参加到Layer树中
@override
voidattach(Objectowner){
super.attach(owner);
Layerchild=firstChild;
while(child!=null){
child.attach(owner);
child=child.nextSibling;
}
}
//将当时节点及其链表上的一切子节点都从Layer树中移除
@override
voiddetach(){
super.detach();
Layerchild=firstChild;
while(child!=null){
child.detach();
child=child.nextSibling;
}
}
//将child增加到链表中
voidappend(Layerchild){
adoptChild(child);
child._previousSibling=lastChild;
if(lastChild!=null)
lastChild._nextSibling=child;
_lastChild=child;
_firstChild??=child;
}
...
}
在上述的append函数中就将子节点增加到Layer树并参加到双链表中。在adoptChild函数中终究会调用attach函数,然后完成Layer树的增加。
5.3. Layer的状况更新
classContainerLayerextendsLayer{
...
//更新Layer节点的状况。
@override
voidupdateSubtreeNeedsAddToScene(){
super.updateSubtreeNeedsAddToScene();
Layerchild=firstChild;
while(child!=null){
child.updateSubtreeNeedsAddToScene();
_needsAddToScene=_needsAddToScene||child._needsAddToScene;
child=child.nextSibling;
}
}
...
}
updateSubtreeNeedsAddToScene函数便是更新Layer的状况,能够发现,在更新当时Layer的状况时,也会更新其一切子Layer的状况。
6. compositing阶段:
将UI数据发送给GPU处理。
该阶段首要是将更新后的数据传递给GPU。这时候调用的是compositeFrame函数,该函数很简单,便是调用了一个Native函数。
voidcompositeFrame(){
Timeline.startSync('Compositing',arguments:timelineWhitelistArguments);
try{
finalui.SceneBuilderbuilder=ui.SceneBuilder();
finalui.Scenescene=layer.buildScene(builder);
if(automaticSystemUiAdjustment)
_updateSystemChrome();
//更新后数据交给GPU处理
_window.render(scene);
scene.dispose();
}finally{
Timeline.finishSync();
}
}
7. semantics阶段:
与渠道的辅佐功能相关。
在向GPU发送数据后,Flutter还会调用flushSemantics函数。该函数与系统的辅佐功能相关,一般状况下是不做任何处理。
8. finalization阶段:
首要是从Element树中移除无用的Element目标及处理制作结束回调。
在该阶段,首要是将Element目标从树中移除及处理增加在_postFrameCallbacks调集中的回调函数。因为该回调函数是在制作结束时调用,所以在该回调函数中,context现已创立成功。
其他:
flushLayout触发布局,将RenderObject树的dirty节点经过调用performLayout办法进行逐一布局,咱们先看一下RenderPadding中的完成
@override
voidperformLayout() {
_resolve();//解析padding参数
if(child== null) {//假如没有child,直接将constraints与padding归纳核算得出自己的size
size=constraints.constrain(Size(
_resolvedPadding.left+_resolvedPadding.right,
_resolvedPadding.top+_resolvedPadding.bottom
));
return;
}
finalBoxConstraintsinnerConstraints=constraints.deflate(_resolvedPadding);//将padding减去,生成新的束缚innerConstraints
child.layout(innerConstraints, parentUsesSize: true);//用新的束缚去布局child
finalBoxParentDatachildParentData=child.parentData;
childParentData.offset=Offset(_resolvedPadding.left,_resolvedPadding.top);//设置childParentData的offset值
size=constraints.constrain(Size(//将constraints与padding以及child的sieze归纳核算得出自己的size
_resolvedPadding.left+child.size.width+_resolvedPadding.right,
_resolvedPadding.top+child.size.height+_resolvedPadding.bottom
));
}
能够看到RenderPadding中的布局分两种状况。假如没有child,那么就直接拿parent传过来的束缚以及padding来确认自己的巨细;不然就先去布局child,让后再拿parent传过来的束缚和padding以及child的size来确认自己的巨细。RenderPadding是典型的单child的RenderBox,咱们看一下多个child的RenderBox。例如RenderFlow
@override
voidperformLayout() {
size=_getSize(constraints);//直接先确认自己的size
inti=0;
_randomAccessChildren.clear();
RenderBoxchild=firstChild;
while(child!= null) {//遍历孩子
_randomAccessChildren.add(child);
finalBoxConstraintsinnerConstraints=_delegate.getConstraintsForChild(i,constraints);//获取child的束缚,此办法为抽象
child.layout(innerConstraints, parentUsesSize: true);//布局孩子
finalFlowParentDatachildParentData=child.parentData;
childParentData.offset=Offset.zero;
child=childParentData.nextSibling;
i+=1;
}
}
能够看到RenderFlow的size直接就依据束缚来确认了,并没去有先布局孩子,所以RenderFlow的size不依赖与孩子,后边依旧是对每一个child顺次进行布局。
还有一种比较典型的树尖类型的RenderBox,LeafRenderObjectWidget子类创立的RenderObject目标都是,他们没有孩子,他们才是终究需求烘托的目标,例如
@override
voidperformLayout() {
size=_sizeForConstraints(constraints);
}
十分简单就经过束缚确认自己的巨细就结束了。所以performLayout进程便是两点,确认自己的巨细以及布局孩子。咱们上面说到的都是RenderBox的子类,这些RenderObject束缚都是经过BoxConstraints来完成,可是RenderSliver的子类的束缚是经过SliverConstraints来完成,尽管他们对child的束缚方式不同,但他们在布局进程需求履行的操作都是共同的。
制作
布局完成了,PipelineOwner就经过flushPaint来进行制作
voidflushPaint() {
try{
finalList<RenderObject>dirtyNodes=_nodesNeedingPaint;
_nodesNeedingPaint=<RenderObject>[];
//对dirtynodes列表进行排序,最深的在第一位
for(RenderObjectnodeindirtyNodes..sort((RenderObjecta,RenderObjectb)=>b.depth-a.depth)) {
assert(node._layer!= null);
if(node._needsPaint&&node.owner==this) {
if(node._layer.attached) {
PaintingContext.repaintCompositedChild(node);
} else{
node._skippedPaintingOnLayer();
}
}
}
} finally{}
}
PaintingContext.repaintCompositedChild(node)会调用到child._paintWithContext(childContext,Offset.zero)办法,进而调用到child的paint办法,咱们来看一下第一次制作的状况,dirty的node就应该是RenderView,跟进RenderView的paint办法
@override
voidpaint(PaintingContextcontext,Offsetoffset) {
if(child!= null)
context.paintChild(child,offset);//直接制作child
}
自己没有什么制作的内容,直接制作child,再看一下RenderShiftedBox
@override
voidpaint(PaintingContextcontext,Offsetoffset) {
if(child!= null) {
finalBoxParentDatachildParentData=child.parentData;
context.paintChild(child,childParentData.offset+offset);//直接制作child
}
}
如同没有制作内容就直接递归的进行制作child,那找一个有制作内容的吧,咱们看看RenderDecoratedBox
@override
voidpaint(PaintingContextcontext,Offsetoffset) {
_painter??=_decoration.createBoxPainter(markNeedsPaint);//获取painter画笔
finalImageConfigurationfilledConfiguration=configuration.copyWith(size:size);
if(position==DecorationPosition.background) {//画布景
_painter.paint(context.canvas,offset,filledConfiguration);//制作进程,详细细节再painter中
if(decoration.isComplex)
context.setIsComplexHint();
}
super.paint(context,offset);//画child,里边直接调用了paintChild
if(position==DecorationPosition.foreground) {//画远景
_painter.paint(context.canvas,offset,filledConfiguration);
if(decoration.isComplex)
context.setIsComplexHint();
}
}
假如自己有制作内容,paint办法中的完成就应该包括制作自己以及制作child,假如没有孩子就只制作自己的内容,看一下RenderImage
@override
voidpaint(PaintingContextcontext,Offsetoffset) {
if(_image== null)
return;
_resolve();
paintImage(//直接制作Image,详细细节再此办法中
canvas:context.canvas,
rect:offset&size,
image:_image,
scale:_scale,
colorFilter:_colorFilter,
fit:_fit,
alignment:_resolvedAlignment,
centerSlice:_centerSlice,
repeat:_repeat,
flipHorizontally:_flipHorizontally,
invertColors:invertColors,
filterQuality:_filterQuality
);
}
所以基本上制作需求完成的流程便是,假如自己有制作内容,paint办法中的完成就应该包括制作自己以及制作child,假如没有孩子就只制作自己的内容,流程比较简单。
开端履行Dart代码,经历Layout、Paint等进程,生成一棵LayerTree,将制作指令保存在Layer中,接着进行栅格化和合成上屏。