您的位置 首页 > 腾讯云社区

Flutter完整开发实战详解(二十一、 Flutter 画面渲染的全面解析)---恋猫

作为系列文章的第二十一篇,本篇将通过不一样的角度来介绍 Flutter Framework 的整体渲染原理,深入剖析 Flutter 中构成 Layer 后的绘制流程,让开发者对 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 的蓝色方块,并且代码里没有用到任何 Widget 、 RenderObject 甚至 Layer,而是使用了 PictureRecorder 、Canvas 、 SceneBuilder 这些相对陌生的对象完成了画面绘制,并且在最后执行的是 window.render 。

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.Picture picture = recorder.endRecording(); final ui.SceneBuilder sceneBuilder = ui.SceneBuilder() ..pushOffset(centerX, centerY) ..addPicture(ui.Offset.zero, picture) ..pop(); ui.window.render(sceneBuilder.build()); }

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

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

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

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

void render(Scene scene) native 'Window_render';

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

三、Scene 和 Layer 之间的苟且

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

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

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';

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

接着看 Flutter Framework 中的 Layer ,如下代码所示,在 Layer 默认就存在 EngineLayer 参数,所以可以得知 Layer 肯定和 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 layer for retained rendering. When there no /// corresponding engine layer, null is returned. @protected void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]);

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

所以如下代码所示,在 OffsetLayer 和 PictureLayer 的 addToScene 方法实现中可以看到:

PictureLayer 调用了 SceneBuilder 的 addPicture; OffsetLayer 调用了 SceneBuilder 的 pushOffset ;class PictureLayer extends Layer { ··· @override void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) { builder.addPicture(layerOffset, picture, isComplexHint: isComplexHint, willChangeHint: willChangeHint); } ··· } class OffsetLayer extends ContainerLayer { ··· OffsetLayer({ 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(); } ··· }

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

image

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

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()); }四、Layer 的品种

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

image

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。

void stopRecordingIfNeeded() { if (!_isRecording) return; _currentLayer.picture = _recorder.endRecording(); _currentLayer = null; _recorder = null; _canvas = null; }五、Layer 的内外兼修

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

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

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

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

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

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

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

那 markNeedsAddToScene 在什么时候会被调用?

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

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

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

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; }六、Flutter Framework 的 Layer 构成

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

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

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((debugCurrentRepaintColor.hue + 2.0) % 360.0); return true; }()); } finally { Timeline.finishSync(); } }

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

这里举个例子,如下图所示是一个简单的不规范代码,运行后出现的结果是一个黑色空白页面,这里我们通过 debugDumpLayerTree 方法打印出 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, ); } }

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

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

I/flutter (32494): TransformLayer#f8fa5 I/flutter (32494): │ owner: RenderView#2d51e I/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] 0.0,2.8,0.0,0.0 I/flutter (32494): │ [2] 0.0,0.0,1.0,0.0 I/flutter (32494): │ [3] 0.0,0.0,0.0,1.0 I/flutter (32494): │ I/flutter (32494): ├─child 1: OffsetLayer#4503b I/flutter (32494): │ │ creator: RepaintBoundary ← _FocusMarker ← Semantics ← FocusScope I/flutter (32494): │ │ ← PageStorage ← Offstage ← _ModalScopeStatus ← I/flutter (32494): │ │ _ModalScope<dynamic>-[LabeledGlobalKey<_ModalScopeState<dynamic>>#e1be1] I/flutter (32494): │ │ ← _OverlayEntry-[LabeledGlobalKey<_OverlayEntryState>#95107] ← I/flutter (32494): │ │ Stack ← _Theatre ← I/flutter (32494): │ │ Overlay-[LabeledGlobalKey<OverlayState>#ceb36] ← ⋯ I/flutter (32494): │ │ offset: Offset(0.0, 0.0) I/flutter (32494): │ │ I/flutter (32494): │ └─child 1: OffsetLayer#e8309 I/flutter (32494): │ creator: RepaintBoundary-[GlobalKey#bbad8] ← IgnorePointer ← I/flutter (32494): │ FadeTransition ← FractionalTranslation ← SlideTransition ← I/flutter (32494): │ _FadeUpwardsPageTransition ← AnimatedBuilder ← RepaintBoundary I/flutter (32494): │ ← _FocusMarker ← Semantics ← FocusScope ← PageStorage ← ⋯ I/flutter (32494): │ offset: Offset(0.0, 0.0) I/flutter (32494): │ I/flutter (32494): └─child 2: PictureLayer#be4f1 I/flutter (32494): paint bounds: Rect.fromLTRB(0.0, 0.0, 1080.0, 2030.0)

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

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

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

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

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, ); } }

可以看到这里多了一个 PhysicalModelLayer 和 PictureLayer ,PhysicalModelLayer 是用于调试时显示调试蒙层的,比如打开蒙层后可以看到各种颜色的标注,如果不需要可以设置 debugDisablePhysicalShapeLayers,而之后的 PictureLayer 也是用于绘制。

I/flutter (32494): TransformLayer#ac14b I/flutter (32494): │ owner: RenderView#f5ecc I/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] 0.0,2.8,0.0,0.0 I/flutter (32494): │ [2] 0.0,0.0,1.0,0.0 I/flutter (32494): │ [3] 0.0,0.0,0.0,1.0 I/flutter (32494): │ I/flutter (32494): ├─child 1: OffsetLayer#c0128 I/flutter (32494): │ │ creator: RepaintBoundary ← _FocusMarker ← Semantics ← FocusScope I/flutter (32494): │ │ ← PageStorage ← Offstage ← _ModalScopeStatus ← I/flutter (32494): │ │ _ModalScope<dynamic>-[LabeledGlobalKey<_ModalScopeState<dynamic>>#fe143] I/flutter (32494): │ │ ← _OverlayEntry-[LabeledGlobalKey<_OverlayEntryState>#9cb60] ← I/flutter (32494): │ │ Stack ← _Theatre ← I/flutter (32494): │ │ Overlay-[LabeledGlobalKey<OverlayState>#ee455] ← ⋯ I/flutter (32494): │ │ offset: Offset(0.0, 0.0) I/flutter (32494): │ │ I/flutter (32494): │ └─child 1: OffsetLayer#fb2a6 I/flutter (32494): │ │ creator: RepaintBoundary-[GlobalKey#fd46b] ← IgnorePointer ← I/flutter (32494): │ │ FadeTransition ← FractionalTranslation ← SlideTransition ← I/flutter (32494): │ │ _FadeUpwardsPageTransition ← AnimatedBuilder ← RepaintBoundary I/flutter (32494): │ │ ← _FocusMarker ← Semantics ← FocusScope ← PageStorage ← ⋯ I/flutter (32494): │ │ offset: Offset(0.0, 0.0) I/flutter (32494): │ │ I/flutter (32494): │ └─child 1: PhysicalModelLayer#f1460 I/flutter (32494): │ │ creator: PhysicalModel ← AnimatedPhysicalModel ← Material ← I/flutter (32494): │ │ PrimaryScrollController ← _ScaffoldScope ← Scaffold ← Semantics I/flutter (32494): │ │ ← Builder ← RepaintBoundary-[GlobalKey#fd46b] ← IgnorePointer ← I/flutter (32494): │ │ FadeTransition ← FractionalTranslation ← ⋯ I/flutter (32494): │ │ elevation: 0.0 I/flutter (32494): │ │ color: Color(0xfffafafa) I/flutter (32494): │ │ I/flutter (32494): │ └─child 1: PictureLayer#f800f I/flutter (32494): │ paint bounds: Rect.fromLTRB(0.0, 0.0, 392.7, 738.2) I/flutter (32494): │ I/flutter (32494): └─child 2: PictureLayer#af14d I/flutter (32494): paint bounds: Rect.fromLTRB(0.0, 0.0, 1080.0, 2030.0) I/flutter (32494):

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

I/flutter (32494): TransformLayer#12e21 I/flutter (32494): │ owner: RenderView#aa5c7 I/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] 0.0,2.8,0.0,0.0 I/flutter (32494): │ [2] 0.0,0.0,1.0,0.0 I/flutter (32494): │ [3] 0.0,0.0,0.0,1.0 I/flutter (32494): │ I/flutter (32494): ├─child 1: OffsetLayer#fc176 I/flutter (32494): │ │ creator: RepaintBoundary ← _FocusMarker ← Semantics ← FocusScope I/flutter (32494): │ │ ← PageStorage ← Offstage ← _ModalScopeStatus ← I/flutter (32494): │ │ _ModalScope<dynamic>-[LabeledGlobalKey<_ModalScopeState<dynamic>>#43140] I/flutter (32494): │ │ ← _OverlayEntry-[LabeledGlobalKey<_OverlayEntryState>#46f19] ← I/flutter (32494): │ │ Stack ← _Theatre ← I/flutter (32494): │ │ Overlay-[LabeledGlobalKey<OverlayState>#af6f4] ← ⋯ I/flutter (32494): │ │ offset: Offset(0.0, 0.0) I/flutter (32494): │ │ I/flutter (32494): │ └─child 1: OffsetLayer#b6e14 I/flutter (32494): │ │ creator: RepaintBoundary-[GlobalKey#0ce90] ← IgnorePointer ← I/flutter (32494): │ │ FadeTransition ← FractionalTranslation ← SlideTransition ← I/flutter (32494): │ │ _FadeUpwardsPageTransition ← AnimatedBuilder ← RepaintBoundary I/flutter (32494): │ │ ← _FocusMarker ← Semantics ← FocusScope ← PageStorage ← ⋯ I/flutter (32494): │ │ offset: Offset(0.0, 0.0) I/flutter (32494): │ │ I/flutter (32494): │ └─child 1: PhysicalModelLayer#4fdc6 I/flutter (32494): │ │ creator: PhysicalModel ← AnimatedPhysicalModel ← Material ← I/flutter (32494): │ │ PrimaryScrollController ← _ScaffoldScope ← Scaffold ← I/flutter (32494): │ │ ClipDemoPage ← Semantics ← Builder ← I/flutter (32494): │ │ RepaintBoundary-[GlobalKey#0ce90] ← IgnorePointer ← I/flutter (32494): │ │ FadeTransition ← ⋯ I/flutter (32494): │ │ elevation: 0.0 I/flutter (32494): │ │ color: Color(0xfffafafa) I/flutter (32494): │ │ I/flutter (32494): │ ├─child 1: PictureLayer#6ee26 I/flutter (32494): │ │ paint bounds: Rect.fromLTRB(0.0, 0.0, 392.7, 738.2) I/flutter (32494): │ │ I/flutter (32494): │ ├─child 2: AnnotatedRegionLayer<SystemUiOverlayStyle>#cbeaf I/flutter (32494): │ │ │ value: {systemNavigationBarColor: 4278190080, I/flutter (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(392.7, 83.6) I/flutter (32494): │ │ │ offset: Offset(0.0, 0.0) I/flutter (32494): │ │ │ I/flutter (32494): │ │ └─child 1: PhysicalModelLayer#edb15 I/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 ← ⋯ I/flutter (32494): │ │ │ elevation: 4.0 I/flutter (32494): │ │ │ color: MaterialColor(primary value: Color(0xff2196f3)) I/flutter (32494): │ │ │ I/flutter (32494): │ │ └─child 1: PictureLayer#418ce I/flutter (32494): │ │ paint bounds: Rect.fromLTRB(0.0, 0.0, 392.7, 83.6) I/flutter (32494): │ │ I/flutter (32494): │ └─child 3: TransformLayer#7f867 I/flutter (32494): │ │ offset: Offset(0.0, 0.0) I/flutter (32494): │ │ transform: I/flutter (32494): │ │ [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] 0.0,0.0,0.0,1.0 I/flutter (32494): │ │ I/flutter (32494): │ └─child 1: PhysicalModelLayer#9f36b I/flutter (32494): │ │ creator: PhysicalShape ← _MaterialInterior ← Material ← I/flutter (32494): │ │ ConstrainedBox ← _FocusMarker ← Focus ← _InputPadding ← I/flutter (32494): │ │ Semantics ← RawMaterialButton ← KeyedSubtree-[GlobalKey#9ead9] I/flutter (32494): │ │ ← TickerMode ← Offstage ← ⋯ I/flutter (32494): │ │ elevation: 6.0 I/flutter (32494): │ │ color: Color(0xff2196f3) I/flutter (32494): │ │ I/flutter (32494): │ └─child 1: PictureLayer#2a074 I/flutter (32494): │ paint bounds: Rect.fromLTRB(320.7, 666.2, 376.7, 722.2) I/flutter (32494): │ I/flutter (32494): └─child 2: PictureLayer#3d42d I/flutter (32494): paint bounds: Rect.fromLTRB(0.0, 0.0, 1080.0, 2030.0) 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 绘制。

---来自腾讯云社区的---恋猫

关于作者: 瞎采新闻

这里可以显示个人介绍!这里可以显示个人介绍!

热门文章

留言与评论(共有 0 条评论)
   
验证码: