.family
本セクションの前に「プロバイダとは」と「プロバイダの利用方法」のセクションに目を通していただくことをおすすめします。
ここでは「プロバイダ修飾子」の一つである .family
について詳しく説明します。
.family
修飾子の目的は「外部のパラメータをもとに一意のプロバイダを作成すること」です。
次のようなケースでの使用が考えられます。
- FutureProvider と
family
を組み合わせて、メッセージ ID からMessage
を取得する。 - プロバイダにアプリの現在のロケール情報を渡して、翻訳文を取得する。
使用方法
.family
修飾子を付けてプロバイダを作成すると、パラメータが追加されます。
このパラメータはプロバイダのステート(状態)を計算する要素として使用することができます。
例えば family
と FutureProvider を組み合わせることで、メッセージ 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'));
}
.family
修飾子を使用するプロバイダに、異なる引数を同時に渡すことも可能です。
例えば titleFamily
プロバイダに異なるロケール情報を渡し、英語とフランス語の翻訳文をそれぞれ取得することができます。
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
- Equatable
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 を使って何かする
});
Widget build(BuildContext context, WidgetRef ref) {
int userId; // ユーザ ID をどこかで取得する
final locale = Localizations.localeOf(context);
final something = ref.watch(
exampleProvider(MyParameter(userId: userId, locale: locale)),
);
...
}
class MyParameter extends Equatable {
MyParameter({
required this.userId,
required this.locale,
});
final int userId;
final Locale locale;
List<Object> get props => [userId, locale];
}
final exampleProvider = Provider.family<Something, MyParameter>((ref, myParameter) {
print(myParameter.userId);
print(myParameter.locale);
// userId と locale を使って何かする
});
Widget build(BuildContext context, WidgetRef ref) {
int userId; // ユーザ ID をどこかで取得する
final locale = Localizations.localeOf(context);
final something = ref.watch(
exampleProvider(MyParameter(userId: userId, locale: locale)),
);
...
}