Appearance
状态管理、路由、架构
一句话定位
这一模块不把"状态管理库 + 路由库"平铺成选型清单,而是按一条状态流转主线建立判断力:
状态放哪层 → 状态怎么变(单向数据流)→ 状态怎么跨页面共享 → 状态怎么跨进程存活 → 页面怎么导航与恢复。
读完后,应该能先判断"某个状态该放 UI 层、业务层还是持久层",再决定用哪个库、走哪条恢复路径。
模块目标
建立 Compose / Flutter / Vue 在状态管理、路由、页面恢复、业务状态分层上的对照心智。
模块成熟度与当前优化方向
当前模块可视为:L2 为主,局部主题接近 L3。
- 已有:状态分层、恢复态、go_router、SavedStateHandle、Vue 对照等主线知识树
- 缺口:最容易出线上事故的“状态样板”还不够集中,容易看完概念仍不会落地
所以本模块后续不应继续扩成“状态管理百科”,而应收敛为 状态事故样板库。
先看 4 类最常见状态事故
例子 1:提交按钮为什么会重复发单?
- 错误写法:
isSubmitting、接口结果、Toast 事件全塞进一个持久 state,对页面重建和返回恢复没有边界 - 常见后果:
- 旋转屏幕后按钮重新可点
- 返回页面后二次点击重复提交
- 成功 Toast 因重建被重复消费
- 正确拆法:
- UI state:按钮 loading / 输入校验提示
- 业务 state:订单创建结果 / 提交中的请求标识
- one-shot event:Toast、导航、支付拉起结果
例子 2:列表页为什么经常“回到顶部 + 条件丢失 + 重刷一遍”?
- 错误写法:把筛选条件、分页游标、滚动位置都当临时内存态,页面重建就重置
- 正确拆法:
- 筛选条件:恢复态或 URL query
- 分页游标:业务 state / ViewModel
- 滚动位置:局部恢复态 / keep-alive / rememberSaveable
这个例子最适合用来串起 Flutter、Compose、Vue 三端的状态分层对照。
本模块要回答的问题
- UI state 和业务 state 到底怎么分?
- 一句话答:UI state 是页面渲染所需(loading / error / 选中项),业务 state 是跨页面事实(登录态 / 购物车);边界决定状态放哪层管理。
- one-shot event 为什么总是容易写乱?
- 一句话答:事件是一次性脉冲,塞进持久的 state 会被重建或重放(重复消费);需要独立事件通道表达,而不是当状态存。
- Flutter 为什么不能只会 setState?
- 一句话答:
setState只是局部 UI 重建手段,跨页面 / 跨组件共享状态需要状态提升与依赖注入来管理。
- 一句话答:
- Riverpod / Provider / Bloc / GetX 各自适合什么?
- 一句话答:Provider 轻量依赖注入;Riverpod 编译期安全 + 组合能力强;Bloc 事件驱动适合中大型项目;GetX 简洁但易滥用全局状态,按团队规模与可控性选。
- Vue 的 Pinia / vue-router 和 Flutter / Compose 的对应关系是什么?
- 一句话答:Pinia 对应全局状态仓库(Flutter 的 Riverpod / Provider、Compose 的 State 层),vue-router 对应导航栈(Flutter 的 Navigator / go_router、Compose 的 Navigation)。
知识导航树:从状态归属到页面恢复
第一层:状态归属(先判断状态放哪层)
- UI state:页面渲染所需,放 Composable / Widget / 组件内(
remember/StatefulWidget) - 业务 state:跨页面事实,放 ViewModel / Riverpod / Pinia
- 恢复态:跨配置变更 / 进程恢复的轻量锚点,放 SavedStateHandle / go_router state
- 持久态:跨启动存活的核心数据,放 DataStore / Room / 本地库
先读:Compose / Flutter / Vue 状态管理对照
第二层:状态流转(状态怎么变)
- Compose 单向数据流:State 下放 + Event 上抛
- Flutter:
setState局部重建,跨组件靠状态提升 + 注入 - 选型:Provider / Riverpod / Bloc / GetX 的约束强度差异
再读:Riverpod / Provider / Bloc / GetX 对比 · Jetpack Compose 状态模型、StateFlow 与重组优化 · Koin 依赖注入与 4.0 迁移 · Jetpack Compose 嵌套滚动与滚动协同 · Jetpack Compose 文本测量、自定义截断与“展开更多”
第三层:生命周期与恢复(状态怎么存活)
- ViewModel / SavedStateHandle:跨旋转保留 vs 跨进程恢复的边界
- 状态三分层:内存态 / 恢复态 / 持久态
再读:ViewModel 架构、SavedStateHandle 与进程被杀恢复 · 状态持久化、内存恢复与分层恢复策略
第四层:路由与深链(页面怎么导航)
- Flutter:Navigator 1.0 vs go_router(Navigator 2.0 声明式)
- 深链 / App Link / 合成返回栈
最后读:Flutter 路由:go_router / Navigator 体系 · 深链 (Deep Link)、App Link 与合成返回栈治理
推荐阅读路径:按问题而不是按文件名走
路径 A:页面状态一旋转就丢
- Jetpack Compose 状态模型、StateFlow 与重组优化(
remembervsrememberSaveable) - Jetpack Compose 文本测量、自定义截断与“展开更多”(展开更多 / 状态提升 / 文本测量)
- ViewModel 架构、SavedStateHandle 与进程被杀恢复(ViewModel / SavedStateHandle)
- 状态持久化、内存恢复与分层恢复策略(三分层)
路径 B:Flutter 状态管理怎么选型
路径 C:深链唤起后返回体验差
- 深链 (Deep Link)、App Link 与合成返回栈治理(App Link + 合成返回栈)
- Flutter 路由:go_router / Navigator 体系(go_router 统一治理)
路径 D:要直接看生产级落地方案
- 2026 生产级参考架构指南:Android (Compose/XML) 与 Flutter 最优解(Android Compose + Flutter 最优解)
- Koin 依赖注入与 4.0 迁移(依赖注入与架构装配:Koin 4.0 / Kotlin 2.0+ 迁移)
路径 E:滚动联动、折叠头部、BottomSheet 为什么总写乱
- Jetpack Compose 嵌套滚动与滚动协同(滚动位移消费链)
- Jetpack Compose 状态模型、StateFlow 与重组优化(高频状态读取优化)
- Flutter 路由:go_router / Navigator 体系(Flutter 路由/页面栈对照)
导航总表
| 导航层 | 先回答什么 | 对应文档 |
|---|---|---|
| 状态归属 | 状态该放 UI / 业务 / 恢复 / 持久哪一层 | Compose / Flutter / Vue 状态管理对照 |
| 状态流转 | 状态怎么变、选哪个库、约束多强 | Riverpod / Provider / Bloc / GetX 对比 · Jetpack Compose 状态模型、StateFlow 与重组优化 |
| 架构装配与依赖注入 | 业务层与 ViewModel 如何解耦注入、Kotlin 2.0+ 迁移 | Koin 依赖注入与 4.0 迁移 |
| 文本与局部交互状态 | 文本怎么精确截断、展开状态放哪层 | Jetpack Compose 文本测量、自定义截断与“展开更多” |
| 生命周期与恢复 | 跨旋转保留 vs 跨进程恢复的边界 | ViewModel 架构、SavedStateHandle 与进程被杀恢复 · 状态持久化、内存恢复与分层恢复策略 |
| 路由与深链 | 页面怎么导航、深链怎么治理返回栈 | Flutter 路由:go_router / Navigator 体系 · 深链 (Deep Link)、App Link 与合成返回栈治理 |
| 滚动协同 | 父子滚动位移如何协商消费 | Jetpack Compose 嵌套滚动与滚动协同 |
推荐复习顺序
- 先读状态管理对照建立"状态分层"心智
- 再进 ViewModel / SavedStateHandle 与 Compose 状态模型(Android 主线)
- 再进 Riverpod 选型 与 Koin 依赖注入
- 再进 go_router 路由(Flutter 路由与深度链接)
- 再用 文本测量与展开更多 和 Compose 嵌套滚动 补两个高频真实 UI 题
- 最后用 Vue 对照 与 深链治理 收口跨端
对应 labs 入口
| 类型 | 已落地入口 | 覆盖的事故样板 |
|---|---|---|
| Android 恢复态基础 | viewmodel-savedstate-demo | ViewModel 抗旋转 + SavedStateHandle 抗进程死亡的基础边界 |
| Android 恢复态样板 | state-persistence-demo | 筛选条件 / 分页游标 / 草稿正文三分层恢复 |
| Android one-shot event 样板 | one-shot-event-demo | Toast / 导航 / 提交结果只消费一次,避免重建后重放 |
| Android 深链返回栈样板 | deeplink-navigation-demo | Deep Link 命中详情页后的合成返回栈治理 |
| Android / Flutter 页面返回结果样板 | page-result-demo (Android) · page-result-demo (Flutter) | Activity Result / Navigator.pop(result) 一次性回传,对照 SavedStateHandle 边界 |
| Android 高级 UI 状态样板 | android-nested-scroll-coordination-demo · android-expandable-text-demo | Compose / View 的滚动协同、文本测量、状态提升与复用边界 |
| Flutter 路由基础 | flutter-routing-basic · flutter-routing-go-router | Navigator 基础与 go_router 路由声明式治理 |
| Flutter 高级 UI 状态样板 | flutter-nested-scroll-coordination · flutter-expandable-text-demo | Sliver / NestedScrollView 协同、TextPainter 测量与列表展开状态管理 |
viewmodel-savedstate-demo负责“ViewModel 抗旋转 + SavedStateHandle 抗进程死亡”的基础边界;state-persistence-demo负责“列表页恢复态”这类真实事故样板,重点放在筛选条件、分页游标、滚动位置的分层恢复,避免两者叙事重叠。
当前最值得优先补的状态样板
- 表单提交态样板:本轮已用 one-shot-event-demo 落下“副作用单独走事件通道”的最小版本;更完整的表单校验 / 防重复提交链路仍待补。
- 列表页恢复态样板:本轮已用 state-persistence-demo 落下筛选条件 / 分页游标 / 草稿正文三分层。
- 页面返回结果样板:本轮已用 page-result-demo (Android) · page-result-demo (Flutter) 落下
setResult/EXTRA_RESULT与Navigator.pop(result)的最小回传链路,并注明SavedStateHandle恢复态边界。 - Compose / Android View 高级 UI 状态样板:已落地 android-nested-scroll-coordination-demo 与 android-expandable-text-demo。
- Flutter 高级 UI 状态样板:已落地 flutter-nested-scroll-coordination 与 flutter-expandable-text-demo。
- one-shot event 样板:已落地 one-shot-event-demo。
复习检查题
为什么状态要分"UI / 业务 / 恢复 / 持久"四层管理?
答:因为四类状态的存活边界不同:UI 态随组件重建、业务态随页面作用域、恢复态抗进程死亡、持久态跨启动存活。混层存放会导致"旋转丢状态""进程被杀丢数据"这类线上事故。
remember和rememberSaveable的关键区别是什么?答:
remember只在重组期间保留,配置变更/进程死亡即失;rememberSaveable额外走 Bundle 序列化,能跨配置变更与进程重建恢复轻量状态。为什么 ViewModel 能跨旋转存活、却不能跨进程存活?
答:ViewModel 存储在进程内(ViewModelStore),旋转只是重建 Activity 但进程还在;进程被杀后 ViewModelStore 一并销毁,只有 SavedStateHandle(经 Bundle 暂存)与持久层能恢复。
go_router 相比 Navigator 1.0 的核心优势是什么?
答:基于 Navigator 2.0 的声明式路由,URL 即状态,天然支持深链统一解析、路径参数、重定向与状态恢复,适合中大型 Flutter 应用治理导航。
备注
这个模块应尽量通过最小 demo 形成对照,不要只停留在概念表述。后续新增内容,优先围绕“高频状态事故”补样板,而不是再平铺很多库的概念页。