AsyncValueでswitch文が使えるらしいぞ?

# 概要
Riverpod3.0から、whenメソッドではなくて、switchが推奨されるらしい？
あまり情報がないので、試行錯誤して、動くものを作ってみました。

これが以前の書き方です。
```dart
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からはこちらが推奨されるとのことらしいです？
```dart
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`を使って実験してみました。

**こちらがソースコード**

プロバイダーを定義したら、ファイルを自動生成するコマンドを実行しましょう!

```dart
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();
}
```

**自動生成するコマンド**
```bash
flutter pub run build_runner watch --delete-conflicting-outputs
```

**ログイン後のページとログイン前のページを作成する**

匿名認証でログインをする機能とログアウト機能を実装したページを作成する。

ログイン後のページ
```dart
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'),
      ),
    );
  }
}
```

ログイン前のページ
```dart
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して、アプリを実行できるようにします。

```dart
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関数を実行するコード
```dart
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()));
}
```

動作検証用に撮影した動画です。参考までに見てください。

https://youtu.be/9Cwz5EPkNPI

## 感想
今回は、進化するのが速いriverpodのAsync Valueの新しい使い方を解説してみました。
whenではなくて、switchを使うと、複数の条件を書くのに適しているということで可読性が上がるからなのか推奨されるようになった感じなのでしょうか？？？

>Riverpod3.0以降だと、switch-caseの使用が推奨されるらしいです?

今回は、ちゅーやんさんの記事を参考にさせていただきました。とはいえ、私は深いところまで追求しないので、今回は、switch文でAsync Value使えるみたいだよっていうのを再現してみただけでした。
**参考になった記事**
https://zenn.dev/chooyan/articles/905e5787aeae3b

https://www.docswell.com/s/riscait/ZRX97R-riverpod3-and-tips4#p6