Flutter
介绍下Flutter的架构
由上图可知,Flutter框架自上而下分为Framework、Engine、Embedder三层。
- Framework(框架)使用Dart 语言编写,包含了基础组件 、布局 、动画 、手势 等功能。提供了丰富的 Widget(组件) ,用于构建用户界面 。负责处理渲染 、布局 、事件处理等逻辑。
- Engine(引擎) 用 C++ 实现的底层引擎 。提供图形渲染 Skia 引擎 、文本排版 、事件处理 、插件架构 等核心功能。负责将 Framework 层构建的界面转换为实际的像素显示。
- Embedder(嵌入)属于特定平台的嵌入层 。负责将 Flutter 引擎嵌入到不同的平台(如 Android、iOS、Web 等)中,并处理与平台相关的交互,如与原生系统的通信、窗口管理等。
Flutter 和其他跨平台方案的本质区别

React Native 之类的框架,只是通过 JavaScript 虚拟机扩展调用系统组件,由 Android 和 iOS 系统进行组件的渲染;
Flutter 则是自己完成了组件渲染的闭环。Flutter只关心向 GPU提供视图数据,GPU的 VSync信号同步到 UI线程,UI线程使用 Dart来构建抽象的视图结构,这份数据结构在 GPU线程进行图层合成,视图数据提供给 Skia引擎渲染为 GPU数据,这些数据通过 OpenGL或者 Vulkan提供给 GPU。
UI线程 负责处理Dart代码、构建 Widget 树转换成 RenderObject 树再生成Layer Tree,GPU线程 负责接收Layer Tree、进行合成 (Compositing
)、调用图形库 (Skia
) 与GPU交互并完成最终绘制。所有GPU操作都在此线程,避免阻塞UI响应。
介绍下Flutter的渲染机制(三棵树)
- Widget树是开发写的代码组件,比如 Container()、Text(),这些一层层嵌套的Widget就构成了一个 Widget 树,Widget 本身是很轻量,它相当于一个配置。它描述了组件的样子、数据,但它自己不直接参与真正的绘制工作。Widget 对象通常是不可变的,一旦创建,它的属性就不会再改了。如果我们想改,通常是创建一个新的 Widget 实例。
- Element树是 Widget 的实体,它持有对应的Widget和 RenderObject ,当 Widget 树中某个节点变化时,Element 会对比新旧 Widget ,决定是否复用旧的 RenderObject ,还是销毁重建。
- RenderObject树是真正干活的,它负责布局(layout)、绘制(paint)、点击测试(hit test)。比如 RenderFlex 对应 Row/Column ,它计算子控件的位置和大小。
它们怎么协同工作的?当 Widget 树变化时,Element 树会去比较新的 Widget 和旧的 Widget:
- 如果 Widget 的类型和 Key 没变,Element 就会被复用,它会拿到新的 Widget 配置去更新对应的 RenderObject。
- 如果类型或 Key 变了,通常旧的 Element 和它管理的 RenderObject 就会被销毁,然后创建新的。
- Element 树进而管理 RenderObject 树。当 Element 更新了 RenderObject 的配置后,RenderObject 就会进行重新布局和重绘。
所以 Flutter 的设计哲学是:频繁重建 Widget 树,但通过 Element 树控制实际渲染开销。这也是为什么 setState() 不会导致性能灾难------底层有 Element 和 RenderObject 的优化。
setState做了哪些工作?
setState()过程主要工作是记录所有的脏元素,会引起build函数执行,更新widget树、更新Element树和RenderObject树,最后重新渲染。
源码中通过 _element!.markNeedsBuild()
实现,该方法将关联的 Element
加入 _dirtyElements
列表(待重建元素队列),setState()
是异步操作 ,不会立即更新 UI。它向 Flutter 框架提交一个重建请求,等待下一帧绘制周期执行。在下一帧事件循环中,框架遍历 _dirtyElements
列表,逐个调用 Element.rebuild()
方法,重新构建对应的 Widget 树。重建过程通过 build()
方法生成新的 Widget 树,并与旧树进行差异化比较(Diff 算法),仅更新发生变化的 UI 部分。
Dart的线程模型是怎样运行的?
Dart 在单线程中是以消息循环机制来运行的,包含两个任务队列,一个是"微任务队列 " microtask queue,另一个叫做"事件队列 " event queue。 微任务队列 的优先级高于事件队列 。在每一次事件循环中,Dart总是先去第一个微任务队列 中查询是否有可执行的任务,如果没有,才会处理后续的事件队列的流程。
微任务队列插入任务?
Future.microtask()
scheduleMicrotask()
Stream
中的执行异步的模式就是scheduleMicrotask
。因为microtask的优先级又高于event。所以,如果 microtask 太多
就可能会对触摸、绘制等外部事件造成阻塞卡顿
。
向事件队列插入任务? Future就是将任务插入到事件队列
Future和Stream有什么区别?
Future中的任务会加入下一轮事件循环,而Stream中的任务则是加入微任务队列。
Future 用于表示单个运算的结果,而 Stream 则表示多个结果的序列。
Flutter 是如何与原生Android、iOS进行通信的?
- MethodChannel: 用于传递方法调用
用途:用于 Flutter 调用原生代码中的方法,并可以异步地接收一个返回结果。反过来,原生代码也可以通过它调用 Flutter (Dart) 中的方法(虽然不那么常见,但可以实现)。
工作方式:你在 Flutter 端定义一个 MethodChannel,并给它一个唯一的名称。然后在原生端也用同样的名称创建一个 MethodChannel 并设置一个 MethodCallHandler。当 Flutter 端调用 invokeMethod 时,原生端的 Handler 就会收到这个调用,执行相应的原生代码,然后可以通过 result.success()
、result.error()
或 result.notImplemented()
返回结果给 Flutter。
- EventChannel:用于原生代码向 Flutter 发送持续的数据流。
用途:用于原生代码向 Flutter 发送持续的数据流。
工作方式:Flutter 端创建一个 EventChannel 并监听它返回的 Stream。原生端则负责在这个 Channel 上发送事件(数据)。一旦有新的数据,Flutter 端的监听器就会收到。
例子:比如原生那边有传感器数据(像 GPS 位置更新、陀螺仪数据)、网络连接状态变化、或者监听广播事件等,就可以通过 EventChannel 持续地把这些信息传递给 Flutter。
- BasicMessageChannel:用于传递字符串和半结构化的信息
用途:用于传递结构化的数据,可以自定义编解码器 (codec)。它比 MethodChannel 更基础,可以双向发送消息。
工作方式:双方都创建一个 BasicMessageChannel,然后可以互相发送消息。你可以指定消息的编解码器,比如 StringCodec、JSONMessageCodec,或者标准的 StandardMessageCodec(支持常见的数据类型如数字、字符串、布尔、列表、字典等)。
例子:当你需要发送一些自定义的、可能比较大的数据块,或者对编解码有特殊要求时,可以考虑使用它。
状态管理框架对比
setState
Flutter 内置,适用于局部状态(如单个 Widget 的交互状态),简单直接
InheritedWidget
InheritedWidget 通过 Element 映射表 和 依赖订阅机制 实现高效数据共享,用于在 Widget 树中高效共享数据的组件,通过它可以在父 Widget 和子 Widget 之间传递数据,避免逐层传递的冗余操作。 依赖注册: 当子 Widget 调用 dependOnInheritedWidgetOfExactType
时,Flutter 会将该子 Widget 的 Element 添加到 InheritedElement 的 依赖列表 中。这建立了一个隐式的订阅关系。 更新通知流程: 父 Widget 通过 setState
触发 InheritedWidget 的重建,当updateShouldNotify
返回 true
则通知所有依赖的子 Widget,InheritedElement 调用 notifyClients
,遍历依赖列表并触发子 Widget 的 didChangeDependencies
或 build
方法。
Provider
核心原理:
Provider 库基于 InheritedWidget 实现,通过封装 ChangeNotifier
和 Consumer
(其Element
被注册到 InheritedWidget 的依赖列表中) 简化了数据更新逻辑。ChangeNotifier 提供订阅能力,当调用 notifyListeners()
时,遍历所有监听器并触发回调(如 setState
),重建 InheritedWidget,并触发子 Widget 的 didChangeDependencies
或 build
方法。
GetX
核心原理:结合响应式编程与依赖注入,通过轻量化设计实现高效状态管理。
使用 .obs
将变量转换为 Rx
对象(如 RxInt
、RxString
),底层通过 StreamController
和 Stream
实现数据流监听,Obx
或 GetX
Widget 自动订阅变化,仅局部刷新关联 UI,通过 Get.put
注册单例,Get.find
全局获取,依赖关系由 GetX
自动管理。
Bloc
核心原理:基于 Stream 数据流 与事件驱动机制,实现业务逻辑与 UI 的分离。
用户交互被封装为 Event 对象 ,通过 Bloc.add(Event)
方法发送到 Bloc 内部的事件队列,Bloc 内部通过 StreamController<Event>
或 RxDart
的 Subject
(如 PublishSubject
)创建事件流,实时接收外部传入的事件,
用户交互触发 Event
,Bloc 通过 mapEventToState
转换为 State
,并输出 Stream
,使用 StreamBuilder
监听状态流,局部更新 UI。
Riverpod
核心原理:Provider
的现代化升级,强调类型安全与自动依赖管理。
Redux
核心原理:基于单向数据流和纯函数,实现可预测的状态管理。