Udemyセール開催中! 対象コースが1,220円から

【Flutter×Riverpod】StateNotifierProviderの使い方|プロバイダで状態管理

今回はRiverpodのプロバイダ「StateNotifierProvider」をFlutterで使用する方法を紹介します。

FlutterでRiverpodを使うのが初めての方は先にこちらの記事をお読みください。

目次

Riverpodの導入

$ flutter pub add flutter_riverpod
$ flutter pub get

pubspec.yamlにパッケージを追加・更新

import 'package:flutter_riverpod/flutter_riverpod.dart';

main.dartファイルにコピペ

「ProviderScope」をFlutterアプリのルートにする

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

「ProviderScope」をrunApp()でFlutterアプリのルートにします。これでFlutterアプリでRiverpodのプロバイダを使えるようになりました。

「StateNotifierProvider」の使い方

コードで解説してる箇所は記事下のサンプルコードを参考にして読んでみてください。

概要

StateNotifierProviderを使えば、StateNotifierを継承したクラスのステート(値)をプロバイダで渡したり、外部から変更できます。

StateProviderが「列挙型(enum)」「String型」「bool型」「数値型」などシンプルなステートを管理するのに対し、StateNotifierProviderは複雑なロジックが必要となるステートをクラスで管理できます。

文章だけだと分かりにくいのでコードを使いながら説明します。

ステップ1:StateNotifierを継承したクラスを定義

//定義
class Notifier名 extends StateNotifier<型> {
  Notifier名() : super(初期値);
  
  void メソッド(){
    処理...;
  }
  ...
}

//例
class CounterNotifier extends StateNotifier<int> {
  CounterNotifier() : super(0);

  //superで指定した値が初期値(stateの初期値は「0」)
  void increment() => state++;
  void decrement() => state--;
}

StateNotifierProviderを使用するにはステートを管理するクラス(StateNotifierを継承したクラス)が必要となるので先に定義しておきます。

StateNotifierクラスを継承したらステートの「型」「初期値」を指定し、ステートを管理するメソッドを定義します。メソッドでは「state」を使用してステートを変更できます。

ステップ2:StateNotifierProviderを定義

//定義
final プロバイダ名 = StateNotifierProvider<Notifier名, 型>((ref) {
  return Notifier名();
});

//例
final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
  return CounterNotifier();
});

上記コードのようにグローバル定数でプロバイダを定義します。

「ref」をWidgetで使用可能にする

プロバイダの値を取得するには「ref」を使用します。ただ通常のStatelessWidget、StatefulWidgetでは「ref」を使用できないので下記のように置き換える必要があります。

StatelessWidgetでプロバイダを使用する場合

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

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Container();
  }
}

「StatelessWidget」を「ConsumerWidget」に置き換える必要があります。またbuild()の第二引数に「WidgetRef ref」を渡します。Riverpodのコードスニペットプラグインを入れておけば自動生成できます。

StatelessWidgetでプロバイダを使用する場合

class MyApp extends ConsumerStatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  ConsumerState<ConsumerStatefulWidget> createState() => _MyAppState();
}

class _MyAppState extends ConsumerState<MyApp> {

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

「StatefulWidget と State」を「ConsumerStatefulWidget と ConsumerState」に置き換える必要があります。StatelessWidget同様コードスニペットで自動生成できます。

ref.watchでプロバイダのデータを取得

final counter = ref.watch(counterProvider);

ref.watchを使用してプロバイダ(counterProvider)の現在の値(ステート)を取得します。ref.watchを使用することでプロバイダのステートに変化があった際、常に「counter」に更新された値を反映できます。

ここで押さえておきたいのがcounterは定数なので、counterから直接プロバイダのステートを変更できません。よってプロバイダのステートを変更するには「ref」を使用します。

StateNotifierのメソッドで値を変更

//書き方
ref.read(プロバイダ.notifier).メソッド();

//例
ref.read(counterProvider.notifier).increment();
ref.read(counterProvider.notifier).decrement();

StateNotifierProviderでステート(値)を変更するにはStateNotifierで定義したメソッドを使用します。

サンプルコード

「ref.watch」でcounterProvider(StateNotifierProvider)のステートを取得・監視し、StateNotifierで定義したメソッド「increment()」と「decrement()」でステートの値を変更しています。

//ソース
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class CounterNotifier extends StateNotifier<int> {
  CounterNotifier() : super(0);

  void increment() => state++;
  void decrement() => state--;
}

final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
  return CounterNotifier();
});

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

class MyApp extends ConsumerStatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  ConsumerState<ConsumerStatefulWidget> createState() => _MyAppState();
}

class _MyAppState extends ConsumerState<MyApp> {
  @override
  Widget build(BuildContext context) {
    final counter = ref.watch(counterProvider);

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Riverpod'),
        ),
        body: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              counter.toString(),
              style: TextStyle(
                fontSize: 50,
              ),
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                IconButton(
                  onPressed: () {
                    ref.read(counterProvider.notifier).increment();
                  },
                  icon: Icon(
                    Icons.add,
                    size: 50,
                  ),
                ),
                IconButton(
                  onPressed: () {
                    ref.read(counterProvider.notifier).decrement();
                  },
                  icon: Icon(
                    Icons.remove,
                    size: 50,
                  ),
                ),
              ],
            )
          ],
        ),
      ),
    );
  }
}

以上です。

参考

Riverpod:StateNotifierProvider

  • URLをコピーしました!
目次
閉じる