跳到主要内容

将参数传递给您的请求

在上一篇文章中,我们看到了如何定义一个“provider”来发出一个简单的 GET HTTP 请求。
但通常,HTTP 请求依赖于外部参数。

例如,以前我们使用 Bored API 向用户推荐随机活动。 但也许用户想要过滤他们想做的活动类型,或者有价格要求,等等……
这些参数事先是未知的。因此,我们需要一种方法将这些参数 从我们的 UI 传递到我们的提供者程序。

更新我们的提供者程序以接受参数

提醒一下,以前我们是这样定义我们的提供者程序的:

// A "functional" provider

Future<Activity> activity(ActivityRef ref) async {
// TODO: perform a network request to fetch an activity
return fetchActivity();
}

// Or alternatively, a "notifier"

class ActivityNotifier2 extends _$ActivityNotifier2 {

Future<Activity> build() async {
// TODO: perform a network request to fetch an activity
return fetchActivity();
}
}

要将参数传递给我们的提供者程序,我们只需在带注解的函数本身上添加参数即可。
例如,我们可以更新提供者程序以接受与所需活动类型相对应的 String 参数:


Future<Activity> activity(
ActivityRef ref,
// 我们可以向提供者程序添加参数。
// 参数的类型可以是您想要的任何类型。
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);
}
警告

将参数传递给提供者程序时,强烈建议在提供者程序上启用“autoDispose”。
否则可能会导致内存泄漏。
有关详细信息,请参阅清除缓存并对状态处置做出反应

更新我们的 UI 以传递参数

以前,小部件是这样消费我们的提供者程序的:

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

但是现在我们的提供者程序收到参数,使用它的语法略有不同。 提供者程序现在是一个函数,需要使用请求的参数来调用它。
我们可以更新我们的 UI 以传递硬编码类型的活动,如下所示:

    AsyncValue<Activity> activity = ref.watch(
// The provider is now a function expecting the activity type.
// Let's pass a constant string for now, for the sake of simplicity.
activityProvider('recreational'),
);

传递给提供者程序的参数对应于带注解的函数的参数,减去“ref”参数。

信息

完全可以同时监听具有不同参数的同一提供者程序。
例如,我们的 UI 可以同时呈现“娱乐(recreational)”“烹饪(cooking)”活动:

    return Consumer(
builder: (context, ref, child) {
final recreational = ref.watch(activityProvider('recreational'));
final cooking = ref.watch(activityProvider('cooking'));

// We can then render both activities.
// Both requests will happen in parallel and correctly be cached.
return Column(
children: [
Text(recreational.valueOrNull?.activity ?? ''),
Text(cooking.valueOrNull?.activity ?? ''),
],
);
},
);

缓存注意事项和参数限制

将参数传递给提供者程序时,仍会缓存计算。 不同之处在于,计算现在是按参数缓存的。

这意味着,如果两个小部件使用具有相同参数的同一提供者程序,则只会发出单个网络请求。
但是,如果两个小部件使用具有不同参数的同一提供者程序,则将发出两个网络请求。

为此,Riverpod 依赖于参数的 == 运算符。
因此,传递给提供者程序的参数必须具有一致的相等性,这一点很重要。

警告

一个常见的错误是直接实例化一个新对象作为提供者程序的参数,但该对象没有重写 ==
例如,您可能很想去这样使用 List

    // We could update activityProvider to accept a list of strings instead.
// Then be tempted to create that list directly in the watch call.
ref.watch(activityProvider(['recreational', 'cooking']));

此代码的问题在于 ['recreational', 'cooking'] == ['recreational', 'cooking'] 的结果是 false。 因此,Riverpod 将认为这两个参数不同,并尝试发出新的网络请求。
这将导致网络请求的无限循环,一直向用户显示进度指示器。

要解决此问题,您可以使用 const 列表 (const ['recreational', 'cooking']) 或使用重写了 == 的自定义列表实现。

为了帮助发现此错误,建议使用 riverpod_lint 并启用 provider_parameters 的 lint 规则。这样做之后,上面的代码片段将显示警告。 有关安装步骤,请参阅入门指南