原文链接:Passing arguments to your requests | Riverpod
pub:riverpod | Dart Package (flutter-io.cn)
译时版本: 2.4.9
之前翻译过 Riverpod 的官方文档,现在随着版本更新,官方文档又多了很多新内容,所以再补充翻译一下。
之前翻译过的内容,现在官方文档有中文了。
Flutter状态管理库Riverpod官方文档翻译汇总 - 掘金 (juejin.cn)
向请求传递参数
在前面的文章中,看到了如何定义 "provider" 创建简单的 GET HTTP 请求。
不过通常 HTTP 请求需要外部参数。
例如,前面使用了 Bored API 给用户一个随机 activity 。 但是用户可能想要过滤 activity 的类型或确认所需价格等。。。
这些参数不可能提前预知。所以需要从 UI 向 provider 传递这些参数。
修改 provider 以接收参数
提醒一下,前面如下定义了 provider :
dart
// "函数式" provider
@riverpod
Future<Activity> activity(ActivityRef ref) async {
// TODO: 执行网络请求获取 activity
return fetchActivity();
}
// 或 替代方案,"notifier"
@riverpod
class ActivityNotifier2 extends _$ActivityNotifier2 {
/// Notifier 参数在 build 方法里指定。
/// 可以指定任意多个,使用任意名字,甚至是可选/被命名的。
@override
Future<Activity> build(String activityType) async {
// 参数也能用于 "this.<argumentName>"
print(this.activityType);
// TODO: 执行网络请求获取 activity
return fetchActivity();
}
}
要传递参数给 provider ,只需为被注解的函数添加参数即可。
例如,将 provider 改为接收一个 String
参数,该参数对应所需的 activity 的类型:
dart
@riverpod
Future<Activity> activity(
ActivityRef ref,
// 向 provider 添加参数。
// 参数的类型可任意。
String activityType,
) async {
// 这里就能使用 "activityType" 来构建 URL 。
// 它会指向 "https://boredapi.com/api/activity?type=<activityType>"
final response = await http.get(
Uri(
scheme: 'https',
host: 'boredapi.com',
path: '/api/activity',
// 无需手动编码查询参数,"URI" 类会自动处理。
queryParameters: {'type': activityType},
),
);
final json = jsonDecode(response.body) as Map<String, dynamic>;
return Activity.fromJson(json);
}
警告
向 provider 传递参数时,非常建议将启用 provider 的 "autoDispose" 。
"autoDispose" 失败的话可能会导致内存泄漏。
查看 清空缓存和响应状态清理 了解更多详细内容。
修改 UI 以传递参数
前面组件是如下消费 provider :
dart
AsyncValue<Activity> activity = ref.watch(activityProvider);
但是现在 provider 要接收参数,所以消费它的语法略有不同。
provider 现在是函数,需要用请求的参数调用。
可以如下修改 UI 以传递一个硬编码的 activity 类型:
dart
AsyncValue<Activity> activity = ref.watch(
// provider 现在是函数,接收 activity 类型。
// 简单起见,现在传递一个常量字符串。
activityProvider('recreational'),
);
传递给 provider 的参数对应着除 "ref" 之外被注解的函数的参数。
信息
同时监听带有不同参数值的同一个 provider 是完全可能的。 例如,UI 可以同时渲染 "recreational" 和 "cooking" 的 activity 。
dart
return Consumer(
builder: (context, ref, child) {
final recreational = ref.watch(activityProvider('recreational'));
final cooking = ref.watch(activityProvider('cooking'));
// 可以同时渲染 activity 。
// 两个请求会并行发生并能被正确地缓存。
return Column(
children: [
Text(recreational.valueOrNull?.activity ?? ''),
Text(cooking.valueOrNull?.activity ?? ''),
],
);
},
);
缓存考量和参数约束
向 provider 传递参数时,计算也会缓存。区别是计算会为每个参数值缓存。
这意味着如果两个组件消费相同参数值的同一个 provider ,只会发送单次网络请求。
但是如果两个组件消费不同参数值的同一个 provider ,则会发送两次网络请求。
这种实现,Riverpod 是依靠参数的 ==
操作符。
因此,向 provider 传递的参数保持完全等同是很重要的。
警告
一个常见错误是直接初始化一个新对象作为 provider 的参数,但该对象没有覆写
==
。 例如,可能想如下传递一个List
(列表):
csharp// 可以修改 activityProvider 接收一个字符串列表。 // 然后在直接 watch 调用里直接创建列表。 ref.watch(activityProvider(['recreational', 'cooking']));
该代码的问题是
['recreational', 'cooking'] == ['recreational', 'cooking']
是false
的。因此,Riverpod 会认为两个参数值是不同的,就会尝试发送新的网络请求。这会导致网络请求的无限循环,会一直向用户显示进程指示器(转圈圈)。
要修改该问题,应该使用
const
列表(const ['recreational', 'cooking']
),或者使用覆写了==
操作符的自定义列表。想要及时发现该错误,建议使用 riverpod_lint 和开启 provider_parameters lint 规则。然后,前面的代码片段就会提示警告。查看 开始 了解安装步骤。