Widget

graph LR
State-->|持有|Widget
Element-->|持有|State
Element-->|持有|Widget

Build

触发State.build时机

graph LR
after_initState-->State.build
after_didUpdateWidget-->State.build
after_setState-->State.build
after_dependencyChange-->State.build
after_deactiveAndReinsertIntoTree-->State.build

挂载树核心方法调用

graph LR
mount-->_firstBuild-->reBuild-->performRebuild-->|1|build-->|stateless|StatelessWidget.build
build-->|stateful|State.build
buildOwner.buildScope-->reBuild
performRebuild-->|2|updateChild
updateChild-->|优先更新而非重新创建child|canUpdate{"canUpdate?"}-->|yes|child.updateWithNewWidget;
canUpdate-->|no|inflateWidgetForNewChildElement-->mount

挂载树挂载过程

graph TB
parentElement-->|2:performRebuild|currentElement
currentElement-->|1:mount|parentElement
currentElement-->|3:buildMyWidget|currentElement

currentElement-->|4:newWidget.createElement|childElement
childElement-->|5:mount|currentElement

类设计

State.setState

//State<T extends StatefulWidget>
/** setState方法标记对应的element需要build
如果当前位于一帧内例如点击(input处理+动画+drawFrame),在调用setState方法之后会触发drawFrame进而触发reBuild
如果当前不位于一帧内,则会策划一次frame*/
@protected void setState(VoidCallback fn) {
  final dynamic result = fn() as dynamic;
  _element.markNeedsBuild();
}

Element.markNeedsBuild

 /// Marks the element as dirty and adds it to the global list of widgets to
 /// rebuild in the next frame.
 ///
 /// Since it is inefficient to build an element twice in one frame,
 /// applications and widgets should be structured so as to only mark
 /// widgets dirty during event handlers before the frame begins, not during
 /// the build itself.
 void markNeedsBuild() {
  _dirty = true;
  owner.scheduleBuildFor(this);
 }
//WidgetsBinding.initInstance时会初始化buildOwner.onBuildScheduled = _handleBuildScheduled;

/// Adds an element to the dirty elements list so that it will be rebuilt
/// when [WidgetsBinding.drawFrame] calls [buildScope].
void scheduleBuildFor(Element element) {
if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
   //如果当前不在一次整体frame流程中,则会调用onBuildScheduled,eq:_handleBuildScheduled策划一次frame
   _scheduledFlushDirtyElements = true;
    onBuildScheduled();
 }
 _dirtyElements.add(element);
 element._inDirtyList = true;
}
void _handleBuildScheduled() {
  ensureVisualUpdate();//may call scheduleFrame();
}

调用WidgetsBinding.drawFrame

drawframe

buildOwner.buildScope(renderViewElement)

/// Establishes a scope for updating the widget tree, and calls the given
 /// `callback`, if any. Then, builds all the elements that were marked as
 /// dirty using [scheduleBuildFor], in depth order.

/// The dirty list is processed after `callback` returns, building all the
 /// elements that were marked as dirty using [scheduleBuildFor], in depth
 /// order. If elements are marked as dirty while this method is running, they
 /// must be deeper than the `context` node, and deeper than any
 /// previously-built node in this pass.

/// To flush the current dirty list without performing any other work, this
 /// function can be called with no callback. This is what the framework does
 /// each frame, in [WidgetsBinding.drawFrame].

void buildScope(Element context, [ VoidCallback callback ]) {
 callback();

 _dirtyElements.sort(Element._sort);
 _dirtyElementsNeedsResorting = false;
 int dirtyCount = _dirtyElements.length;
 int index = 0;
 while (index < dirtyCount) {
 _dirtyElements[index].rebuild();
 }
}

Elements.rebuild

/// Called by the [BuildOwner] when [BuildOwner.scheduleBuildFor] has been
 /// called to mark this element dirty, by [mount] when the element is first
 /// built, and by [update] when the widget has changed.
 void rebuild() {
  performRebuild();
 }

ComponentElements.performRebuild

/// Calls the [StatelessWidget.build] method of the [StatelessWidget] object
 /// (for stateless widgets) or the [State.build] method of the [State] object
 /// (for stateful widgets) and then updates the widget tree.
 ///
 /// Called automatically during [mount] to generate the first build, and by
 /// [rebuild] when the element needs updating.
 @override
 void performRebuild() {
   built = build();
  _child = updateChild(_child, built, slot);
 }

ComponentElements.build

/// Subclasses should override this function to actually call the appropriate
 /// `build` function (e.g., [StatelessWidget.build] or [State.build]) for
 /// their widget.
 @protected
 Widget build();

Element.updateChild

/// Update the given child with the given new configuration.
 ///
 /// This method is the core of the widgets system. It is called each time we
 /// are to add, update, or remove a child based on an updated configuration.

  // |                   | **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].

@protected
 Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
   if(canUpdate) {
     child.update(newWidget);
   } else {
     return inflateWidget(newWidget, newSlot);
   }
 }
Element.update(newWidget)
inflateWidget
/// Create an element for the given widget and add it as a child of this element in the given slot.
@protected
 Element inflateWidget(Widget newWidget, dynamic newSlot) {
   final Element newChild = newWidget.createElement();
   newChild.mount(this, newSlot);
   return newChild;
 }
newChild.mount(this, newSlot);
//Element
/// Add this element to the tree in the given slot of the given parent.
 ///
 /// The framework calls this function when a newly created element is added to
 /// the tree for the first time. Use this method to initialize state that
 /// depends on having a parent. State that is independent of the parent can
 /// more easily be initialized in the constructor.
 ///
 /// This method transitions the element from the "initial" lifecycle state to
 /// the "active" lifecycle state.
@mustCallSuper
 void mount(Element parent, dynamic newSlot) {
 _parent = parent;
 _slot = newSlot;
 _depth = _parent != null ? _parent.depth + 1 : 1;
 _active = true;
 if (parent != null) // Only assign ownership if the parent is non-null
  _owner = parent.owner;
 if (widget.key is GlobalKey) {
  final GlobalKey key = widget.key;
  key._register(this);
 }
 _updateInheritance();
assert(() { _debugLifecycleState = _ElementLifecycle.active; return true; }());
 }
//ComponentElement
@override
 void mount(Element parent, dynamic newSlot) {
  super.mount(parent, newSlot);
  _firstBuild();
 }

RenderObjectElement.mount

@override
 void mount(Element parent, dynamic newSlot) {
  super.mount(parent, newSlot);
  _renderObject = widget.createRenderObject(this);//main
  attachRenderObject(newSlot);//main
  _dirty = false;
 }

widget.createRenderObject

……

attachRenderObject

@override
 void attachRenderObject(dynamic newSlot) {
 _slot = newSlot;
 _ancestorRenderObjectElement = _findAncestorRenderObjectElement();
 _ancestorRenderObjectElement?.insertChildRenderObject(renderObject, newSlot);//main

 final ParentDataElement<RenderObjectWidget> parentDataElement = _findAncestorParentDataElement();
  if (parentDataElement != null)
   _updateParentData(parentDataElement.widget);
 }
RenderObjectElement _findAncestorRenderObjectElement() {
  Element ancestor = _parent;
  while (ancestor != null && ancestor is! RenderObjectElement)
   ancestor = ancestor._parent;
  return ancestor;
 }
ParentDataElement<RenderObjectWidget> _findAncestorParentDataElement() {
  Element ancestor = _parent;
  while (ancestor != null && ancestor is! RenderObjectElement) {
   if (ancestor is ParentDataElement<RenderObjectWidget>)
    return ancestor;
   ancestor = ancestor._parent;
  }
  return null;
 }

RenderObjectElement.update(renderObjectWidget)

@override
void update(covariant RenderObjectWidget newWidget) {
  super.update(newWidget);
  widget.updateRenderObject(this, renderObject);//内部可能会markNeedsLayout或markNeedsPaint进行更新重绘
  _dirty = false;
}

PipelineOwner.flushLayout

–>RenderObject._layoutWithoutResize;

void _layoutWithoutResize() {
 performLayout();
 markNeedsSemanticsUpdate();
 markNeedsPaint();
}

RenderFlex.performLayout

/// Displays its children in a one-dimensional array.
@override
 void performLayout() {
   while (child != null) {
   //配置childParentData供自己使用
   final FlexParentData childParentData = child.parentData;
   //对child进行layout
   child.layout(innerConstraints, parentUsesSize: true);
   allocatedSize += _getMainSize(child);
   crossSize = math.max(crossSize, _getCrossSize(child));
}

//设置自己的size
size = constraints.constrain(Size(idealSize, crossSize));

RenderObject.layout

/// The parent's [performLayout] method should call the [layout] of all its
 /// children unconditionally.
void layout(Constraints constraints, { bool parentUsesSize = false }) {
  RenderObject relayoutBoundary;
  if (!parentUsesSize || sizedByParent || constraints.isTight || parent is! RenderObject) {
    relayoutBoundary = this;
  } else {
    final RenderObject parent = this.parent;
    relayoutBoundary = parent._relayoutBoundary;
  }

  if (!_needsLayout && constraints == _constraints && relayoutBoundary == _relayoutBoundary) {
    return;
  }

 _constraints = constraints;
 _relayoutBoundary = relayoutBoundary;

 if (sizedByParent) {
   performResize();
 }

 performLayout();//single child's performLayout
 markNeedsSemanticsUpdate();
 _needsLayout = false;
 markNeedsPaint();//main

RenderObject.markNeedsPaint

/// Mark this render object as having changed its visual appearance.

/// * [RepaintBoundary], to scope a subtree of render objects to their own
 ///  layer, thus limiting the number of nodes that [markNeedsPaint] must mark
 ///  dirty.

void markNeedsPaint() {
if (_needsPaint)
  return;
 _needsPaint = true;

if (isRepaintBoundary) {
 // If we always have our own layer, then we can just repaint
  // ourselves without involving any other nodes.
  assert(_layer is OffsetLayer);
  if (owner != null) {
   owner._nodesNeedingPaint.add(this);
   owner.requestVisualUpdate();
  }
 } else if (parent is RenderObject) {
  final RenderObject parent = this.parent;
  parent.markNeedsPaint();
 } else {
 if (owner != null)
   owner.requestVisualUpdate();
 } 
}

PipelineOwner.flushCompositingBits

–>renderObject._updateCompositingBits();

renderObject._updateCompositingBits

void _updateCompositingBits() {
  if (!_needsCompositingBitsUpdate)
   return;
  final bool oldNeedsCompositing = _needsCompositing;
  _needsCompositing = false;
  visitChildren((RenderObject child) {
   child._updateCompositingBits();
   if (child.needsCompositing)
    _needsCompositing = true;
  });
  if (isRepaintBoundary || alwaysNeedsCompositing)
   _needsCompositing = true;
  if (oldNeedsCompositing != _needsCompositing)
   markNeedsPaint();
  _needsCompositingBitsUpdate = false;
 }

PipelineOwner.flushPaint

–>PaintingContext.repaintCompositedChild(renderObject)

PaintingContext.repaintCompositedChild(renderObject)

/// A place to paint.
 ///
 /// Rather than holding a canvas directly, [RenderObject]s paint using a painting
 /// context. The painting context has a [Canvas], which receives the
 /// individual draw operations, and also has functions for painting child
 /// render objects.

PaintingContext{}
 
 static void repaintCompositedChild(RenderObject child, { bool debugAlsoPaintedParent = false }) {
    assert(child._needsPaint);
    _repaintCompositedChild(
     child,
     debugAlsoPaintedParent: debugAlsoPaintedParent,
    );
 }


static void _repaintCompositedChild(
  RenderObject child, {
  bool debugAlsoPaintedParent = false,
  PaintingContext childContext,
 }) {
  assert(child.isRepaintBoundary);

  OffsetLayer childLayer = child._layer;
  if (childLayer == null) {
    child._layer = childLayer = OffsetLayer();
  } else {
    assert(childLayer is OffsetLayer);
    childLayer.removeAllChildren();
  }

 childContext ??= PaintingContext(child._layer, child.paintBounds);
 child._paintWithContext(childContext, Offset.zero);//main

 // Double-check that the paint method did not replace the layer (the first
 // check is done in the [layer] setter itself).
 assert(identical(childLayer, child._layer));
 childContext.stopRecordingIfNeeded();

paint总结

graph LR
_paintWithContext-->paint-->|isParent|PaintingContext.paintChild-->|child.isRepaintBoundary|stopRecordingAnd_compositeChild
PaintingContext.paintChild-->|notBoundary|child._paintWithContext-->_paintWithContext
paint-->|isChild|paintIntoCanvas

renderObject._paintWithContext

void _paintWithContext(PaintingContext context, Offset offset) {
  if (_needsLayout)
    return;
  _needsPaint = false;

  paint(context, offset);//main
}

RenderFlex.paint

@override
void paint(PaintingContext context, Offset offset) {
  if (!_hasOverflow) {
    defaultPaint(context, offset);//main
    return;
   }
  
  // There's no point in drawing the children if we're empty.
  if (size.isEmpty)
    return;
  
 // We have overflow. Clip it.
 context.pushClipRect(needsCompositing, offset, Offset.zero & size, defaultPaint);

 paintOverflowIndicator(context, offset, Offset.zero & size, overflowChildRect, overflowHints: debugOverflowHints);
/// Paints each child by walking the child list forwards.

void defaultPaint(PaintingContext context, Offset offset) {
  ChildType child = firstChild;
  while (child != null) {
   final ParentDataType childParentData = child.parentData;
   context.paintChild(child, childParentData.offset + offset);//main
   child = childParentData.nextSibling;
  }
 }

PaintingContext.paintChild

/// Paint a child [RenderObject].
 ///
 /// If the child has its own composited layer, the child will be composited
 /// into the layer subtree associated with this painting context. Otherwise,
 /// the child will be painted into the current PictureLayer for this context.

/// 只有绘制边界节点才有layer。render tree的根节点renderView也是一个绘制边界
//将所有的repaintBoundary按照tree结构生成LayerTree,并用各自的offsetLayer去绘制自己和自己的children

void paintChild(RenderObject child, Offset offset) {
  if (child.isRepaintBoundary) {
     stopRecordingIfNeeded();
    _compositeChild(child, offset);
  } else {
    child._paintWithContext(this, offset);
  }
stopRecordingIfNeeded
@protected
@mustCallSuper
void stopRecordingIfNeeded() {
  if (!_isRecording)
    return;

  _currentLayer.picture = _recorder.endRecording();//main
  _currentLayer = null;
  _recorder = null;
  _canvas = null;
}

  /// Finishes recording graphical operations.
  ///
  /// Returns a picture containing the graphical operations that have been
  /// recorded thus far. After calling this function, both the picture recorder
  /// and the canvas objects are invalid and cannot be used further.
  Picture endRecording() {
    if (_canvas == null)
      throw StateError('PictureRecorder did not start recording.');
    final Picture picture = Picture._();
    _endRecording(picture);
    _canvas!._recorder = null;
    _canvas = null;
    return picture;
  }
_compositeChild
 void _compositeChild(RenderObject child, Offset offset) {
 // Create a layer for our child, and paint the child into it.
 // paint 是 deepest first,因此tree底部的一条repaint boundary绘制完成后,_needsPaint为false,不会重复绘制,但要求对应的layer挂到layerTree上

  if (child._needsPaint) {
    repaintCompositedChild(child, debugAlsoPaintedParent: true);
  }

 assert(child._layer is OffsetLayer);
 final OffsetLayer childOffsetLayer = child._layer;
 childOffsetLayer.offset = offset;
 appendLayer(child._layer);
 }

  @protected
  void appendLayer(Layer layer) {
    assert(!_isRecording);
    layer.remove();
    _containerLayer.append(layer);
  }

RenderImage.paint

  @override
  void paint(PaintingContext context, Offset offset) {
     paintImage(
      canvas: context.canvas,
      rect: offset & size,
      image: _image!,
      ......
    );
  }

PaintingContext.canvas

@override
  Canvas get canvas {
    if (_canvas == null)
      _startRecording();
    return _canvas!;
  }

_startRecording

void _startRecording() {
  assert(!_isRecording);
  _currentLayer = PictureLayer(estimatedBounds);//main
  _recorder = ui.PictureRecorder();
  _canvas = Canvas(_recorder);
  _containerLayer.append(_currentLayer);
 }
/// Records a [Picture] containing a sequence of graphical operations.
///
/// To begin recording, construct a [Canvas] to record the commands.
/// To end recording, use the [PictureRecorder.endRecording] method.
class PictureRecorder extends NativeFieldWrapperClass2 {
  
/// An object representing a sequence of recorded graphical operations.
///
/// To create a [Picture], use a [PictureRecorder].
///
/// A [Picture] can be placed in a [Scene] using a [SceneBuilder], via
/// the [SceneBuilder.addPicture] method. A [Picture] can also be
/// drawn into a [Canvas], using the [Canvas.drawPicture] method.
@pragma('vm:entry-point')
class Picture extends NativeFieldWrapperClass2 {

canvas.drawXxx

void paintImage({
  required Canvas canvas,
  required Rect rect,
  required ui.Image image,
  ......
}) {
  ......
  canvas.save();
  canvas.drawImageRect(image, sourceRect, destinationRect, paint);
  canvas.restore();

renderView.compositeFrame

/// The root of the render tree.
RenderView {
 /// Uploads the composited layer tree to the engine.
 /// Actually causes the output of the rendering pipeline to appear on screen.
 void compositeFrame() {
	final ui.SceneBuilder builder = ui.SceneBuilder();
  final ui.Scene scene = layer.buildScene(builder);//main
  if (automaticSystemUiAdjustment)
    _updateSystemChrome();
    _window.render(scene);//执行render()将layer树发送给GPU线程,main
  
   scene.dispose();
}

layer.buildScene(builder)

ContainerLayer.buildScene

/// A composited layer that has a list of children.
// ContainerLayer

/// Consider this layer as the root and build a scene (a tree of layers)
 /// in the engine.
 // The reason this method is in the `ContainerLayer` class rather than
 // `PipelineOwner` or other singleton level is because this method can be used
 // both to render the whole layer tree (e.g. a normal application frame) and
 // to render a subtree (e.g. `OffsetLayer.toImage`).
  ui.Scene buildScene(ui.SceneBuilder builder) {

   updateSubtreeNeedsAddToScene();
   addToScene(builder);
   // Clearing the flag _after_ calling `addToScene`, not _before_. This is
   // because `addToScene` calls children's `addToScene` methods, which may
   // mark this layer as dirty.
   _needsAddToScene = false;
   final ui.Scene scene = builder.build();

   return scene;
  }
@override
 void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
  addChildrenToScene(builder, layerOffset);
 }

/// Uploads all of this layer's children to the engine.
void addChildrenToScene(ui.SceneBuilder builder, [ Offset childOffset = Offset.zero ]) {
  Layer child = firstChild;
  while (child != null) {
   if (childOffset == Offset.zero) {
    child._addToSceneWithRetainedRendering(builder);
   } else {
    child.addToScene(builder, childOffset);
   }
   child = child.nextSibling;
  }
 }

PictureLayer.addToScene

@override
 void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
  builder.addPicture(layerOffset, picture, isComplexHint: isComplexHint, willChangeHint: willChangeHint);//main
 }

OffsetLayer.addToScene

/// A layer that is displayed at an offset from its parent layer.
OffsetLayer {
  @override
 void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
  // Skia has a fast path for concatenating scale/translation only matrices.
  // Hence pushing a translation-only transform layer should be fast. For
  // retained rendering, we don't want to push the offset down to each leaf
  // node. Otherwise, changing an offset layer on the very high level could
  // cascade the change to too many leaves.
  engineLayer = builder.pushOffset(layerOffset.dx + offset.dx, layerOffset.dy + offset.dy, oldLayer: _engineLayer);//main
  addChildrenToScene(builder);//main
  builder.pop();
 }
}

TransformLayer.addToScene

/// A composited layer that applies a given transformation matrix to its
 /// children.
 ///
 /// This class inherits from [OffsetLayer] to make it one of the layers that
 /// can be used at the root of a [RenderObject] hierarchy.

TransformLayer {

@override
 void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
  assert(transform != null);
  _lastEffectiveTransform = transform;
  final Offset totalOffset = offset + layerOffset;
  if (totalOffset != Offset.zero) {
   _lastEffectiveTransform = Matrix4.translationValues(totalOffset.dx, totalOffset.dy, 0.0)
    ..multiply(_lastEffectiveTransform);
  }
  engineLayer = builder.pushTransform(_lastEffectiveTransform.storage, oldLayer: _engineLayer);
  addChildrenToScene(builder);//main
  builder.pop();
 }
  
}

_window.render(scene)总结

_window.render(scene);//执行render()将layer树发送给GPU线程,main

/**
Updates the view's rendering on the GPU with the newly provided Scene.

To record graphical operations, 
1. first create a PictureRecorder, then construct a Canvas, passing that PictureRecorder to its constructor. 
2. After issuing all the graphical operations, 
3. call the PictureRecorder.endRecording function on the PictureRecorder to obtain the final Picture that represents the issued graphical operations.

4. Next, create a SceneBuilder, and add the Picture to it using SceneBuilder.addPicture. 
5. With the SceneBuilder.build method you can then obtain a Scene object, which you can 
6. display to the user via this render function.
**/
void render(Scene scene) => _render(scene, this);
void _render(Scene scene, FlutterView view) native 'PlatformConfiguration_render';
graph LR
subgraph PictureRcorder
_startRecording-->canvas.drawXxx-->endRecording
end

subgraph SceneBuidler
endRecording-->|Picture|buildScene("layer.buildScene(scenebuilder)")
end

subgraph render
buildScene-->|Scene|_window.render
end

_window.render中surface相关细节

其他

RenderObject

markNeedsLayout

void markNeedsLayout() {
  if (_needsLayout) {
    return;
  }

  if (_relayoutBoundary != this) {
    markParentNeedsLayout();
  } else {
   _needsLayout = true;
  
  if (owner != null) {
    owner._nodesNeedingLayout.add(this);
    owner.requestVisualUpdate();
  }
}

dropChild

@override
 void dropChild(RenderObject child) {
   super.dropChild(child);
   markNeedsLayout();
   markNeedsCompositingBitsUpdate();
   markNeedsSemanticsUpdate();
 }

SemanticsConfiguration

/// Describes the semantic information associated with the owning
 /// [RenderObject].
 ///
 /// The information provided in the configuration is used to generate the
 /// semantics tree.

SemanticsConfiguration