Chapter 02

StateProviderの使い方

JboyHashimoto
JboyHashimoto
2023.02.27に更新
このチャプターの目次

https://docs-v2.riverpod.dev/docs/providers/state_provider
StateProvider は、その状態を変更する方法を公開するプロバイダーです。これは、StateNotifierProviderを単純化したもので、非常に単純なユースケースのためにStateNotifierクラスを書く必要がないように設計されています。

StateProviderは、主にユーザー・インターフェースによって、単純な変数を変更できるようにするために存在します。
StateProviderの状態は、典型的には、次のうちの一つです。

フィルター・タイプのようなenum
文字列。一般的には、テキスト・フィールドの生のコンテンツです。
チェックボックスのためのブーリアン
数値。ページネーションや年齢を表すフォーム・フィールドに使用されます。

以下の場合は、StateProviderを使用しないでください。

あなたの状態は、検証ロジックを必要とします。
状態が複雑なオブジェクトである (カスタム・クラス、リスト/マップなど)
状態を変更するためのロジックが、単純な count++ よりも高度な場合。
より高度なケースでは、代わりに StateNotifierProvider を使用し、StateNotifier クラスを作成することを検討してください。
初期のボイラープレートは少し大きくなりますが、カスタム StateNotifier クラスを持つことは、プロジェクトの長期的なメンテナンス性にとって重要です。


使用するユースケース

StateProviderを使用すると、アプリケーション全体に共通した状態を管理することができます。特に、アプリケーション内で状態を受け渡す必要がある場合に、StateProviderを使用すると便利です。

例えば、アプリケーション内の複数のコンポーネント間でユーザーを認証する必要がある場合に、StateProviderを使用して、ユーザーが認証されているかどうかを共有して、すべてのコンポーネントで同じ状態を参照できるようにすることができます。

ただの変数と思った方が分かりやすいかなと思います。

// TextEditingControllerを使うためのProvider
final textProvider = StateProvider.autoDispose((ref) {
  // riverpodで使うには、('')が必要
  return TextEditingController(text: '');
});

他には、ユーザーが操作すると切り替わるボタンロジックでも使われています。以前ラジオボタンのロジックを実装するのに、使いました。

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

final intProvider = StateProvider<int>((ref) => 0);
final stringProvider = StateProvider<String>((ref) => '');

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

  
  Widget build(BuildContext context, WidgetRef ref) {
    // 状態管理している値を取得するプロバイダー.
    final _value = ref.watch(intProvider);
    final _text = ref.watch(stringProvider);
    // 状態管理している値を操作するプロバイダー.
    final _intValue = ref.watch(intProvider.notifier);
    final _textValue = ref.watch(stringProvider.notifier);

    return Scaffold(
      appBar: AppBar(
        title: const Text('RadioMenu'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          // int型のラジオボタン.
          Row(
            children: [
              Radio(
                  value: 1,
                  groupValue: _value, // 状態を表示する.
                  onChanged: (value) {
                    _intValue.state = value!; // 状態を操作する.
                  }),
              SizedBox(width: 10.0),
              Text('Radio1'),
              Radio(
                  value: 2,
                  groupValue: _value,
                  onChanged: (value) {
                    _intValue.state = value!;
                  }),
              SizedBox(width: 10.0),
              Text('Radio2'),
              Radio(
                  value: 3,
                  groupValue: _value,
                  onChanged: (value) {
                    _intValue.state = value!;
                  }),
              SizedBox(width: 10.0),
              Text('Radio3'),
            ],
          ),
          SizedBox(height: 20),
          // String型のラジオボタン.
          Row(
            children: [
              Radio(
                  value: 'Radio1',
                  groupValue: _text,
                  onChanged: (value) {
                    _textValue.state = value!;
                  }),
              SizedBox(width: 10.0),
              Text('Radio1'),
              Radio(
                  value: 'Radio2',
                  groupValue: _text,
                  onChanged: (value) {
                    _textValue.state = value!;
                  }),
              SizedBox(width: 10.0),
              Text('Radio2'),
              Radio(
                  value: 'Radio3',
                  groupValue: _text,
                  onChanged: (value) {
                    _textValue.state = value!;
                  }),
              SizedBox(width: 10.0),
              Text('Radio3'),
            ],
          ),
          ElevatedButton(
              onPressed: () async {
                // FirestoreにintとStringのデータを保存する.
                await FirebaseFirestore.instance.collection('radio').add({
                  'int': _value,
                  'String': _text,
                });
              },
              child: const Text('save'))
        ],
      ),
    );
  }
}

enumを使った例だと、ボトムナビゲーションバーがあります。過去に書いた記事があるので、ご興味ある方がいたら、見てみて下さい。
https://zenn.dev/flutteruniv_dev/articles/ee8037bdeb7b4b

enumをStateProviderで使用

import 'package:flutter_riverpod/flutter_riverpod.dart';
// enumは列挙型と呼ばれている固定数の定数値を表すために使用される
// 特別な種類のクラスです。
enum TabType { home, map, profile }
// StateProviderを使用して、enum型のTabTypeを型として使い状態管理をする。
// 初期の状態だとhomeの定数を初期値として使うので、HomeScreenが選択される。
final tabTypeProvider = StateProvider<TabType>((ref) => TabType.home);