🧑‍🎤

AsyncNotifierを使ってみた

2023/02/05に公開2

複雑なコードを書かなくて済む?

Riverpod2.0から、AsyncNotifierなるものが使えるようになったそうです。
早速試してみました!
といってもFirestoreに値を保存するだけですけどね。
電話番号を保存するロジックを作ってみました。

今回はこちらのサイトを参考にしました
https://codewithandrea.com/articles/flutter-riverpod-async-notifier/


何処かがいいのか?

StateNotifierだと、コードを書くのが複雑なのですが、こちらは、短くかけるようです。Readerを使わなくても外部ファイルに書いたProviderを呼び出すことができました。

StateNotifierはこんな感じです

慣れてますけど、長いですね!

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_ref_app/provider/firebase_provider.dart';

final textStateProvider = StateNotifierProvider<TextState, dynamic>((ref) {
  // Refをかくと、()の中に、refを書かないとエラーが消えない!
  // クラスにコンストラクターがついている.
  return TextState(ref);
});

class TextState extends StateNotifier<dynamic> {
  // Refを使うと他のファイルのProviderを呼び出せる
  // Riverpod2.0以前は、Reader _readerと書いていた。
  final Ref _ref;
  // superは、親クラスのコンストクラスターを呼び出す
  TextState(this._ref) : super([]);
  // FireStoreにデータを追加するメソッド
  Future<void> textAdd(String text) async {
    // _ref.read()と書いて、firebaseProviderを呼び出す
    final ref = await _ref
        .read(firebaseProvider)
        .collection('users')
        .add({'text': text});
  }
}

AsyncNotifierの場合

AsyncNotifierProviderのお作法があって、buildメソッドをオーバーライドする設定が必要みたいです。
それ以外は複雑な設定はなくコードは書きやすいなと思いました。
commonディレクトリを作成して、以下のファイルを作成する。

Firebaseを使用するProvider

provider.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

final firebaseProvider =
    Provider<FirebaseFirestore>((ref) => FirebaseFirestore.instance);

Firestoreに値を追加するAsyncNotifier

Readerを使わなくても外部ファイルのProviderを呼び出すことができて、型の書き方がvoidでも書けるのが、簡単に書けるなと思いました。
voidでいいのか疑問ですが...

common/app_notifier.dart
import 'dart:async';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:where_search_sample/common/provider.dart';
// AsyncNotifierを使うためのAsyncNotifierProviderを定義する.
final appController = AsyncNotifierProvider<AppController, void>(() {
  return AppController();
});

class AppController extends AsyncNotifier<void> {
  // [build]メソッドをオーバーライドして、[FutureOr]を返すようにする。
  
  FutureOr<void> build() {
    // 値を返す(戻り値の型がvoidの場合は何もしない).
  }
  // Firestoreに値を追加するメソッドを定義する.
  Future<void> addPost(String inputNumber) async {
    // ReaderがなくてもProviderを呼び出せる.
    final appRepository = ref.read(firebaseProvider);

    appRepository.collection('posts').add({'postNumber': inputNumber});
  }
}

アプリを実行するコード

main.dartにFormを作成して、メソッドを呼び出して使用するコード。

main.dart
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:where_search_sample/common/app_notifier.dart';

import 'firebase_options.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  runApp(
    // Adding ProviderScope enables Riverpod for the entire project
    ProviderScope(child: MyApp()),
  );
}

class MyApp extends StatefulWidget {
  
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  
  Widget build(BuildContext context) {
    return const MaterialApp(
        title: "Flutter Demo App",
        debugShowCheckedModeBanner: false,
        home: PhoneWidget());
  }
}

class PhoneWidget extends ConsumerWidget {
  const PhoneWidget({super.key});

  
  Widget build(BuildContext context, WidgetRef ref) {
    var inputNumber = ""; // Formの値を保存する変数.

    return Scaffold(
        appBar: AppBar(
          title: Text('Notifier'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              TextField(
                onChanged: (value) {
                  inputNumber = value; // onChangedの値を保存する.
                },
                keyboardType: TextInputType.number, // 数値でkeyboard入力できるようにする.
              ),
              ElevatedButton(
                child: const Text('番号を登録'),
                // ボタンコールバック内の状態を変更する
                onPressed: () =>
                    ref.read(appController.notifier).addPost(inputNumber),
              ),
            ],
          ),
        ));
  }
}

最後に

Riverpod2.0になって便利な機能が追加されて以前より使いやすくなっているのが、わかりましたが破壊的な変更もあって、対応に追われてしまいますね💦
個人アプリは、Riverpod1.0のままだから、速くコード書き換えたいですね!

Discussion

こんぶこんぶ

addPost するだけなら Provider をつかえばよさそう
state を更新する必要がないのであれば AsyncNotifier を使う必要はなさそう。

JboyHashimotoJboyHashimoto

すいません、師匠🙇‍♂️
Providerを使うユースーケースを想定した記事を書こうと思います。