众所周知,Flutter 最被人熟知的就是 Widget - Element - RenderObject 这三棵树,而实际上如下图所示,在 paint 阶段,RenderObject 并不直接执行真正的绘制操作,而是会创建和配置相应的 Layer 对象,例如 PictureLayer :

而我们知道,在 Dart 层面,当 RenderObject 的 isRepaintBoundary 为 ture 时,Flutter Framework 就会自动创建一个 OffsetLayer 来"承载"这片区域,所以 Layer 内部的画面更新一般不会影响到其他 Layer ,这是 Flutter 在 Dart 层面最基础的 Layer 概念。
例如
Navigator跳转不同路由页面,每个页面内部就有一个RepaintBoundary控件,这个控件对应的RenderRepaintBoundary内的isRepaintBoundary标记位就是为true,从而路由页面之间形成了独立的Layer。
那 Layer 是如何更新?这就涉及了 Dart Layer 内部的 markNeedsAddToScene 和 updateSubtreeNeedsAddToScene 这两个方法,简单来说:
markNeedsAddToScene方法类似setState,它其实就是把Layer内的_needsAddToScene标记为trueupdateSubtreeNeedsAddToScene方法就是遍历所有 childLayer,通过递归调用updateSubtreeNeedsAddToScene()判断是否有child需要_needsAddToScene,如果是那就把自己也标记为true。
而对应到实际的执行上就是:只有当 _needsAddToScene 等于 true 时,对应 Layer 的 addToScene 才会被调用;而当 Layer 的 _needsAddToScene 为 false 且 _engineLayer 不为空时就触发 Layer 的复用。
例如,当一个新的页面打开时,底部的页面并没有发生变化时,它只是参与画面的合成,所以对于底部页面来说它 "
Layer" 是可以直接被复用参与绘制 。
当然,Dart 层的 Layer 实际上对应在 C++ 有具体的 EngineLayer 实现,而 Flutter 提交给 Engine 进行光栅化操作的对象,实际上就是承载绘制指令的 C++ LayerTree,而 Layer 从 Dart 「透传」到 C++ 之后,就会通过 flow 模块来实现光栅化并合成输出,这才是真实绘制的开始。
Flutter 里 Layer 有 Dart 层面的 Layer 和 C++ 层面的 Layer ,简单说,一般 C++ 层面的会称为
EngineLayer,比如ClipPathLayer和ClipPathEngineLayer。
而这里说的「透传」,在上古老版本的是 ui.window,在现在的版本指的是 ui.FlutterView,在 Dart 层面一般是通过如 ui.window.render(sceneBuilder.build())或者 FlutterView.run(sceneBuilder.build()) 来提交到 C++ 构建,但是这里我们可以看到, render 方法需要的参数是 Scene 对象而不是 Layer ,这又是为什么?
因为简单来说,
Scene对象就是 Flutter 在 Dart 操作 C++ Layer 的入口 ,而在 Flutter Framework 中Scene只能通过SceneBuilder构建。
所以如下图所示,一般来说 Dart 层的 Layer 不会直接操作 Scene ,而是通过统一基类的 addToScene 方法来操作 SceneBuilder , 例如 Dart 层的 ClipPathLayer 会在它实现的 addToScene(ui.SceneBuilder builder) 调用 builder.pushClipPath 来得到一个 ClipPathEngineLayer,而所有 Dart Layer 最终会通过 sceneBuilder.build() 统一提交到 C++ :

所以 ui.SceneBuilder (Dart) 属于构建渲染场景的关键 API,它提供了一系列 push 方法和 add 方法,如 addPicture、addTexture 和 addPlatformView,这些方法在底层分别对应着 Engine 中 PictureLayer、TextureLayer 和 PlatformViewLayer 的创建,SceneBuilder 的职责就是收集这些 Layer,并将它们组装成一个完整的 LayerTree,最终交由 Engine 处理。
而在 Flutter 里,一般 Flutter 中 Layer 可以分为 ContainerLayer 和非 ContainerLayer ,ContainerLayer 是可以具备子节点,也就是带有 append 方法,例如:
- 位移类(
OffsetLayer/TransformLayer); - 透明类(
OpacityLayer) - 裁剪类(
ClipRectLayer/ClipRRectLayer/ClipPathLayer); 

对于 ContainerLayer ,因为这些 Layer 都是一些像素合成的操作,其本身是不具备"描绘"控件的能力,如果要呈现画面一般需要和 PictureLayer 结合。
比如
ClipRRect控件的RenderClipRRect内部,在pushClipRRect时可以会创建ClipRRectLayer,而新创建的ClipRRectLayer会通过appendLayer方法触发append操作添加为父Layer的子节点。
非 ContainerLayer 一般不具备子节点,比如:
PictureLayer是用于绘制画面,Flutter 上的控件基本是绘制在这上面;TextureLayer一般用于外界纹理或者 PlatformView 实现PlatformViewLayer一般用于 PlatformView 相关嵌入的使用场景;
举个例子,控件绘制时的 Canvas 来源于 PaintingContext , 而如下代码所示 PaintingContext 通过 _repaintCompositedChild 执行绘制后得到的 Picture 最后就是提交给所在的 PictureLayer.picture:
            
            
              ini
              
              
            
          
          void stopRecordingIfNeeded() {
    if (!_isRecording)
      return;
    _currentLayer.picture = _recorder.endRecording();
    _currentLayer = null;
    _recorder = null;
    _canvas = null;
  }
        而对于 TextureLayer 和 PlatformViewLayer ,实际上它们也是有着不一样的故事,特别是在 PlatformView 实现上。
在 C++ 层面,PlatformViewLayer 和 TextureLayer 是 Flutter Engine 在 flow 渲染层中的两个核心类,特别是在 PlatformView 上:
PlatformViewLayer通过将最终的视图合成工作委托给宿主系统(Android),提供了原生的交互和 UI 保真度,但对应性能开销也会较高 ,比如 Android 平台的 HC 实现TextureLayer提供了更轻量级的纹理合成支持,用于在 Flutter 自己的渲染管线内展示原生像素流,但在交互性和原生视图类型兼容性上存在一些限制,比如 Android 平台的 VD 和 TLHC 实现
要了解 PlatformViewLayer 和 TextureLayer 在 PlatformView 的实现,就需要简单聊聊 Layer 的 Preroll 与 Paint 阶段:
Preroll阶段 :这是对LayerTree的第一次遍历,Engine 会调用每个 Layer 的Preroll方法,用于计算绘制边界(paint bounds)、确定渲染属性(如不透明度、裁剪区域)以及检查所需资源是否就绪Paint阶段 :这是第二次遍历,Engine 调用每个 Layer 的Paint方法来生成绘制指令,对于大多数 Layer 而言,这意味着将绘制命令添加到一个DisplayList(Flutter 内部的绘制操作记录)
TextureLayer
TextureLayer 的核心目标是在 Flutter 画布上提供一个矩形区域,用于渲染由原生代码管理的外部纹理,它的机制非常适合那些会产生连续图像流的场景,例如视频播放器、相机预览,或使用 OpenGL/Vulkan 等原生图形库渲染的自定义图形。
TextureLayer 的 Preroll 方法实现非常直接,它主要调用 set_paint_bounds 来声明其在场景中所占据的矩形区域,此外它还会通知 DiffContext 该子树中存在 TextureLayer,防止在某些情况下父级 ContainerLayer 错误地跳过对 TextureLayer 的差异比对(diffing)优化。
Paint 方法是 TextureLayer 功能的核心,简单流程为:
- 获取纹理对象 :通过 
PrerollContext中的texture_registry,使用成员变量texture_id_查找到对应的flutter::Texture对象 - 执行绘制 :如果纹理对象有效,就调用对象的 
Paint方法texture->Paint(context.canvas,...),这个调用会直接将外部纹理绘制到当前 Flutter 的DlCanvas上 
这里的
texture_id_对应的是一个实现了flutter::ExternalTextureC++ 接口的原生对象,在不同平台上,ExternalTexture的实现有所不同,例如在 Android 上通常由SurfaceTexture支持;在 iOS 上,则由一个实现了FlutterTexture协议的对象来支持,这个对象提供一个CVPixelBuffer。
而 TextureLayer 支持绘制的核心是可以直接在 GPU 上生成纹理,然后通过 ExternalTexture 接口将对应的 GPU 纹理的句柄(handle)传递给 Flutter Engine ,从而避免了将像素数据从 GPU 拷贝到主内存,再从主内存拷贝回 GPU 的昂贵过程。
PlatformViewLayer
如果说 TextureLayer 是把最终绘制过程统一到 Flutter ,那 PlatformViewLayer 就是将最终绘制过程从 Flutter 放到系统渲染。
在 Preroll ,PlatformViewLayer 会执行一个关键检查:if (context->view_embedder == nullptr),这意味着它的正常工作其实依赖于一个有效的 ViewEmbedder 上下文。
如果检查失败,就会直接输出类似 "Trying to embed a platform view but the PrerollContext does not support embedding" 的错误。
通过检查后,它会设置一个贯穿渲染流程的标志位:context->has_platform_view = true;,这个标志位会向整个渲染管线宣告:即将发生一次"合成中断"。
所以 PlatformViewLayer 的 Paint 方法与 TextureLayer 的截然不同,对于 Flutter 的 DlCanvas 而言,这个方法几乎是一个空操作,它不会向 context.canvas 发出任何绘制指令,它的唯一作用是在 LayerTree 中充当一个标记。
在 Dart 层,
PlatformViewLayer.addToScene方法会调用builder.addPlatformView(...),也就是指示 Engine 在场景中插入这个特殊标记的指令。
所以 PlatformViewLayer 里ViewEmbedder 很关键,它起到了编排合成中断的作用。
而 ViewEmbedder 是一个平台相关的组件,负责管理原生视图层级,当 Rasterizer 在 Preroll 阶段检测到 has_platform_view 标志位被设置时,就会导致整个渲染行为会发生根本性改变,它不再将所有 Flutter 内容渲染到单一的 Surface,而是执行以下步骤:
- 渲染底层内容 :将 
PlatformViewLayer之前的所有 Flutter 内容渲染到一个离屏纹理中 - 通知 Embedder :通知 
ViewEmbedder显示由view_id_标识的原生视图 - 渲染顶层内容 :将 
PlatformViewLayer之后的所有 Flutter 内容渲染到第二个离屏纹理中 - 委托系统合成 :最后,它依赖宿主操作系统的合成器(Android 上的 
SurfaceFlinger)来正确地堆叠这些图层,最终形成屏幕上看到的画面 

因此,PlatformViewLayer 本质上不是一个给 Skia 或 Impeller 的绘制指令,而是给 Engine Shell 和操作系统合成器的一个高级命令,而这个切换过程涉及昂贵的内存操作和线程同步,这也是其性能开销的根源。
最后
那么,到这里关于 Flutter 里 Layer 的整体概念就介绍完了,虽然说的是 Layer ,但是也涉及了不少其他东西,例如 PlatformView,整体来说这并不是一篇实用的文章,但是它确实能帮你更好理解 Flutter 里的渲染机制,特别是对于 PlatformView 的底层实现原理,换句话说,也许哪天你遇到 Flutter 画面闪烁了,这些概念也许就用上了呢?