😽

【Flutter】Riverpodで部分再描画のConsumer()の使い方

2022/04/12に公開

Riverpodで部分再描画のConsumer()の使い方の説明です。
このConsumer()を使うと部分的に描画することが可能となり、パフォーマンスが上がる場合があります。

記事を書こうと思った背景です。
RiverpodでConsumerWidgetを継承してウィジェットを構築していましたが、プルダウンを変更した時に再描画され、テキストフィールドの内容が初期化され、空文字の値となりました。

Consumer()を使うことでプルダウンだけ再描画され、テキストフォームは再描画されないので、テキストフォームの値が保持されるようになりました。
忘れないうちにConsumer()の使い方を説明します。

手順

  1. WidgetでConsumerWidgetではなく、StatelessWidgetにします。ConsumerWidgetを使ってしまうと全体を状態が変わった時に再描画してしまいます。
class GooutSuggestionCreatePage extends StatelessWidget {
  1. RiverpodのStateNotifierProviderでsuggestionCreateProviderを持たせ、Riverpodの状態管理を定義します。
final suggestionCreateProvider =
    StateNotifierProvider<SuggestionCreateNotifier, SuggestionCreateState>(
  (ref) => SuggestionCreateNotifier(ref.read),

3.suggestionCreateProviderの状態によって変わって欲しいところだけをConsumer()で囲み、その中のrefメソッドで状態を監視します。
そして、Widgetをreturnで返します。
監視した変数の使い方は通常のriverpodと同じです。

Consumer(builder: (context, ref, _) {
final suggestionCreateState =
    ref.watch(suggestionCreateProvider);
final suggestionCreateStateNotifier =
    ref.watch(suggestionCreateProvider.notifier);

return DropdownButton(
    value: suggestionCreateState.selectedCategory,
    items: categoryDropdownMenuItem,
    onChanged: (String? value) {
      selectedCategory = value!;
      suggestionCreateStateNotifier.setCategory(value!);
    });
}),

全体のコードです。

final suggestionCreateProvider =
    StateNotifierProvider<SuggestionCreateNotifier, SuggestionCreateState>(
  (ref) => SuggestionCreateNotifier(ref.read),
);

class GooutSuggestionCreatePage extends StatelessWidget {
  const GooutSuggestionCreatePage({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {

    final titleController = TextEditingController();
    String selectedCategory = '';
    String selectedSubCategory = '';

    return Scaffold(
        appBar: AppBar(
          title: const Text('募集を作成'),
          backgroundColor: Colors.white,
          foregroundColor: Colors.black,
          centerTitle: true,
          elevation: 0.0,
          automaticallyImplyLeading: true,
        ),
        body: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Row(children: <Widget>[Text("タイトルを入力"), Text("0/30")]),
              TextFormField(
                controller: titleController,
                decoration: InputDecoration(
                  fillColor: inputBackgroundColor,
                  filled: true,
                ),
              ),
              const Text("カテゴリーを選択"),
              Consumer(builder: (context, ref, _) {
                final suggestionCreateState =
                    ref.watch(suggestionCreateProvider);
                final suggestionCreateStateNotifier =
                    ref.watch(suggestionCreateProvider.notifier);

                return DropdownButton(
                    value: suggestionCreateState.selectedCategory,
                    items: categoryDropdownMenuItem,
                    onChanged: (String? value) {
                      selectedCategory = value!;
                      suggestionCreateStateNotifier.setCategory(value!);
                    });
              }),
              Consumer(builder: (context, ref, _) {
                final suggestionCreateState =
                    ref.watch(suggestionCreateProvider);
                final suggestionCreateStateNotifier =
                    ref.watch(suggestionCreateProvider.notifier);
                return DropdownButton(
                    value: suggestionCreateState.selectedSubCategory,
                    items: subCategoryDropdownMenuItem,
                    onChanged: (String? value) {
                      selectedSubCategory = value!;
                      suggestionCreateStateNotifier.setSubCategory(value!);
                    });
              }),
            ]));
  }
}

公式サイト

Consumer can be used to listen to providers inside a StatefulWidget or to rebuild as few widgets as possible when a provider updates.

Consumer は、StatefulWidget 内のプロバイダをリッスンしたり、プロバイダの更新時にできるだけ少ない数のウィジェットを再構築するために使用することができます。
これ、すごい重要!!!

https://pub.dev/documentation//flutter_riverpod/latest/flutter_riverpod/Consumer-class.html

できれば以下のドキュメントでもConsumer()があると助かります。
https://riverpod.dev/

Discussion