用于获取、缓存和失效异步数据的 Flutter 库

快速特性

  • 获取异步数据
  • 数据失效
  • 乐观响应
  • 重置缓存

动机

如何在 Flutter 中进行 API 调用?可能大多数人会回答使用 Dio
但真正的问题是,如何将 API 调用无缝集成到 Flutter 架构中?
一种方法是使用 FutureBuilder
或者像社区中的大多数人那样使用 Bloc

关键是,FutureBuilder 和 Bloc 都有缺陷。
例如,FutureBuilder 太简单了。它提供了查询的状态,但除了当前屏幕的范围之外,无法与某些 FutureBuilder 进行通信。
Bloc 的问题在于,对于简单的 API 调用会包含大量的样板代码,但同样,从其他屏幕也无法与 Bloc 进行通信。Bloc 应该
是处理复杂工作流程的首选,但如果您没有任何复杂的业务逻辑怎么办?

flutter_requery 来帮忙!

示例

让我们从这个简单的例子开始。

/// define data and cache key
final List<String> data = ["Hello"];
final String cacheKey = 'strKey';

/// simulates API call, therefore response is delayed
Future<List<String>> _getData() async {
  await Future.delayed(Duration(seconds: 1));
  return data;
}

/// later, used in the build method
Query<List<String>>(
  cacheKey,
  future: _getData,
  builder: (context, response) {
    if (response.error != null) {
      return Text('Error');
    }
    if (response.loading) {
      return Text('Loading...');
    }
    final children = response.data.map((str) => Text(str)).toList();
    return ListView(
      children: children
    );
  },
);

/// and later when you want to invalidate your data
void _onPress() async {
  await Future.delayed(Duration(seconds: 1));
  data.add("World");
  queryCache.invalidateQueries(cacheKey);
}

这就是它的要点。您可以在 pub.dev 上找到完整的示例。

用法

缓存键

每个查询都需要一个缓存键。
您的数据将在此键下存储在缓存中。
它可以是字符串、整数或字符串和整数的列表。

/// good
const k1 = 'myKey';
const k2 = 1;
const k3 = ["data", 1]

/// bad
const k1 = 1.0;
const k2 = true;
const k3 = ["data", true];

将键指定为列表是为了让您能够更智能地失效查询。
有关更多详细信息,请参阅 失效章节。

查询

定义了缓存键后,下一步是编写查询。
查询接受 3 个参数

  • cacheKey – 有关详细信息,请参阅 此处
  • future – 将要执行的异步操作。
  • builder – 遵循 Flutter 构建器模式。第一个参数是 BuildContext,后面跟着 QueryResponse 对象。

QueryResponse 管理查询状态。它还有 3 个属性

  • data – 从 future 收到的响应,如果发生异常则为 null。
  • loading – 布尔值,如果查询正在运行则为 true。否则为 false。
  • error – 表示从 future 收到的错误。如果 future 成功,error 将为 null。

// use Query widget in the build method
Query<List<String>>(
  'myCacheKey',
  future: ()async {
    await Future.delayed(Duration(seconds: 1));
    return ["Hello"]
  }
  builder: (context, QueryResponse response) {
    /// error state
    if (response.error != null) {
      return Text('Error');
    }
    /// loading state
    if (response.loading) {
      return Text('Loading...');
    }
    final children = response.data.map((str) => Text(str)).toList();
    return ListView(
      children: children
    );
  },
);

失效

数据失效可以有两种不同的形式。
您可以等待 API 响应,或者您只需尽快显示最新数据。
如果您对此感兴趣,请查看 下一章节。

等待 API 响应更为常见,并且 **flutter_requery** 通过使用 queryCache 实例支持这一点。
它是全局的,并且已由库定义。通过传递缓存键来失效您的查询。

// invalidates strKey query
queryCache.invalidateQueries('strKey');

// support for bulk invalidation
queryCache.invalidateQueries(['strKey1', 'strKey2']);

// if your keys are lists, end result would be similar to
queryCache.invalidateQueries([
  ['strKey', 1],
  ['strKey2', 2]
]);

一旦查询被失效,所有订阅该查询的 Query 小部件
将再次执行 future 并使用新数据重建其子项。

对于缓存级别失效,请使用

// invalidate every query stored in cache
queryCache.invalidateAll()

失效与定义为列表的键配对使用。
定义为列表的缓存键必须以分层方式查看,其中定义的列表元素是后续元素的祖先。
例如

// requests is ancestor of 1
const key1 = ["requests", 1]

这样做的原因是支持分层失效。
有时管理失效会变得很麻烦,因此开发者可以决定巧妙地命名键以支持这一点。
例如

const k1 = ["requests", 1]
const k2 = ["requests", 2]
const k3 = "requests"

// without hierarchical invalidation you need to call
queryCache.invalidateQueries([
  ["requests", 1], ["requests", 2], "requests"
]);

// but with hierarchical invalidation you only need to call
queryCache.invalidateQueries("requests");

乐观响应

有时等待 API 响应可用时间过长。
因此,您可以通过使用乐观响应立即更新缓存数据并重建小部件树。
请确保在 API 调用前移除 await 关键字,因为这会阻塞线程。

  queryCache.setOptimistic("requests", [...oldData, newData]);

重置

简而言之,重置可以解释为缓存级别失效而不重建小部件树。
此外,异步操作不会立即运行,而只会在新 Query 小部件挂载时或 cacheKey 更改时运行。
这对于注销操作特别有用。

queryCache.reset();

总结

API 描述
查询 用于数据获取操作的 Flutter 小部件。
queryCache.invalidateQueries 失效由缓存键指定的查询并重建小部件树。
queryCache.invalidateAll 失效缓存中的所有查询并重建小部件树。
queryCache.setOptimistic 手动设置缓存数据并重建小部件树。
queryCache.reset 失效缓存中的所有查询而不重建小部件树。

GitHub

查看 Github