💠
riverpod generatorでauthStateChangeを使う
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されてるからなのかなと思ったんですけど、どうやらそうではなかったようでデータの型が違ったりしたのが原因だったのかもしれないです。
他のロジックを書き換えるときにも色々ハマったりしてて、早く使いこなされるようになりたいなと思いました。
Discussion