Flutter 底层原理深度解析

知识结构

Flutter 是什么

Flutter 是 Google 开发的开源 UI 框架,用于从单一代码库构建跨平台应用。与其他跨平台方案不同,Flutter 自带渲染引擎,不依赖平台原生控件。

Flutter minimizes abstractions by bypassing system UI widget libraries in favor of its own widget set.

Flutter Architectural Overview

简单来说:Flutter 自己画所有的按钮、文字、动画,而不是让 Android 或 iOS 来画。这就像游戏引擎的工作方式。

整体架构

Flutter 采用分层架构设计,每一层都建立在下一层之上:

flowchart TB
    subgraph Framework["Framework 层 (Dart)"]
        direction TB
        Material["Material / Cupertino"]
        Widgets["Widgets 层"]
        Rendering["Rendering 层"]
        Foundation["Foundation 层"]
        Material --> Widgets --> Rendering --> Foundation
    end

    subgraph Engine["Engine 层 (C++)"]
        direction TB
        DartUI["dart:ui API"]
        Impeller["Impeller / Skia"]
        DartRuntime["Dart Runtime"]
        TextLayout["文本布局 / 图形"]
    end

    subgraph Embedder["Embedder 层"]
        direction LR
        Android["Android\n(Java/C++)"]
        iOS["iOS\n(Swift/ObjC)"]
        Desktop["Desktop\n(C++)"]
    end

    subgraph OS["操作系统"]
        direction LR
        AndroidOS["Android"]
        iOSOS["iOS"]
        DesktopOS["Windows/macOS/Linux"]
    end

    Framework --> Engine --> Embedder --> OS

Framework 层 (Dart)

这是我们日常开发接触最多的层,全部用 Dart 编写:

子层职责常用类
Material / Cupertino设计语言实现MaterialApp, CupertinoButton
Widgets组合式 UI 抽象StatelessWidget, StatefulWidget
Rendering布局和绘制RenderBox, RenderObject
Foundation基础工具类ChangeNotifier, Key

Engine 层 (C++)

Engine 是 Flutter 的核心,负责底层能力:

  • Impeller / Skia:图形渲染引擎
  • Dart Runtime:运行 Dart 代码
  • dart:ui:暴露给 Framework 的底层 API
  • 文本布局:处理复杂文字排版

Embedder 层

Embedder 是 Flutter 与各平台的”胶水层”:

  • 提供应用入口点
  • 协调渲染画布(Surface)
  • 处理输入事件
  • 管理消息循环

渲染机制

为什么 Flutter 要自己渲染

传统跨平台方案(如 React Native)将 UI 描述转换为原生控件。问题在于:

  1. 不同平台控件行为不一致
  2. 复杂动画难以实现
  3. 受限于平台控件能力

Flutter 的解决方案:自己画一切

flowchart LR
    subgraph Traditional["传统方案"]
        JS["JavaScript"] --> Bridge["Bridge"] --> Native["原生控件"]
    end

    subgraph Flutter["Flutter 方案"]
        Dart["Dart 代码"] --> Engine2["渲染引擎"] --> Canvas["GPU Canvas"]
    end

Impeller vs Skia

Flutter 有两个渲染引擎:

特性Impeller (新)Skia (旧)
着色器编译构建时预编译 (AOT)运行时编译 (JIT)
性能特点无着色器卡顿可能有首次渲染卡顿
平台支持iOS 默认, Android 实验性Web, 旧版 Android
图形 APIMetal, VulkanOpenGL, Metal

All shaders are compiled offline at build time. No runtime shader compilation jank.

Impeller README

Impeller 解决了 Flutter 长期被诟病的”着色器卡顿”问题。

渲染流水线

每一帧的渲染经过以下阶段:

flowchart LR
    Build["Build\n构建 Widget"] --> Layout["Layout\n计算布局"] --> Paint["Paint\n绑定命令"] --> Composite["Composite\n合成上屏"]

    style Build fill:#e1f5fe
    style Layout fill:#fff3e0
    style Paint fill:#f3e5f5
    style Composite fill:#e8f5e9
  1. Build 阶段:调用 build() 方法构建 Widget 树
  2. Layout 阶段:计算每个元素的大小和位置
  3. Paint 阶段:生成绘制指令(不是真正绑制)
  4. Composite 阶段:将图层合成并提交给 GPU

Engine 的任务运行器

Flutter Engine 使用四个任务运行器来处理不同工作:

运行器职责说明
Platform平台主线程处理平台消息,必须保持响应
UIDart 代码执行运行 build(),构建帧数据
RasterGPU 渲染处理图层树,生成 GPU 命令
IO资源加载处理图片解码、文件读写

三棵树原理

Flutter 中最重要的概念之一是三棵树:Widget Tree、Element Tree、RenderObject Tree。

三者的关系

flowchart TB
    subgraph WidgetTree["Widget Tree (不可变)"]
        W1["MyApp"]
        W2["Scaffold"]
        W3["Column"]
        W4["Text"]
        W5["Button"]
        W1 --> W2 --> W3
        W3 --> W4
        W3 --> W5
    end

    subgraph ElementTree["Element Tree (可变)"]
        E1["MyAppElement"]
        E2["ScaffoldElement"]
        E3["ColumnElement"]
        E4["TextElement"]
        E5["ButtonElement"]
        E1 --> E2 --> E3
        E3 --> E4
        E3 --> E5
    end

    subgraph RenderTree["RenderObject Tree (布局/绑制)"]
        R2["RenderScaffold"]
        R3["RenderFlex"]
        R4["RenderParagraph"]
        R5["RenderButton"]
        R2 --> R3
        R3 --> R4
        R3 --> R5
    end

    W1 -.->|"createElement()"| E1
    W2 -.->|"createElement()"| E2
    E2 -.->|"createRenderObject()"| R2

Widget Tree

Widget 是不可变的 UI 配置,描述”界面应该是什么样”:

// Widget 只是配置,非常轻量
class Greeting extends StatelessWidget {
final String name;
const Greeting({required this.name}); // 不可变
@override
Widget build(BuildContext context) {
return Text('Hello, $name!');
}
}

特点

  • 不可变(immutable)
  • 不存储状态
  • 不存储父子关系
  • 每次 setState() 都可能重建

Element Tree

Element 是 Widget 的实例化对象,持有实际的树结构:

// Element 是可变的,管理生命周期
abstract class Element {
Widget? _widget; // 当前配置
Element? _parent; // 父节点
// ...
}

特点

  • 可变(mutable)
  • 持有 Widget 引用
  • 管理父子关系
  • 管理生命周期和状态

RenderObject Tree

RenderObject 负责实际的布局和绑制

// RenderObject 处理布局和绑制
abstract class RenderObject {
void layout(Constraints constraints); // 布局
void paint(PaintingContext context); // 绘制
bool hitTest(Offset position); // 点击测试
}

特点

  • 处理几何计算
  • 执行实际绘制
  • 进行点击测试
  • 并非所有 Widget 都有对应的 RenderObject

为什么需要三棵树

问题解决方案
Widget 频繁重建开销大Widget 是轻量配置,重建成本低
需要保持状态Element 持有状态,Widget 变化时复用 Element
布局计算昂贵RenderObject 只在必要时重新计算

更新过程示意

当调用 setState() 时:

sequenceDiagram
    participant S as State
    participant F as Framework
    participant E as Element
    participant R as RenderObject

    S->>F: setState(() { count++ })
    F->>F: 标记 Element 为 dirty
    F->>E: 下一帧: rebuild()
    E->>E: 比较新旧 Widget
    alt Widget 类型相同
        E->>E: 更新 Widget 引用
        E->>R: 更新 RenderObject
    else Widget 类型不同
        E->>E: 创建新 Element
        E->>R: 创建新 RenderObject
    end

关键优化:Element 复用。只要 Widget 类型和 Key 相同,就复用 Element,避免重建整棵树。

跨平台原理

自绘引擎的优势

Flutter 不使用平台原生控件,而是自己绘制所有 UI:

flowchart TB
    subgraph RN["React Native 方式"]
        RNCode["React 代码"] --> RNBridge["Bridge"] --> AndroidView["Android View"]
        RNBridge --> iOSView["iOS UIView"]
    end

    subgraph Flutter["Flutter 方式"]
        DartCode["Dart 代码"] --> Impeller2["Impeller 渲染"]
        Impeller2 --> AndroidCanvas["Android Canvas"]
        Impeller2 --> iOSCanvas["iOS Metal"]
    end

优势

  • 像素级一致:所有平台渲染结果完全相同
  • 不受平台限制:可以实现任何视觉效果
  • 性能可控:不经过平台控件层

Platform Channel

当需要访问平台特定功能(如传感器、相机)时,使用 Platform Channel:

// Dart 端
const channel = MethodChannel('com.example/battery');
Future<int> getBatteryLevel() async {
final level = await channel.invokeMethod<int>('getBatteryLevel');
return level ?? -1;
}
// Android 端 (Kotlin)
class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "com.example/battery")
.setMethodCallHandler { call, result ->
if (call.method == "getBatteryLevel") {
result.success(getBatteryLevel())
} else {
result.notImplemented()
}
}
}
}
flowchart LR
    Dart["Dart 代码"] <-->|"MethodChannel"| Platform["Platform 代码"]
    Dart <-->|"EventChannel"| Platform
    Dart <-->|"BasicMessageChannel"| Platform

三种 Channel 类型:

  • MethodChannel:调用方法,获取返回值
  • EventChannel:监听平台事件流
  • BasicMessageChannel:简单消息传递

Dart 的角色

为什么选择 Dart

Flutter 团队评估了多种语言,Dart 胜出的原因:

The languages were compared on four primary dimensions: developer productivity, object-orientation, predictable high performance, and fast allocation.

Flutter FAQ

维度说明
开发效率热重载、良好的工具链
面向对象适合 UI 框架的编程范式
可预测性能无 GC 停顿导致的丢帧
快速内存分配适合 Flutter 的函数式编程风格

双编译模式

Dart 支持两种编译模式:

模式编译方式用途特点
DebugJIT (即时编译)开发调试支持热重载
ReleaseAOT (提前编译)生产发布启动快、性能好
flowchart LR
    subgraph Development["开发模式"]
        DartJIT["Dart 源码"] -->|"JIT"| VM["Dart VM"]
        VM -->|"热重载"| Update["增量更新"]
    end

    subgraph Production["生产模式"]
        DartAOT["Dart 源码"] -->|"AOT"| Native["原生机器码"]
        Native --> ARM["ARM / x64"]
    end

热重载原理

热重载是 Flutter 开发体验的核心:

  1. 增量编译:只编译修改的代码
  2. 注入更新:将新代码注入运行中的 VM
  3. 重建 Widget:触发受影响 Widget 的 build()
  4. 保持状态:Element 和 State 不变
sequenceDiagram
    participant Dev as 开发者
    participant IDE as IDE
    participant VM as Dart VM
    participant App as 运行中的 App

    Dev->>IDE: 保存代码
    IDE->>IDE: 增量编译
    IDE->>VM: 注入新代码
    VM->>App: 更新类定义
    App->>App: 重建 Widget Tree
    Note over App: State 保持不变

Flutter vs 其他方案

特性FlutterReact Native原生开发
渲染方式自绘引擎原生控件原生控件
开发语言DartJavaScriptKotlin/Swift
代码复用90%+70-90%0%
性能接近原生良好最佳
UI 一致性完全一致平台差异平台风格
包体积较大中等最小
热重载支持支持部分支持

何时选择 Flutter

  • 需要高度自定义的 UI
  • 追求跨平台一致性
  • 复杂动画需求
  • 同时发布多平台

何时不选择 Flutter

  • 需要深度平台集成
  • 极致的包体积要求
  • 团队熟悉原生开发

性能优化原理

Flutter 使用多种技术实现高性能:

次线性布局 O(N) / O(1)

Flutter performs one layout per frame, and the layout algorithm works in linear time.

Inside Flutter

  • 首次布局:O(N)
  • 增量更新:O(1) - 只重新布局脏节点

次线性构建

// 不好的写法:整棵树重建
Widget build(BuildContext context) {
return Column(
children: [
ExpensiveWidget(), // 每次都重建
Text('$counter'),
],
);
}
// 好的写法:只重建必要的部分
Widget build(BuildContext context) {
return Column(
children: [
const ExpensiveWidget(), // 不会重建
Text('$counter'),
],
);
}

InheritedWidget O(1) 查找

// O(1) 时间复杂度查找
final theme = Theme.of(context);
final media = MediaQuery.of(context);

使用哈希表存储,避免向上遍历查找。

参考资料

官方文档

GitHub 文档

Dart 相关

下一步

理解了底层原理后,可以继续学习:

Read Next

Flutter Impeller 渲染引擎深度解析

Read Previous

Git 常用命令与技巧