Flutter

【Flutter】Riverpodを使用したカウンターアプリのサンプル (V1.0.0に対応)

記事内に商品プロモーションを含む場合があります

最近はFlutterの状態管理にRiverpodを使用しています。

Flutterでプロジェクトを作成したときに生成されるカウンターアプリは、状態管理にStatefulWidgetを使用していますが、Riverpodを使用したバージョンを備忘録として残しておきます。

本記事で使用している構成は、以下の組み合わせです。

  • hooks_riverpod : ^1.0.0
  • flutter_hooks : ^0.18.0
  • flutter_state_notifier : ^0.7.1
  • freezed : ^0.14.2

※2021/11/6 versionを更新

Riverpodで状態管理する場合に、使用するパッケージ

Riverpodを使用して状態管理する場合、目的により使用するパッケージが異なってきます。

一般的なパッケージ構成は以下の通りです。

  • hooks_riverpod
  • flutter_hooks
  • flutter_state_notifier
  • freezed

Riverpodの公式リリース(V1.0.0)に伴い、使用するWidgetが変わったため、本サンプルでは「flutter_hooks」が不要になりました。なお、useEffectなどを使用する場合は必要です。

いずれも、pub.devにあがっているので、pubspec.yamlに追加すればインストールすることができます。

ちなみにRiverpod作者のRemi Rousseletさんも上記のような構成が好きなようです。

https://twitter.com/remi_rousselet/status/1277504792672325635?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1277504792672325635%7Ctwgr%5E%7Ctwcon%5Es1_&ref_url=https%3A%2F%2Fcdn.embedly.com%2Fwidgets%2Fmedia.html%3Ftype%3Dtext2Fhtmlkey%3Da19fcc184b9711e1b4764040d3dc5c07schema%3Dtwitterurl%3Dhttps3A%2F%2Ftwitter.com%2Fremi_rousselet%2Fstatus%2F1277504792672325635image%3D

Riverpodを使用した、カウンターアプリのサンプル

私は個人的にMVVMの構成を使うことが多いので、今回のサンプルもMVVMで構成しています。(ただし、今回Modelは使用していません。)

ファイル構成は以下にしています。

ポイントをかいつまんで説明します。

main()でProviderScopeを使用する

main()でProviderScopeを使用します。

void main() {
  runApp(ProviderScope(
    child: MyApp(),
  ));
}

MyAppでProviderを使用できるようになります。

viewはHookConsumerWidgetを継承する

viewではProviderを使用するために、HookWidgetを継承します。

class MyHomePage extends HookConsumerWidget {
  final String title;

  MyHomePage({required this.title});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // counterProviderの状態
    final counter = ref.watch(counterProvider).counter;
    // CounterControllerの関数を使用するために読み取り
    final notifier = ref.watch(counterProvider.notifier);
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        // CounterControllerに置いてある処理を呼ぶ
        onPressed: () => notifier.increment(),
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

HookConsumerWidgetを継承することで、ref.watchを使用してProviderのstateにアクセスできるようになります。

また、カウントする処理もProviderに移してあり、こちらはref.watchの引数にprovider.notifierで読み取ります。

StateクラスにProviderで管理する状態を定義する

Stateクラスで管理したい状態を定義する。

@freezed
class CounterState with _$CounterState {
  const CounterState._();
  const factory CounterState({
    required int counter,
  }) = _CounterState;
}

カウンターアプリではcounterしかないため1つですが、複数ある場合は複数定義します。
freezedパッケージによりimmutableなクラスとなっています。

freezedパッケージの解説は以下の記事をご覧ください。

【簡単】Flutter freezedパッケージを最小限の機能だけで使用する手順Flutterの状態管理手法は現在時点(2021年2月時点)で、以下のような組み合わせが流行っています。 Riverpod + S...

ControllerクラスでグローバルなProviderを定義する

ControllerクラスにてStateNotifierを継承したControllerと、グローバルなProviderを用意します。

final counterProvider = StateNotifierProvider<CounterController, CounterState>(
    (ref) => CounterController(ref.read));

class CounterController extends StateNotifier {
  CounterController(this._reader)
      : super(CounterState(
          // Stateを初期化する。counterは必須にしているため、初期値設定しないとエラー
          counter: 0,
        )) {
    // DBからのデータ初期取得処理などあれば、取得処理を書く
  }

  // 他のproviderを使用する場面などで使用する
  final Reader _reader;

  // increment処理をControllerに定義
  void increment() {
    state = state.copyWith(
      counter: state.counter + 1,
    );
  }
}

カウンターの初期値と、カウンターを1増やす処理を用意しています。

CounterStateで定義した状態を操作する場合、stateの値を更新します。

freezedパッケージでCounterStateはimmutableになっているので、stateを更新する場合はfreezedパッケージで生成されたcopyWithを使用します。

まとめ

Riverpodを使用したカウンターアプリのサンプルでした。

人それぞれ書き方は色々あると思いますが、個人的にはこれが1番好きです。Riverpodを使う前に使っていた、ProviderのときにMVVM構成にしていたので、近い感じで使用できていい感じです。

今回のサンプルの全容を確認したい場合は、Githubにてどうぞ。