2025Flutter(安卓)面试题详解

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 的 didChangeDependenciesbuild 方法。

Provider

核心原理:

Provider 库基于 InheritedWidget 实现,通过封装 ChangeNotifierConsumer(其Element 被注册到 InheritedWidget 的依赖列表中) 简化了数据更新逻辑。ChangeNotifier 提供订阅能力,当调用 notifyListeners() 时,遍历所有监听器并触发回调(如 setState),重建 InheritedWidget,并触发子 Widget 的 didChangeDependenciesbuild 方法。

GetX

核心原理:结合响应式编程与依赖注入,通过轻量化设计实现高效状态管理。

使用 .obs 将变量转换为 Rx 对象(如 RxIntRxString),底层通过 StreamControllerStream 实现数据流监听,ObxGetX Widget 自动订阅变化,仅局部刷新关联 UI,通过 Get.put 注册单例,Get.find 全局获取,依赖关系由 GetX 自动管理。

Bloc

核心原理:基于 ​Stream 数据流事件驱动机制,实现业务逻辑与 UI 的分离。

用户交互被封装为 ​Event 对象 ,通过 Bloc.add(Event) 方法发送到 Bloc 内部的事件队列,Bloc 内部通过 StreamController<Event>RxDartSubject(如 PublishSubject)创建事件流,实时接收外部传入的事件,

用户交互触发 Event,Bloc 通过 mapEventToState 转换为 State,并输出 Stream,使用 StreamBuilder 监听状态流,局部更新 UI。

Riverpod

核心原理:Provider 的现代化升级,强调类型安全与自动依赖管理。

Redux

核心原理:基于单向数据流和纯函数,实现可预测的状态管理。

相关推荐
肥肥呀呀呀4 分钟前
flutter 中Stack 使用clipBehavior: Clip.none, 超出的部分无法响应所有事件
flutter
SY.ZHOU4 分钟前
Flutter如何支持原生View
flutter
sg_knight9 分钟前
Flutter嵌入式开发实战 ——从树莓派到智能家居控制面板,打造工业级交互终端
android·前端·flutter·ios·智能家居·跨平台
张风捷特烈2 小时前
每日一题 Flutter#4 | 说说组件 build 函数的作用
android·flutter·面试
小镇梦想家20 小时前
鸿蒙NEXT-Flutter(2)
flutter
至善迎风1 天前
一键更新依赖全指南:Flutter、Node.js、Kotlin、Java、Go、Python 等主流语言全覆盖
java·flutter·node.js
椒盐煎蛋1 天前
新建的Flutter插件工程,无法索引andorid工程代码;无法索引io.flutter包下代码。
flutter
张风捷特烈1 天前
每日一题 Flutter#3 | 说说 Widget 的派生体系
android·flutter·面试
爱意随风起风止意难平1 天前
003 flutter初始文件讲解(2)
学习·flutter
每次的天空2 天前
Android第十一次面试flutter篇
android·flutter·面试