メインコンテンツに進む

.family

本セクションの前に「プロバイダとは」と「プロバイダの利用方法」のセクションに目を通していただくことをおすすめします。 ここでは「プロバイダ修飾子」の一つである .family について詳しく説明します。

.family 修飾子の目的は「外部のパラメータをもとに一意のプロバイダを作成すること」です。

次のようなケースでの使用が考えられます。

  • FutureProviderfamily を組み合わせて、メッセージ ID から Message を取得する。
  • プロバイダにアプリの現在のロケール情報を渡して、翻訳文を取得する。

使用方法

.family 修飾子を付けてプロバイダを作成すると、パラメータが追加されます。 このパラメータはプロバイダのステート(状態)を計算する要素として使用することができます。

例えば familyFutureProvider を組み合わせることで、メッセージ ID に紐づく Message を取得することが可能です。

final messagesFamily = FutureProvider.family<Message, String>((ref, id) async {
return dio.get('http://my_api.dev/messages/$id');
});

family プロバイダをウィジェットなどで利用する際の構文は通常と若干異なります。次の構文ではコンパイル時エラーが出ます。

Widget build(BuildContext context, WidgetRef ref) {
// エラー – messagesFamily はプロバイダではありません
final response = ref.watch(messagesFamily);
}

代わりに、次のように messagesFamily に引数を渡してください。

Widget build(BuildContext context, WidgetRef ref) {
final response = ref.watch(messagesFamily('id'));
}
info

.family 修飾子を使用するプロバイダに、異なる引数を同時に渡すことも可能です。 例えば titleFamily プロバイダに異なるロケール情報を渡し、英語とフランス語の翻訳文をそれぞれ取得することができます。

@override
Widget build(BuildContext context, WidgetRef ref) {
final frenchTitle = ref.watch(titleFamily(const Locale('fr')));
final englishTitle = ref.watch(titleFamily(const Locale('en')));

return Text('fr: $frenchTitle en: $englishTitle');
}

引数として渡すオブジェクトの等価性

family プロバイダを正常に動作させるには、引数として渡すオブジェクトに等価性が定義されている必要があります。

プリミティブ型(bool/int/double/String)か、定数(プロバイダ)もしくは ==hashCode をオーバーライドしたイミュータブル(不変)オブジェクトのいずれかが理想的です。

【重要】 オブジェクトが一定ではない場合は autoDispose 修飾子との併用が望ましい

family を使って検索フィールドの入力値をプロバイダに渡す場合、その入力値は頻繁に変わる上に同じ値が再利用されることはありません。 おまけにプロバイダは参照されなくなっても破棄されないのがデフォルトの動作であるため、この場合はメモリリークにつながります。

こうしたメモリリークは .family.autoDispose 修飾子を併用することで避けることができます。

final characters = FutureProvider.autoDispose.family<List<Character>, String>((ref, filter) async {
return fetchCharacters(filter: filter);
});

family プロバイダに複数のパラメータを渡す

.family 修飾子はプロバイダに複数のオブジェクトを渡すことができません。

しかし、前述した制限を満たしている限りは どのような オブジェクトでも渡すことができます。

これを利用して、例えば以下のオブジェクトを渡すことでプロバイダに複数のパラメータを間接的に渡すことができます。

  • tuple パッケージのタプルオブジェクト
  • Freezed もしくは built_value パッケージで生成されたオブジェクト
  • equatable を使用したオブジェクト

以下は Freezed もしくは equatable を利用した場合のサンプルコードです。

@freezed
abstract class MyParameter with _$MyParameter {
factory MyParameter({
required int userId,
required Locale locale,
}) = _MyParameter;
}

final exampleProvider = Provider.autoDispose.family<Something, MyParameter>((ref, myParameter) {
print(myParameter.userId);
print(myParameter.locale);
// userId と locale を使って何かする
});

@override
Widget build(BuildContext context, WidgetRef ref) {
int userId; // ユーザ ID をどこかで取得する
final locale = Localizations.localeOf(context);

final something = ref.watch(
exampleProvider(MyParameter(userId: userId, locale: locale)),
);

...
}