このチャプターの目次
ScopedProvider - アプリの特定箇所で異なる可能性のあるProvider
ScopedProviderを使うことで、Widgetツリー上で Provider.family を使って引数を渡したり、
Widgetのコンストラクタに渡す必要がなくなります。
パフォーマンスの向上と単純さが得られます。
ScopedProviderを使わず、コンストラクタでindexを渡す場合
todo_list_page.dart
Widget build(BuildContext context) {
ListView.builder(
itemBuilder: (context, index) {
return child: TodoItemTile(index),
}
)
}
}
class TodoItemTile extends StatelessWidget {
const TodoItemTile(int index, {Key? key}): super(key: key);
// TodoListPageのListViewから受け取る index
final int index;
Widget build(BuildContext context) {
return ListTile(
title: Text('$index番目のTODO')
)
}
}
ScopedProviderを使った書き方
まずは、TODOのIndexを指定するためのScopedProviderを定義します。
/// Indexは必須とするので、デフォルトではエラーを設定するか、関数の代わりに `null` を返すようにします。
/// `null` を使った場合は型が `int?` (Nullを許容するint型)になります。
final currentTodoIndex = ScopedProvider<int>((watch) => throw UnimplementedError());
// 上とほぼ同じ動作だが Null を許容した例:
final currentTodoIndex = ScopedProvider<int?>(null);
パラメータを渡したいWidgetを ProviderScope
で囲みます。
overrides
プロパティの中で、ScopedProviderの値を指定します。(ここでは index
)
todo_list_page.dart
ListView.builder(
itemBuilder: (context, index) {
return ProviderScope(
overrides: [
currentTodoIndex.overrideWithValue(index),
],
child: const TodoItemTile(),
);
}
)
受け取りたい側のWidgetでは、通常のProviderを読み取る時と同じく、
watch
でScopedProviderを読み取ることで使用できます。
todo_item.dart
class TodoItemTile extends ConsumerWidget {
const TodoItemTile({Key? key}): super(key: key);
Widget build(BuildContext context, ScopedReader watch) {
// TODOアイテムのIndexを取得
final index = watch(currentTodoIndex);
return ListTile(
title: Text('$index番目のTODO')
)
}
}
この構文を使うメリットは…
TodoItemTileを const
キーワードを使ってインスタンス化できるにも関わらず、動的にWidgetをレンダリングできること
すなわち、ListViewが再構築されても index 自体が変更されなければTodoItemTileが再構築されることはありません。
参考リンク
ScopedProvider | Riverpod公式ドキュメントページ