Chapter 09

ref.listenの使い方

JboyHashimoto
JboyHashimoto
2023.02.26に更新

https://riverpod.dev/docs/concepts/reading
プロバイダの変更に反応するためにref.listenを使用する
ref.watchと同様に、プロバイダを監視するためにref.listenを使用することが可能です。

両者の主な違いは、監視対象のプロバイダが変更された場合にウィジェットやプロバイダを再構築するのではなく、ref.listen を使用すると代わりにカスタム関数が呼び出されることです。

これは、エラーが発生したときにスナックバーを表示するなど、特定の変更が発生したときにアクションを実行するのに便利です。

ref.listenメソッドは2つの位置引数を必要とします。1つ目はプロバイダで、2つ目は状態が変化したときに実行したいコールバック関数です。コールバック関数が呼ばれると、2つの値(前の状態の値、新しい状態の値)が渡されます。

ref.listenメソッドは、プロバイダ本体の内部で使用することができます。


使用するユースケース

状態が更新されることをステートが更新されると表現するのですけど、ref.listenが呼ばれるのは、状態が更新されたとき、例えばカウンターのボタンを押して画面が更新されたときが、ステートが更新されたことになりますので、そのタイミングで呼ばれます。
StatefulWidgetだと、setStateが呼ばれたときですね。
以下のサンプルコードだと、カウンターが増えるごとに、ref.listenが呼ばれて、トーストが表示されます。

使用したパッケージ

https://pub.dev/packages/fluttertoast

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

final countStateProvider = StateProvider<int>((ref) => 0);

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

  
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(countStateProvider);
    // Providerを購読する
    ref.listen<int>(
      countStateProvider, // 購読対象のProviderを指定
      (previous, next) {
        // 変更前と変更後の値が受け取れる
        // 行いたい処理を書く
        print('古い値: ${previous}');
        print('新しい値: ${next}');
        Fluttertoast.showToast(
          fontSize: 20.0,
          backgroundColor: Colors.green,
          textColor: Colors.black54,
          msg: '新しい値: ${next}',
          toastLength: Toast.LENGTH_LONG,
        );
      },
      // `onError` で何らかのエラーハンドリングが可能(任意)
      onError: (error, stackTrace) => debugPrint('$error'),
    );

    return Scaffold(
      appBar: AppBar(
        title: Text('ref.listen ${count.toString()}'),
      ),
      // 数値をSharedPreferencesに保存するロジック
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          ref.read(countStateProvider.notifier).state++;
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

こんなことができます