Chapter 10

.selectの使い方

JboyHashimoto
JboyHashimoto
2023.02.26に更新
このチャプターの目次

https://riverpod.dev/docs/concepts/reading
select" を使って再構築をフィルタリングする
プロバイダの読み込みに関連する最後の機能として、ref.watch からのウィジェット/プロバイダの再構築回数、または ref.listen による関数の実行回数を減らすことができるようになりました。

これは、デフォルトでは、プロバイダをリッスンすると、オブジェクトの状態全体をリッスンすることになるので、覚えておくことが重要です。しかし、時には、ウィジェット/プロバイダは、オブジェクト全体ではなく、いくつかのプロパティの変更にしか関心がないかもしれません。

selectを使うことで、気になるプロパティを返す関数を指定することができる。

Userが変更されるたびに、Riverpodはこの関数を呼び出し、以前と新しい結果を比較します。両者が異なる場合(名前が変更された場合など)、Riverpodはウィジェットを再構築する。
しかし、もし両者が同じであれば(年齢が変わった時など)、Riverpodはウィジェットを再構築しない。


使用するユースケース

プロパイダーを使って全ての値を読み取るのではなくて、特定のモデルクラスの値を読み込むのに使われている使用例があります。
車の情報が入っているモデルクラスを定義して、その中の特定の値にアクセスして、その値の情報だけ更新する処理を作ってみました。
機能はどんなものかというと、ボタンを押すと、モデルクラスのプロパティが上書きされて、新しくなるものと元の状態に戻す機能です。
画面を動的に、変化させるロジックを作るときに使えそうです。

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

// 内容を変更できないイミュータブルなクラスを定義する.

class CarModel {
  const CarModel(
      {required this.name, required this.model, required this.color});
  // イミュータブルなクラスのプロパティはfinalで書かなければならいルールがある.
  final String name;
  final String model;
  final Color color;

  // このクラスは直接内容を変更できないので、コピーする必要がある.
  //オブジェクトの各プロパティの内容をコピーして新しいCarModelを返すメソッド.
  CarModel copyWith({String? name, String? model, Color? color}) {
    return CarModel(
        name: name ?? this.name,
        model: model ?? this.model,
        color: color ?? this.color);
  }
}

// StateProviderでCarModelを呼び出して、プロパティに初期値を入れる。
// メーカー名、製品名、車の色を設定を初期値として設定する.
final carModelProvider = StateProvider<CarModel>((ref) =>
    const CarModel(name: 'Toyota', model: 'レクサスRX', color: Colors.grey));

class SelectProvider extends ConsumerWidget {
  const SelectProvider({Key? key}) : super(key: key);

  
  Widget build(BuildContext context, WidgetRef ref) {
    // 車の色を表示するプロバイダー.
    Color carColor = ref.watch(carModelProvider.select((value) => value.color));
    // 車のブランド名を表示する。今回だと高級車のレクサス.
    final toyota = ref.watch(carModelProvider.select((value) => value.model));
    // ref.listenでプロバイダーをコールバック関数の引数にして、古い車のブランド名と新しい車のブランド名をログに表紙.
    ref.listen(carModelProvider.select((value) => value.model),
        (prevModel, newModel) {
      print('以前のモデルが $prevModel で後のモデルが $newModel');
    });

    return Scaffold(
      appBar: AppBar(
        actions: [
          IconButton(
              onPressed: () {
                // 初期値の色に戻す処理.
                final carProviderController =
                    ref.read(carModelProvider.notifier);
                carProviderController.state = const CarModel(
                    name: 'Toyota', model: 'レクサスRX', color: Colors.grey);
              },
              icon: const Icon(Icons.reset_tv))
        ],
      ),
      body: Center(
        child: Container(
          width: 300,
          height: 600,
          color: carColor,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(toyota),
            ],
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 色を変更する処理.
          final carProviderController = ref.read(carModelProvider.notifier);

          carProviderController.state = const CarModel(
            name: 'Toyota',
            model: 'LS500h',
            color: Colors.indigoAccent,
          );
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}