🗺️
AsyncValue switch case
Overview
Riverpod3.0から、whenメソッドではなくて、switchが推奨されるらしい?
あまり情報がないので、試行錯誤して、動くものを作ってみました。
これが以前の書き方です。Riverpod2.0のコードですね。
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'),
);
}
}
Riverpod3.0からはこちらが推奨されるとのことらしいです?
// Dart3
class SplashPage extends ConsumerWidget {
const SplashPage({Key? key}) : super(key: key);
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(); // すべてのケースに対するデフォルトの戻り値
}
}
}
summary
今回は、ログインをしているか認証が通っているのを監視するauthStateChange
を使って実験してみました。
こちらがソースコード
プロバイダーを定義したら、ファイルを自動生成するコマンドを実行しましょう!
認証用のProviderを作成する
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/provider/auth_provider.dart';
class PostPage extends HookConsumerWidget {
const PostPage({super.key});
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});
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});
Widget build(BuildContext context, WidgetRef ref) {
return MaterialApp(
title: 'Chat App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const SplashPage(),
);
}
}
// Dart2
// 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'),
// );
// }
// }
// Dart3
class SplashPage extends ConsumerWidget {
const SplashPage({Key? key}) : super(key: key);
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()));
}
動作検証用に撮影した動画です。参考までに見てください。
thoughts
今回は、進化するのが速いriverpodのAsync Valueの新しい使い方を解説してみました。
whenではなくて、switchを使うと、複数の条件を書くのに適しているということで可読性が上がるからなのか推奨されるようになった感じなのでしょうか???
Riverpod3.0以降だと、switch-caseの使用が推奨されるらしいです?
今回は、ちゅーやんさんの記事を参考にさせていただきました。とはいえ、私は深いところまで追求しないので、今回は、switch文でAsync Value使えるみたいだよっていうのを再現してみただけでした。
参考になった記事
Discussion