【LiveStates 04】不仅是状态管理:解锁 Recoverable/Refreshable 工业级特性

9 阅读4分钟

【LiveStates 04】不仅是状态管理:解锁 Recoverable/Refreshable 工业级特性

【LiveStates 01】别再手动 watch 了:开启 Flutter “自动追踪” DX 革命

【LiveStates 02】Zones 不止于异常捕获:揭秘 LiveStates 自动追踪黑科技

【LiveStates 03】拒绝无效重绘:利用 LiveCompute 实现手术刀级 UI 刷新

在完成了“自动追踪”和“计算属性”这两大核心性能支柱后,我们往往会面临一个更棘手的挑战:如何让一个状态管理库从“玩具”变成“工业级工具”?

在真实的生产环境下,我们总会遇到一些边缘但致命的场景:

  1. 用户在填一个很长的表单,跳转到隐私协议页再回来,发现刚填的数据全没了。
  2. 一个页面嵌套了五个子模块,每个模块都有自己的接口,下拉刷新时如何优雅地让它们按序全部重载?

今天我们聊聊 live_states 是如何通过 RecoverableRefreshable 这两个高级特性,去解决这些业务里的“牛皮癣”问题的。

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 刷新上快,更应该在业务处理上“稳”。

RecoverableRefreshable 或许不是你每天都会用到的功能,但当你在生产环境下遇到那些棘手的、需要跨层级协同或持久化状态的需求时,你会庆幸你的框架已经为你打好了底子。

这,就是我们追求的“工业级”稳定性。


系列终结预告: 下一篇《【LiveStates 05】实战指南:手把手带你用 LiveStates 构建高性能生产级页面》,我们将进行一次全方位的“大总结”,并提供一份 live_states最佳实践指南