跳转到内容

Flutter 中的服务端状态

服务端数据需要的是缓存,而不是另一个状态类:缓存为每个界面提供同一份数据,跟踪加载和错误,并刷新已经过时的数据。

Flutter 应用显示的大部分内容来自服务器:待办事项列表、个人资料、一页搜索结果。这些数据的表现与应用自己拥有的状态不同:

  • 应用并不拥有它。它在服务器上变化,不会通知应用。
  • 它是共享的。多个界面显示同一个列表,它们应该保持一致。
  • 它来得晚,或者根本不来。每次读取都有加载状态和错误状态。
  • 它会过时。1 分钟前获取的列表,现在可能已经不对了。

应用状态(例如选中了哪个标签页)没有这些问题:

应用状态 服务端数据
谁更改它 你的代码 服务器,随时都可能
谁掌握真实数据 应用 别处
何时到达 立即 稍后,或者永远不到
是否会过时 否 是

如果把服务端数据和应用状态放在一起,就要自己编写缓存:仓库中的缓存、每个界面的加载标志、刷新方法、每次写入后的失效处理,以及两个界面同时请求时的规则。

Fuery 把服务端数据按键保存在一个缓存中:

final todosQuery = Query(
queryKey: ['todos'],
queryFn: (_) => api.getTodos(),
);

有了这一个定义:

  • 每个键一份数据。使用 ['todos'] 的所有界面读取同一个缓存条目(CachedQuery)。
  • 同一时间只有一个请求。在获取进行中请求数据的界面共享这个请求。
  • 先新鲜,后过期。数据在 staleTime(默认值:0,因此数据一到达就过期)内保持新鲜。
  • 旧数据留在屏幕上。Fuery 在后台重新获取时,界面继续显示旧数据。
  • 自动重新获取。界面开始使用过期数据时,以及应用回到前台时,Fuery 重新获取这些数据。接入网络状态来源后,Fuery 还会在网络重新连接时重新获取(网络重新连接时)。
  • 写入后重新获取。变更后使 ['todos'] 失效,就会重新获取屏幕上的列表。
  • 加载和错误随数据一起提供。结果带有 status、error 以及 isRefetching 等标志,因此界面直接读取它们,无须自己跟踪。
  • 未使用的数据从内存中移除。没有界面使用数据时,Fuery 在垃圾回收时间(gcTime,默认值:5 分钟)过后移除它。持久化的数据仍保留在设备上。

查询生命周期列出缓存数据的每个阶段,以及结束每个阶段的条件。

缓存是一个普通的 Dart 对象,不属于 widget 树。同一个查询可以用在三个地方:

  • QueryBuilder 渲染它;
  • cubit 监听 todosQuery.observe() 的 stream;
  • 脚本用 await 等待 client.query(todosQuery)。

Fuery 不会取代你已经在用的状态管理方案。Bloc 和 cubit 展示如何在 cubit 和 bloc 中使用查询。

  • 缓存的工作原理:定义、客户端、观察者和缓存数据的生命周期。
  • 快速开始:安装 Fuery,缓存第一个请求。
  • 查询:键、新鲜度和各个选项。
  • 试用演示:在浏览器中运行示例应用,附带开发者工具。