Chapter 11

[v0.14.0以下版] ScopedProviderで特定の範囲内で状態を伝達する

村松龍之介
村松龍之介
2023.02.12に更新

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公式ドキュメントページ
https://pub.dev/documentation/riverpod/latest/riverpod/ScopedProvider-class.html