作为系列文章的第二十一篇,本篇将通过不一样的角度来介绍 Flutter Framework 的整体渲染原理,深入剖析 Flutter 中构成 Layer 后的绘制流程,让开发者对 Flutter 的渲染原理和实现逻辑有更清晰的认知。

文章汇总地址:

Flutter 完整实战实战系列文章专栏

Flutter 番外的世界系列文章专栏

一、Layer 相关的回顾

先回顾下,我们知道在 Flutter 中的控件会经历 Widget -> Element -> RenderObject -> Layer 这样的变化过程,而其中 Layer 的组成由 RenderObject 中的 isRepaintBoundary 标志位决定。

当调用 setState 时,RenderObject 就会往上的父节点去查找,根据 isRepaintBoundary是否为 true,会决定是否从这里开始往下去触发重绘,换个说法就是:确定要更新哪些区域

比如 Navigator 跳转不同路由页面,每个页面内部就有一个 RepaintBoundary 控件,这个控件对应的 RenderRepaintBoundary 内的 isRepaintBoundary 标记位就是为 true ,从而路由页面之间形成了独立的 Layer

所以相关的 RenderObject 在一起组成了 Layer,而由 Layer 构成的 Layer Tree 最后会被提交到 Flutter Engine 绘制出画面

Layer 是怎么工作的?它的本质又是什么? Flutter Framework 中 Layer 是如何被提交到 Engine 中?

二、Flutter Framework 中的绘制

带着前面 Layer 的问题,我们先做个假设:如果抛开 Flutter Framework 中封装好的控件,我们应该如何绘制出一个画面?或者说如何创建一个 Layer

举个例子,如下代码所示,运行后可以看到一个居中显示的 100 x 100 的蓝色方块,并且代码里没有用到任何 WidgetRenderObject 甚至 Layer,而是使用了 PictureRecorderCanvasSceneBuilder 这些相对陌生的对象完成了画面绘制,并且在最后执行的是 window.render

  1. import 'dart:ui' as ui;
  2. void main() {
  3. ui.window.onBeginFrame = beginFrame;
  4. ui.window.scheduleFrame();
  5. }
  6. void beginFrame(Duration timeStamp) {
  7. final double devicePixelRatio = ui.window.devicePixelRatio;
  8. ///创建一个画板
  9. final ui.PictureRecorder recorder = ui.PictureRecorder();
  10. ///基于画板创建一个 Canvas
  11. final ui.Canvas canvas = ui.Canvas(recorder);
  12. canvas.scale(devicePixelRatio, devicePixelRatio);
  13. var centerX = ui.window.physicalSize.width / 2.0;
  14. var centerY = ui.window.physicalSize.height / 2.0;
  15. ///画一个 100 的剧中蓝色
  16. canvas.drawRect(
  17. Rect.fromCenter(
  18. center: Offset.zero,
  19. width: 100,
  20. height: 100),
  21. new Paint()..color = Colors.blue);
  22. ///结束绘制
  23. final ui.Picture picture = recorder.endRecording();
  24. final ui.SceneBuilder sceneBuilder = ui.SceneBuilder()
  25. ..pushOffset(centerX, centerY)
  26. ..addPicture(ui.Offset.zero, picture)
  27. ..pop();
  28. ui.window.render(sceneBuilder.build());
  29. }

因为在 Flutter 中 Canvas 的创建是必须有 PictureRecorder ,而 PictureRecorder 顾名思义就是创建一个图片用于记录绘制,所以在上述代码中:

  • 先是创建了 PictureRecorder
  • 然后使用 PictureRecorder 创建了 Canvas
  • 之后使用 Canvas 绘制蓝色小方块;
  • 结束绘制后通过 SceneBuilderpushOffsetaddPicture 加载了绘制的内容;
  • 通过 window.render 绘制出画面。

需要注意⚠️: render 方法被限制必须在 onBeginFrameonDrawFrame 中调用,所以上方代码才会有 window.onBeginFrame = beginFrame;。在官方的examples/layers/raw/ 下有不少类似的例子。

二十一、 Flutter 画面渲染的全面解析 - 图1

可以看到 Flutter Framework 在底层绘制的最后一步是 window.render ,而如下代码所示: render 方法需要的参数是 Scene 对象,并且 render 方法是一个 native 方法,说明 Flutter Framework 最终提交给 Engine 的是一个 Scene

  1. void render(Scene scene) native 'Window_render';

Scene 又是什么?前面所说的 Layer 又在哪里呢?它们之间又有什么样的关系?

三、Scene 和 Layer 之间的苟且

在 Flutter 中 Scene 其实是一个 Native 对象,它对应的其实是 Engine 中的 scene.cc 结构,而 Engine 中的 scene.cc 内包含了一个 layer_tree_ 用于绘制,所以首先可以知道SceneEngine 是和 layer_tree_ 有关系

然后就是在 Flutter Framework 中 Scene 只能通过 SceneBuilder 构建,而 SceneBuilder 中存在很多方法比如: pushOffsetpushClipRectpushOpacity 等,这些方法的执行后,可以通过 Engine 会创建出一个对应的 EngineLayer

  1. OffsetEngineLayer pushOffset(double dx, double dy, { OffsetEngineLayer oldLayer }) {
  2. assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushOffset'));
  3. final OffsetEngineLayer layer = OffsetEngineLayer._(_pushOffset(dx, dy));
  4. assert(_debugPushLayer(layer));
  5. return layer;
  6. }
  7. EngineLayer _pushOffset(double dx, double dy) native 'SceneBuilder_pushOffset';

所以 SceneBuilderbuildScene 之前,可以通过 push 等相关方法产生 EngineLayer, 比如前面的蓝色小方块例子,SceneBuilder 就是通过 pushOffset 创建出对应的图层偏移。

接着看 Flutter Framework 中的 Layer ,如下代码所示,在 Layer 默认就存在 EngineLayer 参数,所以可以得知 Layer 肯定和 SceneBuilder 有一定关系。

  1. @protected
  2. ui.EngineLayer get engineLayer => _engineLayer;
  3. @protected
  4. set engineLayer(ui.EngineLayer value) {
  5. _engineLayer = value;
  6. if (!alwaysNeedsAddToScene) {
  7. if (parent != null && !parent.alwaysNeedsAddToScene) {
  8. parent.markNeedsAddToScene();
  9. }
  10. }
  11. }
  12. ui.EngineLayer _engineLayer;
  13. /// Override this method to upload this layer to the engine.
  14. ///
  15. /// Return the engine layer for retained rendering. When there no
  16. /// corresponding engine layer, null is returned.
  17. @protected
  18. void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]);

其次在 Layer 中有一个关键方法: addToScene,先通过注释可以得知这个方法是由子类实现,并且执行后可以得到一个 EngineLayer ,并且这个方法需要一个 SceneBuilder ,而查询该方法的实现恰好就有OffsetLayerPictureLayer 等。

二十一、 Flutter 画面渲染的全面解析 - 图2

所以如下代码所示,在 OffsetLayerPictureLayeraddToScene 方法实现中可以看到:

  • PictureLayer 调用了 SceneBuilderaddPicture;
  • OffsetLayer 调用了 SceneBuilderpushOffset
  1. class PictureLayer extends Layer {
  2. ···
  3. @override
  4. void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
  5. builder.addPicture(layerOffset, picture, isComplexHint: isComplexHint, willChangeHint: willChangeHint);
  6. }
  7. ···
  8. }
  9. class OffsetLayer extends ContainerLayer {
  10. ···
  11. OffsetLayer({ Offset offset = Offset.zero }) : _offset = offset;
  12. @override
  13. void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
  14. engineLayer = builder.pushOffset(
  15. layerOffset.dx + offset.dx,
  16. layerOffset.dy + offset.dy,
  17. oldLayer: _engineLayer as ui.OffsetEngineLayer,
  18. );
  19. addChildrenToScene(builder);
  20. builder.pop();
  21. }
  22. ···
  23. }

所以到这里 SceneBuilderLayer 通过 EngineLayeraddToScene 方法成功关联起来,而 window.render 提交的 Scene 又是通过 SceneBuilder 构建得到,所以如下图所示, LayerScene 就这样“苟且”到了一起

二十一、 Flutter 画面渲染的全面解析 - 图3

对面前面的蓝色小方块代码,如下代码所示,这里修改为使用 Layer 的方式实现,可以看到这样的实现更接近 Flutter Framework 的实现:通过 rootLayer 一级一级 append 构建出Layer 树,而 rootLayer 调用 addToScene 方法后,因为会执行 addChildrenToScene 方法,从而往下执行 child LayeraddToScene

  1. import 'dart:ui' as ui;
  2. void main() {
  3. ui.window.onBeginFrame = beginFrame;
  4. ui.window.scheduleFrame();
  5. }
  6. void beginFrame(Duration timeStamp) {
  7. final double devicePixelRatio = ui.window.devicePixelRatio;
  8. ///创建一个画板
  9. final ui.PictureRecorder recorder = ui.PictureRecorder();
  10. ///基于画板创建一个 Canvas
  11. final ui.Canvas canvas = ui.Canvas(recorder);
  12. canvas.scale(devicePixelRatio, devicePixelRatio);
  13. var centerX = ui.window.physicalSize.width / 2.0;
  14. var centerY = ui.window.physicalSize.height / 2.0;
  15. ///画一个 100 的剧中蓝色
  16. canvas.drawRect(Rect.fromCenter(center: Offset.zero, width: 100, height: 100),
  17. new Paint()..color = Colors.blue);
  18. final ui.SceneBuilder sceneBuilder = ui.SceneBuilder();
  19. OffsetLayer rootLayer = new OffsetLayer();
  20. OffsetLayer offsetLayer = new OffsetLayer(offset: Offset(centerX, centerY));
  21. rootLayer.append(offsetLayer);
  22. PictureLayer pictureLayer = new PictureLayer(Rect.zero);
  23. pictureLayer.picture = recorder.endRecording();
  24. offsetLayer.append(pictureLayer);
  25. rootLayer.addToScene(sceneBuilder);
  26. ui.window.render(sceneBuilder.build());
  27. }

四、Layer 的品种

这里额外介绍下 Flutter 中常见的 Layer,如下图所示,一般 Flutter 中 Layer 可以分为 ContainerLayer 和非 ContainerLayer

二十一、 Flutter 画面渲染的全面解析 - 图4

ContainerLayer 是可以具备子节点,也就是带有 append 方法,大致可以分为:

  • 位移类(OffsetLayer/TransformLayer);
  • 透明类(OpacityLayer
  • 裁剪类(ClipRectLayer/ClipRRectLayer/ClipPathLayer);
  • 阴影类 (PhysicalModelLayer)

为什么这些 Layer 需要是 ContainerLayer因为这些 Layer 都是一些像素合成的操作,其本身是不具备“描绘”控件的能力,就如前面的蓝色小方块例子一样,如果要呈现画面一般需要和 PictureLayer 结合

比如 ClipRRect 控件的 RenderClipRRect 内部,在 pushClipRRect 时可以会创建 ClipRRectLayer ,而新创建的 ClipRRectLayer 会通过 appendLayer 方法触发 append 操作添加为父 Layer 的子节点。

而非 ContainerLayer 一般不具备子节点,比如:

  • PictureLayer 是用于绘制画面,Flutter 上的控件基本是绘制在这上面;
  • TextureLayer 是用于外界纹理,比如视频播放或者摄像头数据;
  • PlatformViewLayer 是用于 iOS 上 PlatformView 相关嵌入纹理的使用;

举个例子,控件绘制时的 Canvas 来源于 PaintingContext , 而如下代码所示 PaintingContext 通过 _repaintCompositedChild 执行绘制后得到的 Picture 最后就是提交给所在的 PictureLayer.picture

  1. void stopRecordingIfNeeded() {
  2. if (!_isRecording)
  3. return;
  4. _currentLayer.picture = _recorder.endRecording();
  5. _currentLayer = null;
  6. _recorder = null;
  7. _canvas = null;
  8. }

五、Layer 的内外兼修

了解完 Layer 是如何提交绘制后,接下来介绍的就是 Layer 的刷新和复用。

我们知道当 RenderObjectisRepaintBoundaryture 时,Flutter Framework 就会自动创建一个 OffsetLayer 来“承载”这片区域,而 Layer 内部的画面更新一般不会影响到其他 Layer

Layer 是如何更新?这就涉及了 Layer 内部的 markNeedsAddToSceneupdateSubtreeNeedsAddToScene 这两个方法。

如下代码所示,markNeedsAddToScene 方法其实就是把 Layer 内的 _needsAddToScene 标记为 true ; 而 updateSubtreeNeedsAddToScene 方法就是遍历所有 child Layer,通过递归调用 updateSubtreeNeedsAddToScene() 判断是否有 child 需要 _needsAddToScene ,如果是那就把自己也标记为 true

  1. @protected
  2. @visibleForTesting
  3. void markNeedsAddToScene() {
  4. // Already marked. Short-circuit.
  5. if (_needsAddToScene) {
  6. return;
  7. }
  8. _needsAddToScene = true;
  9. }
  10. @override
  11. void updateSubtreeNeedsAddToScene() {
  12. super.updateSubtreeNeedsAddToScene();
  13. Layer child = firstChild;
  14. while (child != null) {
  15. child.updateSubtreeNeedsAddToScene();
  16. _needsAddToScene = _needsAddToScene || child._needsAddToScene;
  17. child = child.nextSibling;
  18. }
  19. }

是不是和 setState 调用 markNeedsBuild 把自己标志为 _dirty 很像?_needsAddToScene 等于 true 时,对应 LayeraddToScene 才会被调用;而当 Layer_needsAddToScenefalse_engineLayer 不为空时就触发 Layer 的复用

  1. void _addToSceneWithRetainedRendering(ui.SceneBuilder builder) {
  2. if (!_needsAddToScene && _engineLayer != null) {
  3. builder.addRetained(_engineLayer);
  4. return;
  5. }
  6. addToScene(builder);
  7. _needsAddToScene = false;
  8. }

是的,当一个 Layer_needsAddToScenefalse 时 表明了自己不需要更新,那这个 LayerEngineLayer 又存在,那 就可以被复用。举个例子:当一个新的页面打开时,底部的页面并没有发生变化时,它只是参与画面的合成,所以对于底部页面来说它 “Layer” 是可以直接被复用参与绘制。

markNeedsAddToScene 在什么时候会被调用?

如下图所示,当 Layer 子的参数,比如: PictureLayerpictureOffsetLayeroffset 发生变化时,Layer 就会主动调用 markNeedsAddToScene 标记自己为“脏”区域。另外当 LayerengineLayer 发生变化时,就会尝试触发父节点的 Layer 调用 markNeedsAddToScene ,这样父节点也会对应产生变化。

二十一、 Flutter 画面渲染的全面解析 - 图5

  1. @protected
  2. set engineLayer(ui.EngineLayer value) {
  3. _engineLayer = value;
  4. if (!alwaysNeedsAddToScene) {
  5. if (parent != null && !parent.alwaysNeedsAddToScene) {
  6. parent.markNeedsAddToScene();
  7. }
  8. }
  9. }

updateSubtreeNeedsAddToScene 是在 buildScene 的时候触发,在 addToScene 之前调用 updateSubtreeNeedsAddToScene 再次判断 child 节点,从而确定是否需要发生改变。

  1. ui.Scene buildScene(ui.SceneBuilder builder) {
  2. List<PictureLayer> temporaryLayers;
  3. assert(() {
  4. if (debugCheckElevationsEnabled) {
  5. temporaryLayers = _debugCheckElevations();
  6. }
  7. return true;
  8. }());
  9. updateSubtreeNeedsAddToScene();
  10. addToScene(builder);
  11. _needsAddToScene = false;
  12. final ui.Scene scene = builder.build();
  13. return scene;
  14. }

六、Flutter Framework 的 Layer 构成

最后回归到 Flutter Framework ,在 Flutter Framework 中 _window.render 是在 RenderViewcompositeFrame 方法中被调用;而 RenderView 是在RendererBindinginitRenderView 被初始化;initRenderView 是在 initInstances 时被调用,也就是 runApp 的时候。

简单来说就是:runApp 的时候创建了 RenderView ,并且 RenderView 内部的 compositeFrame 就是通过 _window.render来提交 Layer 的绘制。

  1. void compositeFrame() {
  2. Timeline.startSync('Compositing', arguments: timelineWhitelistArguments);
  3. try {
  4. final ui.SceneBuilder builder = ui.SceneBuilder();
  5. final ui.Scene scene = layer.buildScene(builder);
  6. if (automaticSystemUiAdjustment)
  7. _updateSystemChrome();
  8. _window.render(scene);
  9. scene.dispose();
  10. assert(() {
  11. if (debugRepaintRainbowEnabled || debugRepaintTextRainbowEnabled)
  12. debugCurrentRepaintColor = debugCurrentRepaintColor.withHue((debugCurrentRepaintColor.hue + 2.0) % 360.0);
  13. return true;
  14. }());
  15. } finally {
  16. Timeline.finishSync();
  17. }
  18. }

所以 runApp 的时候 Flutter 创建了 RenderView,并且在 WindowdrawFrame 方法中调用了 renderView.compositeFrame(); 提交了绘制,而 RenderView 作为根节点,它携带的 rootLayerOffsetLayer 的子类 TransformLayer,属于是 Flutter 中 Layer 的根节点

二十一、 Flutter 画面渲染的全面解析 - 图6

这里举个例子,如下图所示是一个简单的不规范代码,运行后出现的结果是一个黑色空白页面,这里我们通过 debugDumpLayerTree 方法打印出 Layer 的机构。

  1. void main() => runApp(MyApp());
  2. class MyApp extends StatelessWidget {
  3. // This widget is the root of your application.
  4. @override
  5. Widget build(BuildContext context) {
  6. new Future.delayed(Duration(seconds: 1), () {
  7. debugDumpLayerTree();
  8. });
  9. return MaterialApp(
  10. title: 'GSY Flutter Demo',
  11. theme: ThemeData(
  12. primarySwatch: Colors.blue,
  13. ),
  14. home: Container(),
  15. //routes: routers,
  16. );
  17. }
  18. }

打印出的结果如下 LOG 所示,正如前面所说 TransformLayer 作为 rooterLayer 它的 ownerRenderView,然后它有两个 child 节点: child1 OffsetLayer 和 child2 PictureLayer

默认情况下因为 Layer 的形成机制(isRepaintBoundaryture 自动创建一个 OffsetLayer)和 Canvas 绘制需要,至少会有一个 OffsetLayerPictureLayer

  1. I/flutter (32494): TransformLayer#f8fa5
  2. I/flutter (32494): owner: RenderView#2d51e
  3. I/flutter (32494): creator: [root]
  4. I/flutter (32494): offset: Offset(0.0, 0.0)
  5. I/flutter (32494): transform:
  6. I/flutter (32494): [0] 2.8,0.0,0.0,0.0
  7. I/flutter (32494): [1] 0.0,2.8,0.0,0.0
  8. I/flutter (32494): [2] 0.0,0.0,1.0,0.0
  9. I/flutter (32494): [3] 0.0,0.0,0.0,1.0
  10. I/flutter (32494):
  11. I/flutter (32494): ├─child 1: OffsetLayer#4503b
  12. I/flutter (32494): creator: RepaintBoundary _FocusMarker Semantics FocusScope
  13. I/flutter (32494): PageStorage Offstage _ModalScopeStatus
  14. I/flutter (32494): _ModalScope<dynamic>-[LabeledGlobalKey<_ModalScopeState<dynamic>>#e1be1]
  15. I/flutter (32494): _OverlayEntry-[LabeledGlobalKey<_OverlayEntryState>#95107]
  16. I/flutter (32494): Stack _Theatre
  17. I/flutter (32494): Overlay-[LabeledGlobalKey<OverlayState>#ceb36]
  18. I/flutter (32494): offset: Offset(0.0, 0.0)
  19. I/flutter (32494):
  20. I/flutter (32494): └─child 1: OffsetLayer#e8309
  21. I/flutter (32494): creator: RepaintBoundary-[GlobalKey#bbad8] ← IgnorePointer ←
  22. I/flutter (32494): FadeTransition FractionalTranslation SlideTransition
  23. I/flutter (32494): _FadeUpwardsPageTransition AnimatedBuilder RepaintBoundary
  24. I/flutter (32494): _FocusMarker Semantics FocusScope PageStorage
  25. I/flutter (32494): offset: Offset(0.0, 0.0)
  26. I/flutter (32494):
  27. I/flutter (32494): └─child 2: PictureLayer#be4f1
  28. I/flutter (32494): paint bounds: Rect.fromLTRB(0.0, 0.0, 1080.0, 2030.0)

根据上述 LOG 所示,首先看:

  • OffsetLayercreatorRepaintBoundary,而其来源是 Overlay,我们知道 Flutter 中可以通过 Overlay 做全局悬浮控件,而 Overlay 就是在 MaterialAppNavigator 中创建,并且它是一个独立的Layer
  • OffsetLayer 的 child 是 PageStoragePageStorage 是通过 Route 产生的,也即是默认的路由第一个页面。

所以现在知道为什么 Overlay 可以在 MaterialApp 的所有路由页面下全局悬浮显示了吧。

如下代码所示,再原本代码的基础上增加 Scaffold 后继续执行 debugDumpLayerTree

  1. void main() => runApp(MyApp());
  2. class MyApp extends StatelessWidget {
  3. // This widget is the root of your application.
  4. @override
  5. Widget build(BuildContext context) {
  6. new Future.delayed(Duration(seconds: 1), () {
  7. debugDumpLayerTree();
  8. });
  9. return MaterialApp(
  10. title: 'GSY Flutter Demo',
  11. theme: ThemeData(
  12. primarySwatch: Colors.blue,
  13. ),
  14. home: Scaffold(
  15. body: Container(),
  16. ),
  17. //routes: routers,
  18. );
  19. }
  20. }

可以看到这里多了一个 PhysicalModelLayerPictureLayerPhysicalModelLayer 是用于设置阴影等效果的,比如关闭 debugDisablePhysicalShapeLayersAppBar 的阴影会消失,而之后的 PictureLayer 也是用于绘制。

  1. I/flutter (32494): TransformLayer#ac14b
  2. I/flutter (32494): owner: RenderView#f5ecc
  3. I/flutter (32494): creator: [root]
  4. I/flutter (32494): offset: Offset(0.0, 0.0)
  5. I/flutter (32494): transform:
  6. I/flutter (32494): [0] 2.8,0.0,0.0,0.0
  7. I/flutter (32494): [1] 0.0,2.8,0.0,0.0
  8. I/flutter (32494): [2] 0.0,0.0,1.0,0.0
  9. I/flutter (32494): [3] 0.0,0.0,0.0,1.0
  10. I/flutter (32494):
  11. I/flutter (32494): ├─child 1: OffsetLayer#c0128
  12. I/flutter (32494): creator: RepaintBoundary _FocusMarker Semantics FocusScope
  13. I/flutter (32494): PageStorage Offstage _ModalScopeStatus
  14. I/flutter (32494): _ModalScope<dynamic>-[LabeledGlobalKey<_ModalScopeState<dynamic>>#fe143]
  15. I/flutter (32494): _OverlayEntry-[LabeledGlobalKey<_OverlayEntryState>#9cb60]
  16. I/flutter (32494): Stack _Theatre
  17. I/flutter (32494): Overlay-[LabeledGlobalKey<OverlayState>#ee455]
  18. I/flutter (32494): offset: Offset(0.0, 0.0)
  19. I/flutter (32494):
  20. I/flutter (32494): └─child 1: OffsetLayer#fb2a6
  21. I/flutter (32494): creator: RepaintBoundary-[GlobalKey#fd46b] ← IgnorePointer ←
  22. I/flutter (32494): FadeTransition FractionalTranslation SlideTransition
  23. I/flutter (32494): _FadeUpwardsPageTransition AnimatedBuilder RepaintBoundary
  24. I/flutter (32494): _FocusMarker Semantics FocusScope PageStorage
  25. I/flutter (32494): offset: Offset(0.0, 0.0)
  26. I/flutter (32494):
  27. I/flutter (32494): └─child 1: PhysicalModelLayer#f1460
  28. I/flutter (32494): creator: PhysicalModel AnimatedPhysicalModel Material
  29. I/flutter (32494): PrimaryScrollController _ScaffoldScope Scaffold Semantics
  30. I/flutter (32494): Builder RepaintBoundary-[GlobalKey#fd46b] ← IgnorePointer ←
  31. I/flutter (32494): FadeTransition FractionalTranslation
  32. I/flutter (32494): elevation: 0.0
  33. I/flutter (32494): color: Color(0xfffafafa)
  34. I/flutter (32494):
  35. I/flutter (32494): └─child 1: PictureLayer#f800f
  36. I/flutter (32494): paint bounds: Rect.fromLTRB(0.0, 0.0, 392.7, 738.2)
  37. I/flutter (32494):
  38. I/flutter (32494): └─child 2: PictureLayer#af14d
  39. I/flutter (32494): paint bounds: Rect.fromLTRB(0.0, 0.0, 1080.0, 2030.0)
  40. I/flutter (32494):

最后通过再使用 Navigator 跳到另外一个页面,再新页面打印 Layer 树,可以看到又可以多了个 PictureLayerAnnotatedRegionLayerTransformLayer : 其中多了的 AnnotatedRegionLayer 是用于处理新页面顶部状态栏的显示效果。

  1. I/flutter (32494): TransformLayer#12e21
  2. I/flutter (32494): owner: RenderView#aa5c7
  3. I/flutter (32494): creator: [root]
  4. I/flutter (32494): offset: Offset(0.0, 0.0)
  5. I/flutter (32494): transform:
  6. I/flutter (32494): [0] 2.8,0.0,0.0,0.0
  7. I/flutter (32494): [1] 0.0,2.8,0.0,0.0
  8. I/flutter (32494): [2] 0.0,0.0,1.0,0.0
  9. I/flutter (32494): [3] 0.0,0.0,0.0,1.0
  10. I/flutter (32494):
  11. I/flutter (32494): ├─child 1: OffsetLayer#fc176
  12. I/flutter (32494): creator: RepaintBoundary _FocusMarker Semantics FocusScope
  13. I/flutter (32494): PageStorage Offstage _ModalScopeStatus
  14. I/flutter (32494): _ModalScope<dynamic>-[LabeledGlobalKey<_ModalScopeState<dynamic>>#43140]
  15. I/flutter (32494): _OverlayEntry-[LabeledGlobalKey<_OverlayEntryState>#46f19]
  16. I/flutter (32494): Stack _Theatre
  17. I/flutter (32494): Overlay-[LabeledGlobalKey<OverlayState>#af6f4]
  18. I/flutter (32494): offset: Offset(0.0, 0.0)
  19. I/flutter (32494):
  20. I/flutter (32494): └─child 1: OffsetLayer#b6e14
  21. I/flutter (32494): creator: RepaintBoundary-[GlobalKey#0ce90] ← IgnorePointer ←
  22. I/flutter (32494): FadeTransition FractionalTranslation SlideTransition
  23. I/flutter (32494): _FadeUpwardsPageTransition AnimatedBuilder RepaintBoundary
  24. I/flutter (32494): _FocusMarker Semantics FocusScope PageStorage
  25. I/flutter (32494): offset: Offset(0.0, 0.0)
  26. I/flutter (32494):
  27. I/flutter (32494): └─child 1: PhysicalModelLayer#4fdc6
  28. I/flutter (32494): creator: PhysicalModel AnimatedPhysicalModel Material
  29. I/flutter (32494): PrimaryScrollController _ScaffoldScope Scaffold
  30. I/flutter (32494): ClipDemoPage Semantics Builder
  31. I/flutter (32494): RepaintBoundary-[GlobalKey#0ce90] ← IgnorePointer ←
  32. I/flutter (32494): FadeTransition
  33. I/flutter (32494): elevation: 0.0
  34. I/flutter (32494): color: Color(0xfffafafa)
  35. I/flutter (32494):
  36. I/flutter (32494): ├─child 1: PictureLayer#6ee26
  37. I/flutter (32494): paint bounds: Rect.fromLTRB(0.0, 0.0, 392.7, 738.2)
  38. I/flutter (32494):
  39. I/flutter (32494): ├─child 2: AnnotatedRegionLayer<SystemUiOverlayStyle>#cbeaf
  40. I/flutter (32494): value: {systemNavigationBarColor: 4278190080,
  41. I/flutter (32494): systemNavigationBarDividerColor: null, statusBarColor: null,
  42. I/flutter (32494): statusBarBrightness: Brightness.dark, statusBarIconBrightness:
  43. I/flutter (32494): Brightness.light, systemNavigationBarIconBrightness:
  44. I/flutter (32494): Brightness.light}
  45. I/flutter (32494): size: Size(392.7, 83.6)
  46. I/flutter (32494): offset: Offset(0.0, 0.0)
  47. I/flutter (32494):
  48. I/flutter (32494): └─child 1: PhysicalModelLayer#edb15
  49. I/flutter (32494): creator: PhysicalModel AnimatedPhysicalModel Material
  50. I/flutter (32494): AnnotatedRegion<SystemUiOverlayStyle> Semantics AppBar
  51. I/flutter (32494): FlexibleSpaceBarSettings ConstrainedBox MediaQuery
  52. I/flutter (32494): LayoutId-[<_ScaffoldSlot.appBar>] CustomMultiChildLayout
  53. I/flutter (32494): AnimatedBuilder
  54. I/flutter (32494): elevation: 4.0
  55. I/flutter (32494): color: MaterialColor(primary value: Color(0xff2196f3))
  56. I/flutter (32494):
  57. I/flutter (32494): └─child 1: PictureLayer#418ce
  58. I/flutter (32494): paint bounds: Rect.fromLTRB(0.0, 0.0, 392.7, 83.6)
  59. I/flutter (32494):
  60. I/flutter (32494): └─child 3: TransformLayer#7f867
  61. I/flutter (32494): offset: Offset(0.0, 0.0)
  62. I/flutter (32494): transform:
  63. I/flutter (32494): [0] 1.0,0.0,0.0,-0.0
  64. I/flutter (32494): [1] -0.0,1.0,0.0,0.0
  65. I/flutter (32494): [2] 0.0,0.0,1.0,0.0
  66. I/flutter (32494): [3] 0.0,0.0,0.0,1.0
  67. I/flutter (32494):
  68. I/flutter (32494): └─child 1: PhysicalModelLayer#9f36b
  69. I/flutter (32494): creator: PhysicalShape _MaterialInterior Material
  70. I/flutter (32494): ConstrainedBox _FocusMarker Focus _InputPadding
  71. I/flutter (32494): Semantics RawMaterialButton KeyedSubtree-[GlobalKey#9ead9]
  72. I/flutter (32494): TickerMode Offstage
  73. I/flutter (32494): elevation: 6.0
  74. I/flutter (32494): color: Color(0xff2196f3)
  75. I/flutter (32494):
  76. I/flutter (32494): └─child 1: PictureLayer#2a074
  77. I/flutter (32494): paint bounds: Rect.fromLTRB(320.7, 666.2, 376.7, 722.2)
  78. I/flutter (32494):
  79. I/flutter (32494): └─child 2: PictureLayer#3d42d
  80. I/flutter (32494): paint bounds: Rect.fromLTRB(0.0, 0.0, 1080.0, 2030.0)
  81. I/flutter (32494):

所以可以看到,Flutter 中的 Widget 在最终形成各式各样的 Layer ,每个 Layer 都有自己单独的区域和功能,比如 AnnotatedRegionLayer在新的页面处理状态栏颜色的变化,而这些 Layer 最终通过 SceneBuilder 转化为 EngineLayer ,最后提交为 Scene 经由 Engine 绘制。

最后总结一下:Flutter Framework 的 Layer 在绘制之前,需要经历 SceneBuinlder 的处理得到 EngineLayer,其实 Flutter Framework 中的 Layer 可以理解为 SceneBuinlder 的对象封装,而 EngineLayer 才是真正的 Engine 图层 ,在之后得到的 Scene 会被提交 Engine 绘制

自此,第二十一篇终于结束了!(///▽///)

资源推荐

二十一、 Flutter 画面渲染的全面解析 - 图7