【LiveStates 04】不仅是状态管理:解锁 Recoverable/Refreshable 工业级特性
【LiveStates 01】别再手动 watch 了:开启 Flutter “自动追踪” DX 革命
【LiveStates 02】Zones 不止于异常捕获:揭秘 LiveStates 自动追踪黑科技
【LiveStates 03】拒绝无效重绘:利用 LiveCompute 实现手术刀级 UI 刷新
在完成了“自动追踪”和“计算属性”这两大核心性能支柱后,我们往往会面临一个更棘手的挑战:如何让一个状态管理库从“玩具”变成“工业级工具”?
在真实的生产环境下,我们总会遇到一些边缘但致命的场景:
- 用户在填一个很长的表单,跳转到隐私协议页再回来,发现刚填的数据全没了。
- 一个页面嵌套了五个子模块,每个模块都有自己的接口,下拉刷新时如何优雅地让它们按序全部重载?
今天我们聊聊 live_states 是如何通过 Recoverable 和 Refreshable 这两个高级特性,去解决这些业务里的“牛皮癣”问题的。
1. 消失的数据:为何 state 不够用?
在 Flutter 中,State 的生命周期是绑定在 Element 上的。这意味着当一个 Widget 从树中移除(比如页面跳转、或者 IndexedStack 切换),它的状态就极有可能被销毁。
虽然你可以用 AutomaticKeepAliveClientMixin,但那更像是一种“强行保命”,会增加内存压力。
live_states 的方案:Recoverable(状态恢复)
它借鉴了 Android 原生的 onSaveInstanceState 思想。通过在 LiveViewModel 中混入 Recoverable,你可以让状态“原地复活”。
class SearchVM extends LiveViewModel with Recoverable {
// 唯一的 Key,决定了状态存哪
@override
String get storageKey => 'user_search_history';
// 1. 存什么
@override
Map<String, dynamic>? storage() => {'query': query.value};
// 2. 怎么回
@override
void recover(Map<String, dynamic>? storage) {
if (storage != null) {
query.value = storage['query'];
}
}
}
底层逻辑: live_states 内部维护了一棵 VM 逻辑树(基于 AdvancedNode)。即便 Widget 暂时离开了渲染树,只要你的 VM 树结构还在,状态就会在后台被自动序列化和恢复。这对于处理复杂表单、搜索缓存或者长列表的滚动位置来说,是真正的救命稻草。
2. 刷新地狱:如何优雅地“一呼百应”?
想象一下,你正在做一个复杂的仪表盘页面,里面有“实时水位”、“设备状态”、“报警记录”三个子模块,每个模块都有自己的异步加载逻辑。
产品经理说:“我要给整页加个下拉刷新。”
传统做法里,你可能得在父组件里拿到三个子组件的引用(或者 Controller),然后挨个调他们的 fetchData()。如果层级再深一点,这简直是代码噩梦。
live_states 的方案:Refreshable(级联刷新)
因为 live_states 的 ViewModel 是有父子关系的,我们实现了一种类似“事件冒泡”反过来的**“任务下发”**机制。
class DashboardVM extends LiveViewModel with Refreshable {
@override
Future<bool> onRefresh() async {
await fetchGlobalConfig();
return true;
}
// 只要在顶层调一下 refresh(),整棵树上所有的异步节点都会被触发
void userPullToRefresh() => refresh();
}
当你调用 refresh() 时,live_states 会顺着 VM 树向下递归。所有混入了 Refreshable 的子节点,都会自动触发它们的 onRefresh。
这不仅是代码量的减少,更是业务逻辑的彻底解耦。 父模块不需要知道子模块具体怎么加载数据,它只需要发出一声“刷新”的号令,整棵树就会协同工作。
3. 树的魔力:AdvancedNode
你可能会好奇,这些高级特性是怎么知道“谁是谁的儿子”的?
秘诀就在 AdvancedNode 这个 Mixin。在 LiveViewModel 初始化时,它会通过 context 向上寻找最近的 LiveElement,从而建立起一棵逻辑上的 VM 树。
这棵树独立于 Widget 树,但又与 Widget 树的生命周期同步。它像是一套无形的骨架,支撑起了状态流转、节点寻找以及资源回收。
4. 给开发者的思考:为什么要写这么多“多余”的代码?
有的同学可能会说:“我手动存一下 SharedPreference 不也一样吗?”
当然不一样。
工业级开发的本质是确定性。手动存储意味着你需要处理 IO 异常、处理 key 冲突、处理生命周期不匹配导致的脏数据。
而 live_states 的这些特性,是框架级的保证。它把这些复杂的、容易出错的操作抽象成了声明式的 Mixin。你只需要告诉框架“我要恢复这个 key”,剩下的脏活累活——什么时候存、什么时候读、怎么处理节点解绑——全部由框架的底层逻辑闭环处理。
结语
一个好的状态管理库,不应该只在 UI 刷新上快,更应该在业务处理上“稳”。
Recoverable 和 Refreshable 或许不是你每天都会用到的功能,但当你在生产环境下遇到那些棘手的、需要跨层级协同或持久化状态的需求时,你会庆幸你的框架已经为你打好了底子。
这,就是我们追求的“工业级”稳定性。
系列终结预告:
下一篇《【LiveStates 05】实战指南:手把手带你用 LiveStates 构建高性能生产级页面》,我们将进行一次全方位的“大总结”,并提供一份 live_states 的最佳实践指南。