likes
comments
collection
share

[译][官方文档] Flutter/Dart 状态管理库 Riverpod - 概要 - 向请求传递参数

作者站长头像
站长
· 阅读数 37

!!!译文为作者本人人肉翻译~转载请注明出处!!!


原文链接:Passing arguments to your requests | Riverpod

pub:riverpod | Dart Package (flutter-io.cn)

译时版本: 2.4.9



向请求传递参数

在前面的文章中,看到了如何定义 "provider" 创建简单的 GET HTTP 请求。 不过通常 HTTP 请求需要外部参数。

例如,前面使用了 Bored API 给用户一个随机 activity 。 但是用户可能想要过滤 activity 的类型或确认所需价格等。。。

这些参数不可能提前预知。所以需要从 UI 向 provider 传递这些参数。

修改 provider 以接收参数

提醒一下,前面如下定义了 provider :

// "函数式" 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 的类型:

@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 :

AsyncValue<Activity> activity = ref.watch(activityProvider);

但是现在 provider 要接收参数,所以消费它的语法略有不同。 provider 现在是函数,需要用请求的参数调用。 可以如下修改 UI 以传递一个硬编码的 activity 类型:

AsyncValue<Activity> activity = ref.watch(
  // provider 现在是函数,接收 activity 类型。
  // 简单起见,现在传递一个常量字符串。
  activityProvider('recreational'),
);

传递给 provider 的参数对应着除 "ref" 之外被注解的函数的参数。

信息

同时监听带有不同参数值的同一个 provider 是完全可能的。 例如,UI 可以同时渲染 "recreational"  "cooking" 的 activity 。

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 (列表):

// 可以修改 activityProvider 接收一个字符串列表。  
// 然后在直接 watch 调用里直接创建列表。  
ref.watch(activityProvider(['recreational', 'cooking']));

该代码的问题是 ['recreational', 'cooking'] == ['recreational', 'cooking'] 是 false 的。因此,Riverpod 会认为两个参数值是不同的,就会尝试发送新的网络请求。 这会导致网络请求的无限循环,会一直向用户显示进程指示器(转圈圈)。

要修改该问题,应该使用 const 列表(const ['recreational', 'cooking']),或者使用覆写了 == 操作符的自定义列表。

想要及时发现该错误,建议使用 riverpod_lint 和开启 provider_parameters lint 规则。然后,前面的代码片段就会提示警告。查看 开始 了解安装步骤。


转载自:https://juejin.cn/post/7307469193028534299
评论
请登录