概要
Riverpod3.0から、whenメソッドではなくて、switchが推奨されるらしい?
あまり情報がないので、試行錯誤して、動くものを作ってみました。
これが以前の書き方です。
class SplashPage extends ConsumerWidget {
const SplashPage({Key? key}) : super(key: key);
@override
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'),
);
}
}
Riverpod3.0からはこちらが推奨されるとのことらしいです?
class SplashPage extends ConsumerWidget {
const SplashPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final authStateAsync = ref.watch(authStateChangeProvider);
switch (authStateAsync) {
case AsyncData(:final value):
return value != null ? const PostPage() : const SignInPage();
case AsyncError(:final error):
return Text('Error: $error');
case AsyncLoading():
return const CircularProgressIndicator();
default:
return const CircularProgressIndicator(); // すべてのケースに対するデフォルトの戻り値
}
}
}
要訳
今回は、ログインをしているか認証が通っているのを監視するauthStateChange
を使って実験してみました。
こちらがソースコード
プロバイダーを定義したら、ファイルを自動生成するコマンドを実行しましょう!
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
@riverpod
FirebaseAuth firebaseAuth(FirebaseAuthRef ref) {
return FirebaseAuth.instance;
}
// ログイン状態を監視するStreamを提供するProvider
@riverpod
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/provider/auth_provider.dart';
class PostPage extends HookConsumerWidget {
const PostPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(
actions: [
// ログアウトボタン
IconButton(
onPressed: () async {
await ref.read(firebaseAuthProvider).signOut();
},
icon: const Icon(Icons.logout),
),
],
title: const Text('Post Page'),
),
);
}
}
ログイン前のページ
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});
@override
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'),
),
],
),
),
);
}
}
アプリを実行するコード
main.dartで必要なファイルをimportして、アプリを実行できるようにします。
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});
@override
Widget build(BuildContext context, WidgetRef ref) {
return MaterialApp(
title: 'Chat App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const SplashPage(),
);
}
}
// Riverpod2.0
// class SplashPage extends ConsumerWidget {
// const SplashPage({Key? key}) : super(key: key);
// @override
// 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'),
// );
// }
// }
// Riverpod3.0
class SplashPage extends ConsumerWidget {
const SplashPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final authStateAsync = ref.watch(authStateChangeProvider);
switch (authStateAsync) {
case AsyncData(:final value):
return value != null ? const PostPage() : const SignInPage();
case AsyncError(:final error):
return Text('Error: $error');
case AsyncLoading():
return const CircularProgressIndicator();
default:
return const CircularProgressIndicator(); // すべてのケースに対するデフォルトの戻り値
}
}
}
main関数を実行するコード
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:widget_cook/firebase_options.dart';
import 'package:widget_cook/sns_app/pages/post_app.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const ProviderScope(child: PostApp()));
}
動作検証用に撮影した動画です。参考までに見てください。
感想
今回は、進化するのが速いriverpodのAsync Valueの新しい使い方を解説してみました。
whenではなくて、switchを使うと、複数の条件を書くのに適しているということで可読性が上がるからなのか推奨されるようになった感じなのでしょうか???
Riverpod3.0以降だと、switch-caseの使用が推奨されるらしいです?
今回は、ちゅーやんさんの記事を参考にさせていただきました。とはいえ、私は深いところまで追求しないので、今回は、switch文でAsync Value使えるみたいだよっていうのを再現してみただけでした。
参考になった記事
https://zenn.dev/chooyan/articles/905e5787aeae3b