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目标。

Flutter的绘制流程

最后便是调用叶子节点的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中,接着进行栅格化和合成上屏。