💠

riverpod generatorでauthStateChangeを使う

2023/10/22に公開

Overview

以前書いていたProviderのコードをriverpod generatorに移行するときに、色々ハマったので、記事にしようと思いました。

やることは、コードの書き方を関数ぽくするだけなんですけど、なれるまでは抵抗を感じますね。それではやっていきましょう🧑‍🎓

summary

これが以前から使われていたコードですね。Providerって言ったらこれをイメージしていました。これにautoDisposeをつけて使ったりしてました。

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

// FirebaseAuthを利用するためのProvider
final firebaseAuthProvider = Provider((ref) => FirebaseAuth.instance);

// 認証状態を監視するためのProvider
final authStateChangesProvider = StreamProvider((ref) {
  return ref.watch(firebaseAuthProvider).authStateChanges();
});

// uidを取得するためのProvider
final uidProvider = Provider((ref) {
  return ref.watch(firebaseAuthProvider).currentUser?.uid;
});

でこちらが新しい書き方なんですけど、以前書いたコードを書き換えて使ったものです。uidは、今回使わなかったので、省いてます。

import 'package:firebase_auth/firebase_auth.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'auth_provider.g.dart';

// flutter pub run build_runner watch --delete-conflicting-outputs

// FirebaseAuthを提供するProvider

FirebaseAuth firebaseAuth(FirebaseAuthRef ref) {
  return FirebaseAuth.instance;
}

// ログイン状態を監視するStreamを提供するProvider

Stream<User?> authStateChange(AuthStateChangeRef ref) {
  return ref.watch(firebaseAuthProvider).authStateChanges();
}

ジェネレーターのコードを定義したらコマンドを打ってプロバイダーを自動生成しましょう。

flutter pub run build_runner watch --delete-conflicting-outputs

使用するときは、いつものようにこんな感じで使えばできます。

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:widget_cook/sns_app/pages/post_page.dart';
import 'package:widget_cook/sns_app/pages/sign_in_page.dart';
import 'package:widget_cook/sns_app/provider/auth_provider.dart';

class PostApp extends HookConsumerWidget {
  const PostApp({super.key});
  
  Widget build(BuildContext context, WidgetRef ref) {
    return MaterialApp(
      title: 'Chat App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const SplashPage(),
    );
  }
}

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

  
  Widget build(BuildContext context, WidgetRef ref) {
    // StreamProvider を監視し、AsyncValue<User?> を取得する。
    final authStateAsync = ref.watch(authStateChangeProvider);
    // パターンマッチングを使用して、状態をUIにマッピングする
    return authStateAsync.when(
      data: (user) => user != null ? const PostPage() : const SignInPage(),
      loading: () => const CircularProgressIndicator(),
      error: (err, stack) => Text('Error: $err'),
    );
  }
}

コマンドを打って自動生成されたプロバイダーをimportしたページで、読み込めば匿名認証を使えます。コードジャンプすると、自動生成されたファイルの場所にいくのでコード量が多いプロジェクトだと探すのが大変になるのが、デメリットですかね。

普通のプロバイダーだとそれがないので、「ああ、ここのファイルにあった!」とすぐ探せるところが良かったですね。

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:widget_cook/sns_app/provider/auth_provider.dart';

class SignInPage extends HookConsumerWidget {
  const SignInPage({super.key});
  
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Sign In Page'),
      ),
      body: Center(
        child: Column(
          children: [
            ElevatedButton(
              onPressed: () async {
                ref.read(firebaseAuthProvider).signInAnonymously();
              },
              child: const Text('Sign In'),
            ),
          ],
        ),
      ),
    );
  }
}

thoughts

使ってみて、最初にハマったのはimportしたときにエラーが出たりすることでしたね。autoDisposeされてるからなのかなと思ったんですけど、どうやらそうではなかったようでデータの型が違ったりしたのが原因だったのかもしれないです。
他のロジックを書き換えるときにも色々ハマったりしてて、早く使いこなされるようになりたいなと思いました。

Jboy王国メディア

Discussion