Flutter 绘制原理

本文详细介绍了Flutter中的Canvas、Layer和Scene在绘制过程中的角色,Canvas负责指令记录,Layer包括容器和绘制类,Scene管理屏幕内容。Picture是绘制产物,PictureLayer用于保存和复用绘制结果。还提到如何将Canvas绘制的位图导出为图片以及Layer的作用和类型划分。

Flutter中和绘制相关的对象有三个,分别是Canvas、Layer 和 Scene:

  • Canvas:封装了Flutter Skia各种绘制指令,比如画线、画圆、画矩形等指令。
  • Layer:分为容器类和绘制类两种;暂时可以理解为是绘制产物的载体,比如调用 Canvas 的绘制 API 后,相应的绘制产物被保存在 PictureLayer.picture 对象中。
  • Scene:屏幕上将要要显示的元素。在上屏前,我们需要将Layer中保存的绘制产物关联到 Scene 上。

Flutter 绘制流程:

  1. 构建一个 Canvas,用于绘制;同时还需要创建一个绘制指令记录器,因为绘制指令最终是要传递给 Skia 的,而 Canvas 可能会连续发起多条绘制指令,指令记录器用于收集 Canvas 在一段时间内所有的绘制指令,因此Canvas 构造函数第一个参数必须传递一个 PictureRecorder 实例。
  2. Canvas 绘制完成后,通过 PictureRecorder 获取绘制产物,然后将其保存在 Layer 中。
  3. 构建 Scene 对象,将 layer 的绘制产物和 Scene 关联起来。
  4. 上屏;调用window.render API 将Scene上的绘制产物发送给GPU。

Picture

  1. Picture 实际上是一系列的图形绘制操作指令,这一点可以参考 Picture 类源码的注释。
  2. Picture 要显示在屏幕上,必然会经过光栅化,随后Flutter会将光栅化后的位图信息缓存起来,也就是说同一个 Picture 对象,其绘制指令只会执行一次,执行完成后绘制的位图就会被缓存起来。

综合以上两点,我们可以看到 PictureLayer 的“绘制产物”一开始是一些列“绘图指令”,当第一次绘制完成后,位图信息就会被缓存,绘制指令也就不会再被执行了,所以这时“绘制产物”就是位图了。为了便于理解,后续我们可以认为指的就是绘制好的位图。

Canvas绘制的位图转图片

既然 Picture 中保存的是绘制产物,那么它也应该能提供一个方法能将绘制产物导出,实际上,Picture有一个toImage方法,可以根据指定的大小导出Image。

//将图片导出为Uint8List
final Image image = await pictureLayer.picture.toImage();
final ByteData? byteData = await image.toByteData(format: ImageByteFormat.png);
final Uint8List pngBytes = byteData!.buffer.asUint8List();
print(pngBytes);

Layer

作用

  1. 可以在不同的frame之间复用绘制产物(如果没有发生变化)。
  2. 划分绘制边界,缩小重绘范围。

类型

  1. OffsetLayer:根 Layer,它继承自ContainerLayer,而ContainerLayer继承自 Layer 类,我们将直接继承自ContainerLayer 类的 Layer 称为容器类Layer,容器类 Layer 可以添加任意多个子Layer。
    1. 将组件树的绘制结构组成一棵树。

      因为 Flutter 中的 Widget 是树状结构,那么相应的 RenderObject 对应的绘制结构也应该是树状结构,Flutter 会根据一些“特定的规则”(后面解释)为组件树生成一棵 Layer 树,而容器类Layer就可以组成树状结构(父 Layer 可以包含任意多个子 Layer,子Layer又可以包含任意多个子Layer)。

    2. 可以对多个 layer 整体应用一些变换效果。

      容器类 Layer 可以对其子 Layer 整体做一些变换效果,比如剪裁效果(ClipRectLayer、ClipRRectLayer、ClipPathLayer)、过滤效果(ColorFilterLayer、ImageFilterLayer)、矩阵变换(TransformLayer)、透明变换(OpacityLayer)等。

  2. PictureLayer:保存绘制产物的 Layer,它直接继承自 Layer 类。我们将可以直接承载(或关联)绘制结果的 Layer 称为绘制类 Layer

### ### Flutter 的工作原理和实现机制 Flutter 是一个基于 Skia 引擎的跨平台 UI 框架,其核心机制包括 Dart 语言运行时、渲染引擎、平台通道通信以及 Widget 架构。Flutter 的独特之处在于它不依赖平台原生组件,而是通过自绘引擎实现 UI 的一致性与高性能。 #### 渲染机制 Flutter 的界面渲染过程分为三个主要阶段:布局(Layout)、绘制(Paint)、合成(Composition)。在布局阶段,Flutter 根据约束条件计算每个 Widget 的尺寸和位置;在绘制阶段,将 Widget 转换为低级的绘图命令;在合成阶段,将多个图层合并为最终的图像并提交给 Skia 引擎进行 GPU 渲染 [^1]。 Flutter 使用 Dart 语言构建 UI,通过 Dart 的即时编译(JIT)和提前编译(AOT)机制支持热重载(Hot Reload)和高性能发布版本。在开发阶段使用 JIT 编译,提升开发效率;在发布阶段使用 AOT 编译,提高执行效率。 #### Widget 架构 Flutter 的 UI 是基于 Widget 构建的,Widget 是不可变的声明式 UI 描述。Flutter 的 Widget 分为 Stateless 和 Stateful 两种类型,StatelessWidget 表示静态 UI,StatefulWidget 表示具有状态变化的 UI。Widget 树通过 Reconciliation 算法高效更新 UI,确保性能最优 [^1]。 #### 平台通信机制 Flutter 通过 Platform Channel 与原生平台进行通信。Platform Channel 是一种异步消息机制,允许 Dart 代码与原生代码(如 Android 的 Java 或 Kotlin,iOS 的 Objective-C 或 Swift)交换数据。这种机制支持方法调用、事件流和基本数据类型的传输 [^1]。 例如,调用原生相机功能的代码如下: ```dart import 'package:flutter/services.dart'; final MethodChannel _channel = MethodChannel('camera'); Future<void> openCamera() async { await _channel.invokeMethod('openCamera'); } ``` #### 路由管理机制 Flutter 的路由管理通过 Navigator 实现,支持 push、pop、pushReplacement 等操作。例如,`pushNamed` 方法通过字符串匹配创建对应的 Route 并压入导航栈,`pushReplacement` 则在压入新路由的同时移除旧的路由 [^2]。 ```dart Navigator.pushNamed(context, '/secondPage'); ``` #### 数据绑定与状态管理 Flutter 提供了多种状态管理方案,包括 InheritedWidget、Provider、Bloc、MobX 等。其中,MobX 是基于响应式编程和观察者模式的框架,通过自动追踪状态变化并更新 UI,实现高效的数据绑定 [^4]。 例如,使用 MobX 的简单示例: ```dart class Counter = _Counter with _$Counter; abstract class _Counter implements Store { @observable int value = 0; @action void increment() { value++; } } ``` #### Dart 语言特性支持 Flutter 使用 Dart 语言进行开发,Dart 是一门强类型、支持面向对象和函数式编程的语言。Dart 的垃圾回收机制、异步支持(async/await)以及丰富的标准库为 Flutter 提供了良好的开发体验 [^3]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值