用于获取、缓存和失效异步数据的 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 | 失效缓存中的所有查询而不重建小部件树。 |