跳到主要內容

將引數傳遞給您的請求

在上一篇文章中,我們看到了如何定義一個“provider”來發出一個簡單的 GET HTTP 請求。
但通常,HTTP 請求依賴於外部引數。

例如,以前我們使用 Bored API 向用戶推薦隨機活動。 但也許使用者想要過濾他們想做的活動型別,或者有價格要求,等等……
這些引數事先是未知的。因此,我們需要一種方法將這些引數 從我們的 UI 傳遞到我們的提供者程式。

更新我們的提供者程式以接受引數

提醒一下,以前我們是這樣定義我們的提供者程式的:

// A "functional" provider

Future<Activity> activity(Ref 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(
Ref 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 規則。這樣做之後,上面的程式碼片段將顯示警告。 有關安裝步驟,請參閱入門指南