🐊

【Flutter】RiverPodの基本的な使い方

2023/12/13に公開

はじめに

refを使用したRiverPodの基本的なコードをまとめました。
下記GitHubにコードを載せてます。また、.vscodeの中にスニペットもまとめており、これを使用いただければ、テンプレートとして呼び出せます。
自分の備忘録なので、至らない点はお許しを、、。

実際のコード(GitHub)

開発環境

  • mac
  • Vscode
  • Chrome(エミュレーターはなんでも可)

事前準備

大変なコードは自動で生成してもらえるようにします

  • pubspec.yamlにパッケージを追加
    dependencies:
        flutter:
          sdk: flutter
        flutter_riverpod:     #=> riverpod本体
        riverpod_annotation:  #=> コードを自動作成するため
    
    dev_dependencies:
        flutter_test:
          sdk: flutter
        flutter_lints: ^2.0.0
        build_runner:         #=> コードを自動作成するため
        riverpod_generator:   #=> コードを自動作成するため
    

用語

初めに使用する用語をまとめて紹介します。イメージしやすい言葉を使ってるので必ずしも正確とは限りませんのでご了承ください。

名前 説明
state 変化するデータ本体。
Proider stateを守る壁。refという鍵がないとstateを監視したり変更できないようにする。
Notifier stateを変更することができる編集者。
ConsumerWidget refを使用してstateを監視することができる。
ref Notifierがstateを変更するための鍵
  • 流れ

    1. Providerを作成。ほとんどパッケージが作ってくれます。
    2. ConsumerWidgetを作成してその中でrefを使用してstateを監視をする
    3. stateを変更する場合はnotifierを作成する

コードと説明

*注意* importは省略しています。不明な場合GitHubのコードを確認ください。

main.dart

MyApp(MaterialAppがあるクラス)をProviderScopeで囲みます。
RiverPodを使用する上で必須です。

void main() {
  const app = ProviderScope(child: MyApp());
  runApp(app);
}

my_app.dart (RiverPodは関係なし)

homeで呼び出すページ指定しています。

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: CountUp(),
    );
  }
}

count_up_page.dart (RiverPodは関係なし)

画面に表示されるページ部分。

class CountUp extends StatelessWidget {
  const CountUp({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('HOW TO RIVERPOD 1'),
      ),
      body: const Center(child: CountUpWidet()),
    );
  }
}

count_up.dart

Widgetを作成する前に、NotifierとProviderを作成します。
まずは、下記コードを書きます。エラーが出てる箇所がありますが、あとで消えます。

part 'count_up.g.dart';

@riverpod
class CountUpNotifier extends _$CountUpNotifier {
  @override
  int build() {
    return 0;
  }

  void updateState() {
    final oldState = state;
    final newState = oldState + 1;
    state = newState;
  }
}

次にコマンド上で下記コードを実行します。
作成中のプロジェクトのディレクトリで実行してください。
VSCodeでターミナルを開くとデフォルトでそうなっているかと思います。

flutter pub run build_runner build --delete-conflicting-outputs

実行すると、count_up.g.dartというファイルができます。
コードのエラーが消えていたら成功です。

Providerは自動で作成されます。名前はcountUpNotifierProviderになります。
最初の文字が小文字になっていることに注意してください。

CountNotifierでreturnされた値がstateの初期値になります。

更にupdateState()が呼び出された際は、stateに1を足した数を再度stateに格納します。
状態を変更するupdateState()を呼び出すには、notifierが必要になります。

count_up_widget.dart

最後にwidgetを作成します。
ここではrefを使用して状態を監視したり、状態を変化させたりします。

refの種類や使用用途は下記の表通りです。

種類 特徴 用途
ref.watch stateを監視し続ける。stateが変更されても反映される。 stateの状態をリアルタイムで画面上に表示するとき...etc
ref.listen stateを監視し続ける。stateの値が変更されたか判断し、変更されたらコード内の動きが実行される。 変更されたらその通知をする...etc
ref.read stateをぱっとみる。上記2つと違い、監視し続けないので呼び出された瞬間のstateしか確認しない。 state状態を変更する時に呼び出す

実際どのような動きかは下記の通りです。

1. refを使用するには、ConsumerWidgetを使用します。
2. ConsumerWidgetを使用する場合は、「WidgetRef ref」を追加します。
3. watchで状態を監視し続けます。watchはずっと続くのでstateの値が変化しても表示され続けます。ここでは、ボタンを押されるとstateの値が1ずつ増えるので、画面の表示も1ずつ増えていきます。
4. listenで状態を監視しつづけます。今回のコードではstateに状態の変化があったらスナクッバーで通知されるようにしてます。oldStateには変更前のデータ、newStateには変更後のデータが格納されてます。
5. readで状態を変更する為のnotifierを作成。***NotifierProvider.notifierでnotifierを作成して、notifier.updateState()でcount_up.dartのupdateState()を呼び出している。

//①
class CountUpWidet extends ConsumerWidget {
  const CountUpWidet({super.key});

  @override
  //②
  Widget build(BuildContext context, WidgetRef ref) {
    //③ 
    final countUp = ref.watch(countUpNotifierProvider);

    //④
    ref.listen(
      countUpNotifierProvider,
      (oldState, newState) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('$oldState から $newState に変更されました。'),
            duration: const Duration(milliseconds: 200),
          ),
        );
      },
    );

    final button = ElevatedButton(
      onPressed: () {
        //⑤
        final notifire = ref.read(countUpNotifierProvider.notifier);
        notifire.updateState();
      },
      child: const Text('1増やす'),
    );

/**********************
 *   実際のウィジェット  *
 **********************/
    return Center(
      child: Container(
        width: 300,
        height: 300,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Text(
              '$countUp',       //=> watchで監視しているstate
              style: const TextStyle(fontSize: 50),
            ),
            button,             //=> notifierで状態変更をするためのボタン
          ],
        ),
      ),
    );
  }
}

おわりに

長くなりましたが以上です。
最初はよく整理ができなかったですが、部品ごとに細かくファイルを分けてみると意外と整理できました。
もっともっと勉強していこうと思います。

Discussion