文章目录
- 学习建议
- [基础层(7 题)](#基础层(7 题))
-
- [#1 Flutter 里「一切皆 Widget」是什么意思? 🔥](#1 Flutter 里「一切皆 Widget」是什么意思? 🔥)
- [#2 StatelessWidget 和 StatefulWidget 有什么区别? 🔥](#2 StatelessWidget 和 StatefulWidget 有什么区别? 🔥)
- [#3 `build()` 方法的作用是什么? 🔥](#3
build()方法的作用是什么? 🔥) - [#4 `setState()` 做了什么? 🔥](#4
setState()做了什么? 🔥) - [#5 `BuildContext` 是什么? ⭐](#5
BuildContext是什么? ⭐) - [#6 `const` 构造函数有什么作用? ⭐](#6
const构造函数有什么作用? ⭐) - [#7 Flutter 常见 Widget 如何分类? ⭐](#7 Flutter 常见 Widget 如何分类? ⭐)
- [进阶层(7 题)](#进阶层(7 题))
-
- [#8 三棵树(Widget / Element / RenderObject)各自职责? 🔥](#8 三棵树(Widget / Element / RenderObject)各自职责? 🔥)
- [#9 为什么 Widget 设计成 immutable? 🔥](#9 为什么 Widget 设计成 immutable? 🔥)
- [#10 Element 的 diff(reconciliation)怎么做? ⭐](#10 Element 的 diff(reconciliation)怎么做? ⭐)
- [#11 Key 的作用与常见类型? ⭐](#11 Key 的作用与常见类型? ⭐)
- [#12 State 生命周期有哪些阶段? 🔥](#12 State 生命周期有哪些阶段? 🔥)
- [#13 InheritedWidget 是什么机制? ⭐](#13 InheritedWidget 是什么机制? ⭐)
- [#14 `didChangeDependencies` 何时调用? ⭐](#14
didChangeDependencies何时调用? ⭐)
- [核心层(6 题)](#核心层(6 题))
-
- [#15 从 `setState` 到屏幕更新经历什么? 🔥](#15 从
setState到屏幕更新经历什么? 🔥) - [#16 GlobalKey 的使用场景与代价? 💡](#16 GlobalKey 的使用场景与代价? 💡)
- [#17 Element 的 mount、update、unmount 过程? 💡](#17 Element 的 mount、update、unmount 过程? 💡)
- [#18 Flutter Widget 体系与 Jetpack Compose 有何异同? 💡](#18 Flutter Widget 体系与 Jetpack Compose 有何异同? 💡)
- [#19 Theme 与 MediaQuery 如何向下传递? ⭐](#19 Theme 与 MediaQuery 如何向下传递? ⭐)
- [#20 如何优化 Flutter 列表滑动性能? 🔥](#20 如何优化 Flutter 列表滑动性能? 🔥)
- 面试策略速查
- 完整链路一句通
- 相关推荐
- [#15 从 `setState` 到屏幕更新经历什么? 🔥](#15 从
学习建议
| 目标层级 | 建议 |
|---|---|
| 初级 | 掌握基础层 7 题:Widget 概念、Stateless/Stateful、build/setState、BuildContext、const;工程联想看追问第 1 条 |
| 中级 | 通读全文;进阶层能讲清三棵树、Element diff、Key 选型、State 生命周期、InheritedWidget |
| 高级 | 核心层必答:setState 到渲染链路、GlobalKey 代价、Widget 重建 vs Element 复用;能对比 Compose 重组与 Android View 绘制 |
基础层(7 题)
#1 Flutter 里「一切皆 Widget」是什么意思? 🔥
标准回答
Flutter 用 Widget 描述 UI 配置:布局、样式、交互、动画都是 Widget 组合。runApp 传入根 Widget,子树递归 build 形成界面。Widget 本身是不可变配置对象 ,真正挂载与绘制由 Element、RenderObject 承担;开发者日常写的 Container、Text 都是 Widget 子类。
面试官可能继续追问
-
Android 工程里怎么接入 Flutter 页面?
FlutterActivity/FlutterFragment或FlutterEngine缓存;runApp挂根 Widget,与原生 Activity 并存。 -
Widget 和 Android 的 View 是一回事吗?
不是;View 是可变绘制节点,Widget 是不可变描述,更接近 Compose 的 Composable 配置层。
#2 StatelessWidget 和 StatefulWidget 有什么区别? 🔥
标准回答
StatelessWidget :无内部可变状态,build 仅依赖构造参数与 InheritedWidget 上下文,配置不变则逻辑可 const 复用。StatefulWidget :自身不可变,可变状态放在 companion State 对象;setState 通知框架标记 dirty 并触发 build 更新子树。有交互、动画、异步结果等需保留跨帧数据时用 Stateful。
面试官可能继续追问
-
Android 里列表项用哪种?
纯展示用 Stateless;点击展开、局部 loading 用 Stateful 或把状态上提到父级。
-
StatefulWidget 为什么拆成 Widget + State?
Widget 不可变便于 diff 复用 Element;State 持有可变数据与
Ticker等生命周期资源。
#3 build() 方法的作用是什么? 🔥
标准回答
build 根据当前配置与上下文描述 子 Widget 树,不负责直接改屏幕像素。框架在需要时调用:首次挂载、setState、父 Widget 更新、InheritedWidget 变化等。build 应无副作用、执行要快;耗时逻辑放 initState、异步回调或状态管理方案,避免每帧重建重算。
面试官可能继续追问
-
Android 混合工程里
build能调 Platform Channel 吗?不应在
build里同步调原生;放initState或按钮回调,避免重建重复调用。 -
build会被频繁调用吗?会;父更新、媒体查询变化、主题切换都会触发,故要保持纯函数式描述。
#4 setState() 做了什么? 🔥
标准回答
setState 在 State 内执行:先执行传入闭包更新字段,再将对应 Element 标记为 dirty,调度下一帧重建该 State 的 build。不会立刻同步重绘,而是合并进帧调度。未挂载的 State 调 setState 会断言失败;应在 dispose 后避免调用。
面试官可能继续追问
-
Android 里 Flutter 页数据从原生传来怎么刷新?
Channel 回调里
setState或改ValueNotifier/ChangeNotifier,切回 UI 线程再更新。 -
setState和直接改字段不调的区别?只改变量不通知框架,界面不更新;必须
setState或等价通知机制。
#5 BuildContext 是什么? ⭐
标准回答
BuildContext 是 Element 对外接口,代表 Widget 在树中的位置。用于 Theme.of(context)、MediaQuery.of(context)、Navigator.of(context) 等向上查找 祖先数据;findAncestorStateOfType 获取祖先 State。Context 对应 Element 生命周期,异步回调后使用前需 mounted 检查,避免 dispose 后访问。
面试官可能继续追问
-
Android 工程里用 context 拿主题适配刘海屏?
MediaQuery.of(context).padding读安全区,配合SafeArea做 Edge-to-Edge 适配。 -
Context 和 Widget 能长期持有吗?
不应在单例中长期持有;异步完成后用
mounted守卫,防内存泄漏类比 Activity 泄漏。
#6 const 构造函数有什么作用? ⭐
标准回答
const Widget 在编译期 canonicalize,运行时复用同一实例,减少分配与 diff 成本。子树配置不变时应尽量 const(如 SizedBox、Padding 固定值)。const 要求构造参数编译期常量;含运行时变量则不能 const。过度省略 const 会增加 GC 与 Element 更新判断开销。
面试官可能继续追问
-
Android 混合页里列表性能怎么蹭
const?列表项静态装饰层
const包裹,仅变化部分(标题文本)非 const。 -
const子树父setState会重建吗?父
build会执行,但const子 Widget 实例相同,利于 Element 复用跳过深层更新。
#7 Flutter 常见 Widget 如何分类? ⭐
标准回答
布局类 :Row/Column/Flex/Stack/Padding 等,约束子节点尺寸位置。绘制类 :Text/Image/DecoratedBox/CustomPaint 等,负责外观。功能/代理类 :GestureDetector、InheritedWidget、Viewport 等,附加交互或数据下发。组合大于继承,复杂 UI 拆小 Widget 利于重建边界控制。
面试官可能继续追问
-
Android 开发者迁移时怎么对应?
LinearLayout→Row/Column,TextView→Text,RecyclerView→ListView.builder/ListView。 -
分类对性能的意义?
把常变数据包在最小 Stateful 子树,减少
setState影响范围。
进阶层(7 题)
#8 三棵树(Widget / Element / RenderObject)各自职责? 🔥
标准回答
Widget :不可变配置,描述「要什么」。Element :可变树节点,负责挂载、更新、卸载,持有 Widget 与 RenderObject 桥梁,执行 diff 决策是否复用。RenderObject :布局、绘制、命中测试,参与帧管线 layout→paint。一 Widget 可对应多 Element(如 StatefulWidget 产生子 Element),但 Render 层才触达屏幕。
面试官可能继续追问
-
Android 里调试「树」看哪?
DevTools Widget Inspector 看三树;类比 Layout Inspector 看 View 层级。
-
为什么分 Widget 与 RenderObject?
配置频繁重建而布局绘制昂贵;分离后配置变可复用 Element/RenderObject,降本。
#9 为什么 Widget 设计成 immutable? 🔥
标准回答
不可变使框架能用引用相等 快速判断配置是否变化:canUpdate 比较 runtimeType 与 key,相同则更新 Element 内 Widget 引用而非重建子树。也避免多 Element 共享可变 Widget 导致状态错乱。可变数据放 State、Model 或状态管理容器,Widget 只携带快照配置。
面试官可能继续追问
-
immutable 意味着不能改界面吗?
能;通过新 Widget 配置替换旧配置驱动更新,而非原地 mutate Widget。
-
Compose 的 Composable 参数也强调稳定吗?
是;两者思想一致------不稳定参数阻碍框架的跳过优化,详见 #19 系统对比。
#10 Element 的 diff(reconciliation)怎么做? ⭐
标准回答
父 Element 在 updateChild 时用 Widget.canUpdate(类型 + Key)判断旧 Element 能否复用:能则 Element.update 换 Widget 配置;不能则 deactivate 旧子树、inflateWidget 新子树。diff 在 build 阶段同步完成,再向下标记 dirty 布局/绘制。
面试官可能继续追问
-
Android 列表滑动项状态乱了怎么排查?
同位置类型变化无 Key 时按位置复用,导致状态带到错误 item。Key 的选型与最佳实践详见 #11。
-
diff 和 Virtual DOM 像吗?
思想相近:配置树对比最小化真实节点变更;Flutter 是三树而非单 DOM。
#11 Key 的作用与常见类型? ⭐
标准回答
Key 标识 Widget 身份 ,供 Element diff 决定复用哪棵子树。ValueKey :值相等即同身份,适合列表 id。ObjectKey :对象身份。UniqueKey :每次 build 新身份,强制重建。GlobalKey :全局唯一,可跨树取 State/RenderObject,代价是打断复用优化。同类型同位置默认无 Key 按位置匹配。
面试官可能继续追问
-
Android 工程里 Tab 切换保页面状态用什么?
AutomaticKeepAliveClientMixin+ 稳定 Key,或IndexedStack保留子树。 -
列表用
index做 Key 可以吗?插入删除会错配;用业务唯一 id 的
ValueKey。
#12 State 生命周期有哪些阶段? 🔥
标准回答
createState→initState(一次,可订阅)→didChangeDependencies(依赖的 InheritedWidget 变化)→build(可多次)→deactivate(暂离树,可重新挂载)→dispose(永久销毁,释资源)。setState 在 mounted 时触发重建。Ticker/AnimationController 须在 dispose 释放。
面试官可能继续追问
-
Android 混合页 FlutterFragment 销毁时要注意?
dispose里取消 Channel 监听、定时器,类比onDestroy清监听。 -
initState里能调InheritedWidget吗?不宜依赖 context 读 Inherited;用
didChangeDependencies或build内读取。
#13 InheritedWidget 是什么机制? ⭐
标准回答
InheritedWidget 向子树下发 配置(主题、语言、用户会话),子节点 dependOnInheritedWidgetOfExactType 注册依赖,祖先更新时标记依赖 Element dirty 并重建。updateShouldNotify 控制是否通知。Theme、MediaQuery、DefaultTextStyle 均基于此;Flutter 3 亦常用 InheritedModel 做选择性通知。
面试官可能继续追问
-
Android 里主题切换怎么联动 Flutter?
原生改主题后通知 Flutter 根
ThemeData重建,或双端各自维护再 Channel 同步。 -
和 Provider 关系?
Provider 基于 InheritedWidget 封装;本篇不展开状态方案,见 FL3。
#14 didChangeDependencies 何时调用? ⭐
标准回答
首次依赖的 InheritedWidget 可用时调用,之后任一已注册 Inherited 祖先更新也会调用,早于 build。适合在此读取 MediaQuery、Localizations 并缓存(注意后续变化需能响应)。比 initState 更适合依赖 context 的一次性初始化;避免在此做重量级重复计算。
面试官可能继续追问
-
Android 旋转屏会触发吗?
会;
MediaQuery变化触发,需重建布局适配新宽高。 -
和
build里直接读 Inherited 区别?功能类似;
didChangeDependencies适合副作用型准备,但仍要轻量。
核心层(6 题)
#15 从 setState 到屏幕更新经历什么? 🔥
标准回答
setState 标记 Element dirty→build 产出新 Widget 子配置→updateChild diff→RenderObject markNeedsLayout/markNeedsPaint→帧回调 PipelineOwner.flushLayout→flushCompositingBits→flushPaint→引擎合成上屏。若仅绘制属性变可能跳过 layout;子树结构不变可复用 RenderObject。多 dirty 合并单帧处理。
面试官可能继续追问
-
Android Display Pipeline 对照理解?
类似 traversal 后提交 buffer;Flutter 在引擎层 Rasterize,最终也进 SurfaceFlinger 合成。
-
频繁
setState一定卡吗?看重建范围与 layout 成本;局部 Stateful +
const可减压,详情见 FL6。 -
Widget 重建、Element 复用、RenderObject 更新三者关系?
父
setState必重新build,产生新 Widget;canUpdate+Key 尽量复用 Element,仅换其 widget 字段;RenderObject 是否markNeedsLayout取决于约束是否变化,仅颜色变可能只 repaint。优化核心是缩小 Stateful 范围与稳定子 Widget 引用。
#16 GlobalKey 的使用场景与代价? 💡
标准回答
GlobalKey 全局唯一,可 currentState 调方法、currentContext 取位置、findRenderObject 测尺寸。适用于 Form 校验、跨子树访问 State、截图测量。代价:移动子树时无法按类型复用 Element,破坏局部性,列表滥用致性能差;能不用则改用回调、Controller 或状态上提。
面试官可能继续追问
-
Android 里用 GlobalKey 做啥常见?
获取子组件高度做滚动定位、全局
FormState.validate()提交前校验。 -
和
ValueKey能同时用吗?同一 Widget 只有一个 Key;GlobalKey 已含身份语义,慎嵌套。
#17 Element 的 mount、update、unmount 过程? 💡
标准回答
mount :inflateWidget 创建 Element 挂到父,递归子 Widget,创建 RenderObject 插入渲染树。update :canUpdate 为真则更新 Widget 引用并 updateChildren diff。unmount :deactivate 离树,无重新挂载则 unmount 销毁 Element 与 RenderObject。GlobalKey 子树搬家时会 deactivate 再 mount 到新父。
面试官可能继续追问
-
Android 混合栈 Navigator 弹路由时?
对应子树 unmount,
State.dispose应释放原生监听。 -
update 会一定重建 RenderObject 吗?
不一定;RenderObjectWidget 可
updateRenderObject仅改绘制属性。
#18 Flutter Widget 体系与 Jetpack Compose 有何异同? 💡
标准回答
同 :声明式、函数/类描述 UI、状态驱动重建、子树局部更新。异 :Compose 靠 Kotlin 编译器插桩与 Recomposer;Flutter 靠三棵树运行时 diff。Compose State 在运行时槽位;Flutter 状态在 State 对象或外部容器。Compose 与 Android 视图互操作成熟;Flutter 自绘引擎跨平台一致性高。
面试官可能继续追问
-
Android 团队选型 Compose 还是 Flutter?
纯 Android 深集成选 Compose;要 iOS/Web 一致 UI 选 Flutter;可混合共存不同模块。
-
重组和
setState最大差别?Compose 细粒度 Skipping;Flutter 以 Element 为粒度,Key/GlobalKey 影响复用策略。
#19 Theme 与 MediaQuery 如何向下传递? ⭐
标准回答
MaterialApp/CupertinoApp 根部注入 Theme、MediaQuery 等 InheritedWidget。子节点 Theme.of(context) 向上查找最近祖先;copyWith 局部覆盖形成子主题。MediaQuery 提供尺寸、padding、textScale、平台亮度等;MediaQuery.removePadding 可局部忽略。切换主题调用 setState 或状态管理更新根 ThemeData 即可向下广播。
面试官可能继续追问
-
Android 深色模式跟系统怎么同步?
MaterialApp.themeMode: ThemeMode.system,或 Channel 读原生UiMode再切。 -
textScale Accessibility 怎么处理?
读
MediaQuery.textScaler,布局用弹性约束防溢出,勿硬编码字号。
#20 如何优化 Flutter 列表滑动性能? 🔥
标准回答
构建层 :用 ListView.builder 而非 ListView,按需构建可见项;item Widget 尽量 const 减少重建。复用层 :提供稳定 ValueKey 用业务 id,确保 Element 正确复用。状态层 :避免 item 内 setState 触发整列表重建,状态上提或使用细粒度状态管理。渲染层 :避免复杂 Clip/Opacity/ShaderMask,图片用 cacheWidth/cacheHeight 控制解码尺寸。监测用 DevTools Performance 与 Timeline。
面试官可能继续追问
-
Android RecyclerView 优化经验能平移吗?
能;ViewHolder 复用思想对应
ListView.builder的 item 构建+Element 复用;避免层级嵌套对应减少 Widget 层级。 -
RepaintBoundary怎么用?包裹独立重绘区域(如滚动列表中的动画项),使其 repaint 不波及父级。用
RenderRepaintBoundary.debugPaintBoundsEnabled可视化边界。

面试策略速查
| 面试等级 | 建议掌握 |
|---|---|
| 初级 | ★★★☆☆ |
| 中级 | ★★★★☆ |
| 高级 | ★★★★★ |
必背知识(🔥)
一切皆 Widget、Stateless/Stateful、setState、三棵树职责、Widget 不可变、Key 基础、State 生命周期、setState→渲染链路、列表性能优化
高频追问
「三棵树区别」「为什么 Widget 不可变」「列表为什么要 Key」「GlobalKey 缺点」「和 Compose 区别」「build 能放副作用吗」「列表怎么优化」
加分项(💡)
Element mount/update/unmount、InheritedWidget 依赖机制、didChangeDependencies 触发时机、RepaintBoundary 使用
容易踩坑
build 里做 IO;列表无 Key 状态错乱;滥用 GlobalKey;dispose 后 setState;长期持有 BuildContext;以为 setState 同步重绘;列表用 index 做 Key
完整链路一句通
Widget 体系一条线 :runApp 挂根 Widget → build 描述不可变配置 → Element updateChild diff 复用 → RenderObject layout/paint → 引擎合成上屏;状态变经 setState 标记 dirty → 最小范围重建配置 → 能复用则不重建 RenderObject;列表场景通过 builder+const+稳定 Key 保证滑动流畅。