As the 21st article in this series, this article will introduce the overall rendering principle of Flutter Framework from a different perspective, and deeply analyze the drawing process of Flutter after the formation of Layer, so that developers can have a clearer understanding of the rendering principle and implementation logic of Flutter.

Article summary address:

A complete series of articles on Flutter

A series of articles on the world outside Flutter

1. Layer related review

To review, we know that the controls in Flutter go through widgets -> Element -> RenderObject -> Layer. The composition of Layer is determined by the isRepaintBoundary flag bit in the RenderObject.

When setState is called, the RenderObject looks up to the parent node and depending on whether isRepaintBoundary is true, determines whether or not to trigger a redraw from there, in other words, which areas to update.

For example, when Navigator jumps to different routing pages, there is a RepaintBoundary control inside each page. The isRepaintBoundary marker bit in the corresponding RenderRepaintBoundary is true. Thus, an independent Layer is formed between routing pages.

Therefore, the related RenderObjects together form the Layer, and the Layer Tree made up of the layers is submitted to the Flutter Engine to draw the image.

So how does Layer work? What is its nature? How does the Layer of the Flutter Framework be submitted to Engine?

Draw in the Flutter Framework

With the previous Layer question in mind, let’s start with an assumption: how can we render a scene without the controls encapsulated in the Flutter Framework? Or how do you create a Layer?

For example, as shown in the following code, you see a 100 x 100 blue square in the center, and there are no widgets, RenderObjects, or even layers in the code. Instead, the relatively unfamiliar objects such as PictureRecorder, Canvas and SceneBuilder were used to complete the painting, and window.render was executed at the end.

import 'dart:ui' as ui;

void main() { ui.window.onBeginFrame = beginFrame; ui.window.scheduleFrame(); } void beginFrame(Duration timeStamp) { final double devicePixelRatio = ui.window.devicePixelRatio; Final UI.picturerecOrder Recorder = UI.picturerecOrder (); Canvas Canvas = uI.canvas (Recorder); // Create a Canvas final UI based on the artboard. canvas.scale(devicePixelRatio, devicePixelRatio); Var centerX = UI. Window. PhysicalSize. Width / 2.0; Var centerY. = UI window. PhysicalSize. Height / 2.0; DrawRect (rect.fromCenter (center: offset.zero, width: 100, height: 100), new Paint().. color = Colors.blue); Picture Picture = recorder. EndRecording (); final ui.SceneBuilder sceneBuilder = ui.SceneBuilder() .. pushOffset(centerX, centerY) .. addPicture(ui.Offset.zero, picture) .. pop(); ui.window.render(sceneBuilder.build()); }Copy the code

Because Canvas creation in Flutter must have a PictureRecorder, which creates a picture to record drawing, in the above code:

  • I’m gonna createPictureRecorder;
  • Then use thePictureRecorderTo create theCanvas
  • After usingCanvasDraw a small blue square;
  • Pass after drawingSceneBuilderpushOffsetaddPictureLoaded the drawn content;
  • throughwindow.renderDraw the picture.

Note that ⚠️ : the render method is limited to onBeginFrame or onDrawFrame, so the upper code will have window. OnBeginFrame = beginFrame; . There are many similar examples in the official examples/layers/raw/.

You can see that the last step of the underlying draw of the Flutter Framework is window.render, as shown in the following code: The render method requires a Scene object, and the render method is a native method, which means that the Flutter Framework ultimately submits to the Engine a Scene.

  void render(Scene scene) native 'Window_render';
Copy the code

theSceneWhat is it? As mentioned earlierLayerWhere is it? What is the relationship between them?

3. The difference between Scene and Layer

In a Flutter, Scene is a Native object that corresponds to the scene.cc structure of Engine, which contains a layer_tree_for drawing. The Scene in Engine is related to layer_tree_.

Then, in the Flutter Framework, scenes can only be built by SceneBuilder. SceneBuilder has many methods, such as: Engine creates a corresponding EngineLayer after executing the pushOffset, pushClipRect, and pushOpacity methods.

  OffsetEngineLayer pushOffset(double dx, double dy, { OffsetEngineLayer oldLayer }) {
    assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushOffset'));
    final OffsetEngineLayer layer = OffsetEngineLayer._(_pushOffset(dx, dy));
    assert(_debugPushLayer(layer));
    return layer;
  }
  EngineLayer _pushOffset(double dx, double dy) native 'SceneBuilder_pushOffset';
Copy the code

So SceneBuilder can create an “EngineLayer” through push and other related methods before building the Scene. For example, SceneBuilder creates the corresponding layer offset through pushOffset.

Then look at the Layer of the Flutter Framework. As shown in the following code, the EngineLayer parameter exists on the Layer by default, so the Layer must be related to SceneBuilder.

  @protected
  ui.EngineLayer get engineLayer => _engineLayer;

  @protected
  set engineLayer(ui.EngineLayer value) {
    _engineLayer = value;
    if(! alwaysNeedsAddToScene) {if(parent ! = null && ! parent.alwaysNeedsAddToScene) { parent.markNeedsAddToScene(); } } } ui.EngineLayer _engineLayer; /// Override this method to upload this layer to the engine. /// /// Return the engine layerfor retained rendering. When there no
  /// corresponding engine layer, null is returned.
  
  @protected
  void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]);

Copy the code

Second, there is a key method in Layer: AddToScene, you can see from the comments that this method is implemented by a subclass, and you can get an EngineLayer, and this method needs a SceneBuilder, The implementation of the query method happens to be OffsetLayer and PictureLayer.

So the following code can be seen in the addToScene method implementation of OffsetLayer and PictureLayer:

  • PictureLayerCall theSceneBuilderaddPicture;
  • OffsetLayerCall theSceneBuilderpushOffset
Class PictureLayer extends Layer {··· @Override void addToScene(uI.sceneBuilder Builder, [ Offset layerOffset = Offset.zero ]) { builder.addPicture(layerOffset, picture, isComplexHint: isComplexHint, willChangeHint: willChangeHint); } ··· ·} class extends ContainerLayer {··· ···} Class extends ContainerLayer ({Offset Offset = offset.zero}) : _offset = Offset; @override void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) { engineLayer = builder.pushOffset( layerOffset.dx + offset.dx, layerOffset.dy + offset.dy, oldLayer: _engineLayer as ui.OffsetEngineLayer, ); addChildrenToScene(builder); builder.pop(); }...}Copy the code

So here SceneBuilder and Layer are successfully connected by EngineLayer and addToScene methods, and the Scene submitted by window.render is constructed by SceneBuilder, Therefore, as shown in the figure below, Layer and Scene are together in this way.

The code in the front of the little blue box is shown in the following code, which is modified to use Layer. You can see that this implementation is closer to the implementation of the Flutter Framework: The Layer tree is built from rootLayer level by appEnd, and when rootLayer calls addToScene, because the addChildrenToScene method is executed, So we go down to addToScene of the Child Layer.

import 'dart:ui' as ui;

void main() {
  ui.window.onBeginFrame = beginFrame;

  ui.window.scheduleFrame();
}

void beginFrame(Duration timeStamp) {
  final double devicePixelRatio = ui.window.devicePixelRatio;
  
  ///创建一个画板
  final ui.PictureRecorder recorder = ui.PictureRecorder();

  ///基于画板创建一个 Canvas
  final ui.Canvas canvas = ui.Canvas(recorder);
  canvas.scale(devicePixelRatio, devicePixelRatio);

  var centerX = ui.window.physicalSize.width / 2.0;
  var centerY = ui.window.physicalSize.height / 2.0;

  ///画一个 100 的剧中蓝色
  canvas.drawRect(Rect.fromCenter(center: Offset.zero, width: 100, height: 100),
      new Paint()..color = Colors.blue);

  final ui.SceneBuilder sceneBuilder = ui.SceneBuilder();

  OffsetLayer rootLayer = new OffsetLayer();


  OffsetLayer offsetLayer = new OffsetLayer(offset: Offset(centerX, centerY));
  rootLayer.append(offsetLayer);

  PictureLayer pictureLayer = new PictureLayer(Rect.zero);
  pictureLayer.picture = recorder.endRecording();
  offsetLayer.append(pictureLayer);


  rootLayer.addToScene(sceneBuilder);


  ui.window.render(sceneBuilder.build());
}

Copy the code

4. Varieties of Layer

Here is an additional description of the common layers of A Flutter, as shown in the following figure. There are ContainerLayer and non-containerlayer in a Flutter.

ContainerLayer can have child nodes with the append method, which can be divided into:

  • Displacement of the class (OffsetLayer/TransformLayer);
  • Transparent class (OpacityLayer)
  • Cut class (ClipRectLayer/ClipRRectLayer/ClipPathLayer);
  • Shadow class (PhysicalModelLayer)

Why do these layers need to be ContainerLayer? Because these layers are pixel composition operations, they do not have the ability to “depict” the controls themselves, just like the previous example of the blue square, if you want to display the picture, you generally need to combine with PictureLayer.

For example, inside the RenderClipRRect of the ClipRRect control, the ClipRRectLayer can be created when pushClipRRect, The newly created ClipRRectLayer adds children to the parent Layer using the appendLayer method that triggers the Append operation.

Non-containerlayer typically does not have child nodes, such as:

  • PictureLayerIt is used to draw images on which the controls on Flutter are basically drawn;
  • TextureLayerIs for external textures, such as video playback or camera data;
  • PlatformViewLayerIt’s for iOSPlatformViewUse of related embedded textures;

For example, the Canvas for control drawing comes from PaintingContext, The PaintingContext is rendered with _repaintCompositedChild and the Picture is submitted to the picturelayer.picture as shown below.

void stopRecordingIfNeeded() {
    if(! _isRecording)return;
    _currentLayer.picture = _recorder.endRecording();
    _currentLayer = null;
    _recorder = null;
    _canvas = null;
  }
Copy the code

Five, Layer inside and outside repair

Now that you know how the Layer is rendered, it’s time to refresh and reuse the Layer.

We know that when the isRepaintBoundary of the RenderObject is ture, the Flutter Framework automatically creates an OffsetLayer to “load” the region. Screen updates inside the Layer generally do not affect other layers.

theLayerHow is it updated? That’s where it comes inLayerThe inside of themarkNeedsAddToSceneupdateSubtreeNeedsAddToSceneThese two methods.

As shown in the following code, the markNeedsAddToScene method simply marks the _needsAddToScene in the Layer as true; And updateSubtreeNeedsAddToScene method is to iterate through all the child Layer, By recursive call updateSubtreeNeedsAddToScene () to determine whether there is a child need _needsAddToScene, if is then set himself on tag to true.

  @protected
  @visibleForTesting
  void markNeedsAddToScene() {
    // Already marked. Short-circuit.
    if (_needsAddToScene) {
      return;
    }

    _needsAddToScene = true;
  }
  
  @override
  void updateSubtreeNeedsAddToScene() {
    super.updateSubtreeNeedsAddToScene();
    Layer child = firstChild;
    while (child != null) {
      child.updateSubtreeNeedsAddToScene();
      _needsAddToScene = _needsAddToScene || child._needsAddToScene;
      child = child.nextSibling;
    }
  }

Copy the code

Isn’t it similar to setState calling markNeedsBuild and marking itself _dirty? When _needsAddToScene is true, addToScene at the corresponding Layer will be called. Layer reuse is triggered when _needsAddToScene of the Layer is false and _engineLayer is not empty.

void _addToSceneWithRetainedRendering(ui.SceneBuilder builder) {
 
    if(! _needsAddToScene && _engineLayer ! = null) { builder.addRetained(_engineLayer);return;
    }
    addToScene(builder);

    _needsAddToScene = false;
  }
Copy the code

Yes, a Layer whose _needsAddToScene is false indicates that it does not need to update, and the Layer’s EngineLayer exists, so it can be reused. For example: when a new page is opened, the bottom page does not change, it only participates in the composition of the picture, so for the bottom page, its “Layer” can be directly reused to participate in the drawing.

themarkNeedsAddToSceneWhen will it be called?

As shown in the figure below, when the parameters of the Layer child, such as PictureLayer’s picture and OffsetLayer’s offset change, the Layer will actively call markNeedsAddToScene to mark itself as “dirty”. In addition, when the Layer’s engineLayer changes, it will try to trigger the parent node’s Layer to call markNeedsAddToScene, so that the parent node will also change accordingly.

@protected
  set engineLayer(ui.EngineLayer value) {
    _engineLayer = value;
    if(! alwaysNeedsAddToScene) {if (parent != null && !parent.alwaysNeedsAddToScene) {
        parent.markNeedsAddToScene();
      }
    }
  }
Copy the code

And updateSubtreeNeedsAddToScene is triggered when buildScene, call before addToScene updateSubtreeNeedsAddToScene again to judge the child nodes, To determine if a change is needed.

ui.Scene buildScene(ui.SceneBuilder builder) {
    List<PictureLayer> temporaryLayers;
    assert(() {
      if (debugCheckElevationsEnabled) {
        temporaryLayers = _debugCheckElevations();
      }
      return true; } ()); updateSubtreeNeedsAddToScene(); addToScene(builder); _needsAddToScene =false;
    final ui.Scene scene = builder.build();

    return scene;
  }
Copy the code

The Layer structure of the Flutter Framework

Render _window. Render is called in the compositeFrame method of the RenderView. The RenderView is initialized in the initRendererBinding; InitRenderView is called in initInstances, which is runApp.

RenderView is created at runApp, and the compositeFrame inside the RenderView is used to render the Layer rendering via _window. Render.

  void compositeFrame() {
    Timeline.startSync('Compositing', arguments: timelineWhitelistArguments);
    try {
      final ui.SceneBuilder builder = ui.SceneBuilder();
      final ui.Scene scene = layer.buildScene(builder);
      if (automaticSystemUiAdjustment)
        _updateSystemChrome();
      _window.render(scene);
      scene.dispose();
      assert(() {
        if(debugRepaintRainbowEnabled || debugRepaintTextRainbowEnabled) debugCurrentRepaintColor = DebugCurrentRepaintColor. WithHue (360.0) (debugCurrentRepaintColor. Hue + 2.0) %.return true;
      }());
    } finally {
      Timeline.finishSync();
    }
  }
Copy the code

So runApp created RenderView Flutter, and Window drawFrame method invokes the renderView.com positeFrame (); RenderView is the root node of the Flutter. The rootLayer it carries is a subclass of OffsetLayer TransformLayer, which is the root node of the Layer in Flutter.

Here is an example, as shown in the figure below is a simple non-standard code, after running the result is a blank black page, here we use the debugDumpLayerTree method to print the mechanism of Layer.

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    new Future.delayed(Duration(seconds: 1), () {
      debugDumpLayerTree();
    });
    return MaterialApp(
      title: 'GSY Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: Container(), //routes: routers, ); }}Copy the code

The result is printed as shown in the following LOG. As mentioned earlier, the TransformLayer acts as the rooterLayer and its owner is RenderView, and it has two child nodes: Child1 OffsetLayer and child2 PictureLayer.

By default there will be at least one OffsetLayer and PictureLayer due to the Layer formation mechanism (isRepaintBoundary automatically creates an OffsetLayer for ture) and Canvas drawing.

I/flutter (32494): TransformLayer#f8fa5
I/flutter (32494):  │ owner: RenderView#2d51eI/flutter (32494) : │ creator: [root] I/flutter (32494) : │ offset: offset (0.0, 0.0) I/flutter (32494) : │ transform: I/flutter (32494) : │ [0] 2.8, 0.0, 0.0, 0.0 I/flutter (32494) : │ [1] of 0.0, 2.8, 0.0, 0.0 I/flutter (32494) : │ [2] of 0.0, 0.0, 1.0, 0.0 I/flutter (32494) : │ [3] of 0.0, 0.0, 0.0, 1.0 I/flutter (32494) : │ I/flutter (32494) : ├ ─ child 1: OffsetLayer#4503bI/ FLUTTER (32494): │ creator: RepaintBoundary ← _FocusMarker ← Semantics ← FocusScope I/flutter (32494): │ │ ← PageStorage ← Offstage ← _ModalScopeStatus ← I/flutter (32494): [LabeledGlobalKey │ │ _ModalScope < dynamic > - < _ModalScopeState < dynamic > >#e1be1]I/ FLUTTER (32494): │ ← overlayentry -[LabeledGlobalKey<_OverlayEntryState>Please # 95107]│ │ Overlay-[LabeledGlobalKey<OverlayState># ceb36] please..I/flutter (32494) : │ │ offset: offset (0.0, 0.0) I/flutter (32494) : │ │ I/flutter (32494) : │ └ ─ child 1: OffsetLayer#e8309I/ FLUTTER (32494): │ Creator: RepaintBoundary-[GlobalKey]# bbad8] please IgnorePointer pleaseI/flutter (32494): │ FadeTransition ← FractionalTranslation ← SlideTransition ← I/flutter (32494): │ _FadeUpwardsPageTransition please AnimatedBuilder please RepaintBoundary I/flutter (32494) : │ ← _FocusMarker ← Semantics ← FocusScope ← PageStorage ←... I/flutter (32494): │ offset: ├ ─ imp (2): └─ imp (2): └─ imp (3): garbage 2#be4f1I/ FLUTTER (32494): paint Bounds: Rect.fromLTRB(0.0, 0.0, 1080.0, 2030.0)Copy the code

According to the above LOG, first look:

  • OffsetLayercreatorRepaintBoundaryAnd its source isOverlayWe know that Flutter can pass throughOverlayDo global hover controls, whileOverlayIs in theMaterialAppNavigator, and it is a stand-aloneLayer
  • whileOffsetLayerThe child isPageStoragePageStorageIs through theRouteThat is, the default route first page.

So now you know whyOverlayCan be found inMaterialAppAll routes are displayed in global suspension.

The Scaffold was added to the original code as shown below and the debugDumpLayerTree was executed.


void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    new Future.delayed(Duration(seconds: 1), () {
      debugDumpLayerTree();
    });
    return MaterialApp(
      title: 'GSY Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( body: Container(), ), //routes: routers, ); }}Copy the code

You can see that there’s a PhysicalModelLayer and a PictureLayer, PhysicalModelLayer is for setting shadows and things like that, After such as closing debugDisablePhysicalShapeLayers AppBar shadow will disappear, and after PictureLayer is also used for drawing.

I/flutter (32494): TransformLayer#ac14b
I/flutter (32494):  │ owner: RenderView#f5eccI/flutter (32494) : │ creator: [root] I/flutter (32494) : │ offset: offset (0.0, 0.0) I/flutter (32494) : │ transform: I/flutter (32494) : │ [0] 2.8, 0.0, 0.0, 0.0 I/flutter (32494) : │ [1] of 0.0, 2.8, 0.0, 0.0 I/flutter (32494) : │ [2] of 0.0, 0.0, 1.0, 0.0 I/flutter (32494) : │ [3] of 0.0, 0.0, 0.0, 1.0 I/flutter (32494) : │ I/flutter (32494) : ├ ─ child 1: OffsetLayer#c0128I/ FLUTTER (32494): │ creator: RepaintBoundary ← _FocusMarker ← Semantics ← FocusScope I/flutter (32494): │ │ ← PageStorage ← Offstage ← _ModalScopeStatus ← I/flutter (32494): [LabeledGlobalKey │ │ _ModalScope < dynamic > - < _ModalScopeState < dynamic > >#fe143]I/ FLUTTER (32494): │ ← overlayentry -[LabeledGlobalKey<_OverlayEntryState># 9 cb60] please│ │ Overlay-[LabeledGlobalKey<OverlayState># ee455] please..I/flutter (32494) : │ │ offset: offset (0.0, 0.0) I/flutter (32494) : │ │ I/flutter (32494) : │ └ ─ child 1: OffsetLayer#fb2a6I/ FLUTTER (32494): │ creator: RepaintBoundary-[GlobalKey]# fd46b] please IgnorePointer pleaseI/flutter (32494): │ │ FadeTransition ← FractionalTranslation ← SlideTransition ← I/flutter (32494): │ │ _FadeUpwardsPageTransition please AnimatedBuilder please RepaintBoundary I/flutter (32494) : │ │ ← _FocusMarker ← Semantics ← FocusScope ← PageStorage ←... I/flutter (32494): │ │ offset: ├ ─ imp. 1, ├ ─ imp. 1, ├ ─ imp. 2, ├ ─ imp. 2, ├ ─ imp. 2, ├ ─ imp. 2, ├ ─ imp. 2, ├ ─ imp. 2, ├ ─ imp#f1460I/ FLUTTER (32494): │ creator: PhysicalModel ← AnimatedPhysicalModel ← Material ← I/flutter (32494): │ │ scaffoldScope ← Scaffold Semantics I/ FLUTTER (32494): │ │ ← Builder ← RepaintBoundary-[GlobalKey# fd46b] please IgnorePointer pleaseI/flutter (32494): │ │ FadeTransition ← FractionalTranslation ←... I/flutter (32494): │ │ elevation: 0.0 I/flutter (32494) : │ │ color: color (0 xfffafafa) I/flutter (32494) : │ │ I/flutter (32494) : │ └ ─ child 1: PictureLayer#f800fI/flutter (32494): │ paint bounds: I/flutter (32494): Child 2: └ ─ PictureLayer#af14dPaint Bounds: Rect.fromLTRB(0.0, 0.0, 1080.0, 2030.0) I/flutter (32494):Copy the code

PictureLayer, AnnotatedRegionLayer, and TransformLayer are added to the Layer tree by using Navigator to jump to another page and print the Layer tree on the new page. The AnnotatedRegionLayer is used to render the status bar at the top of a new page.

I/flutter (32494): TransformLayer#12e21
I/flutter (32494):  │ owner: RenderView#aa5c7I/flutter (32494) : │ creator: [root] I/flutter (32494) : │ offset: offset (0.0, 0.0) I/flutter (32494) : │ transform: I/flutter (32494) : │ [0] 2.8, 0.0, 0.0, 0.0 I/flutter (32494) : │ [1] of 0.0, 2.8, 0.0, 0.0 I/flutter (32494) : │ [2] of 0.0, 0.0, 1.0, 0.0 I/flutter (32494) : │ [3] of 0.0, 0.0, 0.0, 1.0 I/flutter (32494) : │ I/flutter (32494) : ├ ─ child 1: OffsetLayer#fc176I/ FLUTTER (32494): │ creator: RepaintBoundary ← _FocusMarker ← Semantics ← FocusScope I/flutter (32494): │ │ ← PageStorage ← Offstage ← _ModalScopeStatus ← I/flutter (32494): [LabeledGlobalKey │ │ _ModalScope < dynamic > - < _ModalScopeState < dynamic > ># 43140]I/ FLUTTER (32494): │ ← overlayentry -[LabeledGlobalKey<_OverlayEntryState># 46 f19] please│ │ Overlay-[LabeledGlobalKey<OverlayState># af6f4] please..I/flutter (32494) : │ │ offset: offset (0.0, 0.0) I/flutter (32494) : │ │ I/flutter (32494) : │ └ ─ child 1: OffsetLayer#b6e14I/ FLUTTER (32494): │ creator: RepaintBoundary-[GlobalKey]# 0 ce90] please IgnorePointer pleaseI/flutter (32494): │ │ FadeTransition ← FractionalTranslation ← SlideTransition ← I/flutter (32494): │ │ _FadeUpwardsPageTransition please AnimatedBuilder please RepaintBoundary I/flutter (32494) : │ │ ← _FocusMarker ← Semantics ← FocusScope ← PageStorage ←... I/flutter (32494): │ │ offset: ├ ─ imp. 1, ├ ─ imp. 1, ├ ─ imp. 2, ├ ─ imp. 2, ├ ─ imp. 2, ├ ─ imp. 2, ├ ─ imp. 2, ├ ─ imp. 2, ├ ─ imp#4fdc6I/ FLUTTER (32494): │ creator: PhysicalModel ← AnimatedPhysicalModel ← Material ← I/flutter (32494): │ │ scaffoldscope ← SCAFFoldScope ← I/ SCAFFoldController (32494): │ │ ClipDemoPage ← Builder ← I/flutter (32494): │ RepaintBoundary-[GlobalKey]# 0 ce90] please IgnorePointer pleaseI/flutter (32494): │ │ I/flutter (32494): │ │ elevation: 0 I/flutter (32494): │ │ color: Color(0xffFafafa) I/flutter (32494): │ ├─ I/ Flutter (32494): │ ├─ Child 1: PictureLayer#6ee26│ ├ ─ paint bounds: I/flutter (0 folders, 1 folders, 2 folders): I/flutter (0 folders, 2 folders, 2 folders): I/flutter (0 folders, 2 folders): I/flutter (0 folders, 2 folders): Child 2: │ ├ ─ AnnotatedRegionLayer < SystemUiOverlayStyle >#cbeafI/flutter (32494) : │ │ │ value: {I/flutter systemNavigationBarColor: 4278190080, (32494) : │ │ │ systemNavigationBarDividerColor: null, statusBarColor: null, I/flutter (32494) : │ │ │ statusBarBrightness: Brightness. Dark, statusBarIconBrightness: I/flutter (32494) : │ │ │ Brightness, light, systemNavigationBarIconBrightness: I/flutter (32494): │ │ Brightness. Light} I/flutter (32494): │ │ size: size (32494) │ │ ├ ─ imp. 2, ├ ─ imp. 1, ├ ─ imp. 2, ├ ─ imp. 2, ├ ─ imp. 2, ├ ─ imp. 2, ├ ─ imp#edb15I/ FLUTTER (32494): │ │ Creator: PhysicalModel ← AnimatedPhysicalModel ← Material ← I/flutter (32494): │ │ ├ ─ AnnotatedRegion<SystemUiOverlayStyle> ← Semantics ← AppBar ← I/flutter (32494): │ │ FlexibleSpaceBarSettings ← ConstrainedBox ← MediaQuery ← I/flutter (32494): │ │ │ LayoutId-[<_ScaffoldSlot. AppBar >] ← CustomMultiChildLayout ← I/flutter (32494): │ │ │ AnimatedBuilder please... I/flutter (32494) : │ │ │ elevation: 4.0 I/flutter (32494) : │ │ │ color: MaterialColor (primary value: Color (0 xff2196f3)) I/flutter (32494) : │ │ │ I/flutter (32494) : │ │ └ ─ child 1: PictureLayer#418ce│ ├ ─ paint bounds: I/flutter (0 folders, 1 folders, 2 folders): I/flutter (0 folders, 2 folders, 2 folders): I/flutter (0 folders, 2 folders): I/flutter (0 folders, 2 folders): │ └ ─ child 3: TransformLayer#7f867│ │ folders: I/flutter (0 folders, 0 folders): I/flutter (0 folders, 2 folders): I/flutter (0 folders, 2 folders): I/flutter (0 folders, 2 folders): I/flutter (0 folders, 2 folders): I/flutter (0 folders, 2 folders): │ │ [0] 1.0, 0.0, 0.0, 0.0 I/flutter (32494) : │ │ [1] - 0.0, 1.0, 0.0, 0.0 I/flutter (32494) : │ │ [2] 0.0, 0.0, 1.0, 0.0 I/flutter (32494) : │ │ [3] of 0.0, 0.0, 0.0, 1.0 I/flutter (32494) : │ │ I/flutter (32494) : │ └ ─ child 1: PhysicalModelLayer#9f36bI/ FLUTTER (32494): │ creator: PhysicalShape ← _MaterialInterior ← Material ← I/flutter (32494): │ │ ConstrainedBox ← _FocusMarker ← Focus ← _InputPadding ← I/flutter (32494): │ │ Semantics ← RawMaterialButton ← KeyedSubtree-[GlobalKey#9ead9]I/flutter (32494): │ │ I/flutter (32494): │ │ elevation: 0 I/flutter (32494): │ │ color: │ ├ ─ imp. 1, ├ ─ imp. 2, ├ ─ imp. 2, ├ ─ imp. 2, ├ ─ imp. 3, ├ ─ imp#2a0742. I/flutter (32494): │ paint Bounds: I/flutter (32494): 2. Child 2: └ ─ PictureLayer#3d42dPaint Bounds: Rect.fromLTRB(0.0, 0.0, 1080.0, 2030.0) I/flutter (32494):Copy the code

So you can see that the widgets in a Flutter eventually form a variety of layers, each with its own regions and functions. For example, AnnotatedRegionLayer handles the color change of the status bar on a new page. These layers are eventually converted into EngineLayer through SceneBuilder, and finally submitted for Scene to be drawn by Engine.

To sum up: Before the Layer of the Flutter Framework can be drawn, it needs to be processed by SceneBuinlder to get an EngineLayer. The Layer in the Flutter Framework can be interpreted as the object encapsulation of “SceneBuinlder”, while “EngineLayer” is the real EngineLayer. The resulting Scene will be submitted to the Engine to draw.

From then on, canto XXI is finally over! (/ / / del / / /)

Resources to recommend

  • Making: github.com/CarGuo
  • Open Source Flutter complete project:Github.com/CarGuo/GSYG…
  • Open Source Flutter Multi-case learning project:Github.com/CarGuo/GSYF…
  • Open Source Fluttre Combat Ebook Project:Github.com/CarGuo/GSYF…
  • Open Source React Native project: github.com/CarGuo/GSYG…