【Flutter】StateNotifierProviderで状態管理(Riverpod)

flutter

1. 概要

上記前回のブログでは「Riverpod」の「StateProvider」を使ったCounterの状態管理についてでした。

「StateProvider」は「enumや数値」などシンプルなステートを管理するのに適しているようです。

StateProvider は外部から変更が可能なステート(状態)を公開するプロバイダです。 StateNotifierProvider の簡易版であり、ステートの管理にわざわざ StateNotifier クラスを定義するほどではない場合にご利用いただけます。

https://riverpod.dev/ja/docs/providers/state_provider

今回は「StateNotifierProvider」を使い、Counterの状態管理をする内容についてです。

今回も前回と結果は同じでシンプルなステートを管理する内容なので、結果的には「StateProvider」を使った方が良いと思われますが、ここでは「StateNotifierProvider」を使って開発する例として見て頂ければと思います。

StateNotifierProvider は StateNotifier(Riverpod が依存する state_notifier パッケージのクラス)を監視し、公開するためのプロバイダです。 この StateNotifierProvider および StateNotifier は、ユーザ操作などにより変化するステート(状態)を管理するソリューションとして Riverpod が推奨するものです。

https://riverpod.dev/ja/docs/providers/state_notifier_provider

※今回は下記の例で、前回と同じ結果になるように構成してます。

  • Riverpod
    • StatelessWidget / ConsumerWidget
      • アプリ起動中に状態が変化しない
      • StateNotifierProvider / ProviderScope

対象としては開発を1年程やってて自分で最初から開発してみたい方になります。そのため細かい用語などの説明はしません。

2. StateNotifierProvider

2-1. プロジェクトを作成

2-2. Dependencyのインストール

flutter pub add flutter_riverpod
dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^1.0.2
  flutter_riverpod: ^1.0.4

dev_dependencies:
  flutter_test:
    sdk: flutter

  flutter_lints: ^2.0.0

2-3. ソースコード

  • main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_demo_with_model/models/counter.dart';
import 'package:riverpod_demo_with_model/notifiers/counter_notifier.dart';
import 'package:riverpod_demo_with_model/views/home.dart';

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

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

  • views/home.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_demo_with_model/main.dart';

class MyHomePage extends ConsumerWidget {
  const MyHomePage({Key? key, required String title}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final data = ref.watch(counterProvider);
    final counterNotifier = ref.read(counterProvider.notifier);
    return Scaffold(
      appBar: AppBar(
        title: const Text('Riverpod example'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          const Text('You have pushed the button this many times:'),
          Center(
            child: Text(
              data.counter.toString(),
              style: Theme.of(context).textTheme.headline4,
            ),
          ),
          ElevatedButton(
              onPressed: () => counterNotifier.plus(), child: const Text('+')),
          ElevatedButton(
              onPressed: () => counterNotifier.minus(), child: const Text('-')),
        ],
      ),
    );
  }
}

  • models/counter.dart
import 'package:flutter/material.dart';

@immutable
class CounterData {
  const CounterData({required this.counter});
  final int counter;
}

  • notifiers/counter_notifier.dart
// ignore_for_file: avoid_print

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_demo_with_model/models/counter.dart';

class CounterNotifier extends StateNotifier<CounterData> {
  CounterNotifier() : super(const CounterData(counter: 0));

  void plus() {
    state = CounterData(counter: state.counter + 1);
    print('plus=${state.counter}');
  }

  void minus() {
    state = CounterData(counter: state.counter - 1);
    print('minus=${state.counter}');
  }
}

2-4. 結果

2-5. 解説

「ProviderScope」を使い、状態管理の対象を指定します。

状態を管理するためのプロバイダとして「StateNotifierProvider」を使います。

プロバイダを利用するためには「ref」を取得する必要があるので「ConsumerWidget」を継承します。

  • プロバイダの利用方法 | Riverpod
    • ref.watch():プロバイダの値を取得した上で、その変化を監視する。値が変化すると、その値に依存するウィジェットやプロバイダの更新が行われる。
    • ref.read():プロバイダの値を取得する(監視はしない)。クリックイベント等の発生時に、その時点での値を取得する場合に使用できる。

3. 参考

関連記事

  1. 【Widget of the Week】#2 Expanded

  2. 【Widget of the Week】#6 FutureBuilde…

  3. flutter

    インターフェース定義からDart-Flutterクライアントコードを自…

  4. flutter

    【Flutter】APIからデータを取得し画面に表示(Flutter,…

  5. flutter

    【Flutter】Android画面に小さなポップアップ(Toast,…

  6. flutter

    【Flutter】KotlinでNative側のCounterを実装し…

最近の記事

  1. flutter
  2. flutter
  3. flutter
  4. flutter
  5. flutter
  6. flutter
  7. BBQ
  8. flutter

制作実績一覧

  1. Checkeys