🐩

FirebaseAuthのログイン状態によって画面を切り替える

2023/03/29に公開

よく見るあの画面を作る

よく見かけるボトムナビゲーションバーで画面を切り替えると、ログインしていなければ、ログインページが表示されて、ログインしていたら画面が切り替わって、ログイン後のページが表示されるロジックを作るにはどうすればいいのか?

公式で解説されている authStateChanges を使います
https://firebase.google.com/docs/auth/flutter/start?hl=ja

現在の認証状態を確認する

Firebase Auth には、新規または既存の Flutter アプリケーションに安全な認証を統合するためのメソッドとユーティリティが用意されています。多くの場合、ユーザーの認証状態(ユーザーがログインしているのか、ログアウトしているのか)を把握する必要があります。

Firebase Auth では、Stream を使用して、この状態をリアルタイムで取得できます。Stream が呼び出されると、ユーザーの現在の認証状態を即時に提供します。また、認証状態が変更されるたびに、後続のイベントを提供します。

認証状態の変更をリッスンする方法は 3 つあります。

authStateChanges()
これらの変更を取得するには、FirebaseAuth インスタンスで authStateChanges() メソッドを呼び出します。

FirebaseAuth.instance
  .authStateChanges()
  .listen((User? user) {
    if (user == null) {
      print('User is currently signed out!');
    } else {
      print('User is signed in!');
    }
  });

ユーザーがログインしている状態によって、Widgetを切り替えるロジックはこのように作成します。
ログインの状態が、trueならログインしました! が表示されて、falseなら、ログインしていませんのページが表示されます。

import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';

class HomePage extends StatelessWidget {

  
  Widget build(BuildContext context) {
    return StreamBuilder(
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (BuildContext context, AsyncSnapshot<User?> snapshot) {
        if (snapshot.hasData) {
          // ログインしている場合の表示するウィジェット
          return Scaffold(
            appBar: AppBar(
              title: Text('ホーム'),
            ),
            body: Center(
              child: Text('ログインしました!'),
            ),
          );
        } else {
          // ログインしていない場合の表示するウィジェット
          return Scaffold(
            appBar: AppBar(
              title: Text('ホーム'),
            ),
            body: Center(
              child: Text('ログインしていません'),
            ),
          );
        }
      },
    );
  }
}

デモアプリを作ってみた

実際にどんな動きをするのか確かめたくて、デモアプリを作成してみました。これは、もしかしたら、Flutter仕事で使ってる人も知らないかも???

今回作成したコード

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';

class PageD extends StatelessWidget {
  const PageD({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    TextEditingController email = TextEditingController();
    TextEditingController password = TextEditingController();

    return StreamBuilder(
        stream: FirebaseAuth.instance.authStateChanges(),
        builder: (BuildContext context, AsyncSnapshot<User?> snapshot) {
          if (snapshot.hasData) {
            return Scaffold(
              body: Center(
                child: Text(
                  'ログイン中...',
                  style: TextStyle(fontSize: 30),
                ),
              ),
            );
          } else {
            return Scaffold(
              body: Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    SizedBox(
                      width: 300,
                      child: TextField(
                        controller: email,
                        decoration: InputDecoration(
                            labelText: 'メールアドレスを入力してください',
                            filled: true,
                            fillColor: Colors.grey.shade200,
                            border: OutlineInputBorder(
                              borderRadius: BorderRadius.circular(10),
                              borderSide: BorderSide.none,
                            )),
                      ),
                    ),
                    SizedBox(height: 20),
                    SizedBox(
                      width: 300,
                      child: TextField(
                        controller: password,
                        decoration: InputDecoration(
                            labelText: 'パスワードを入力してください',
                            filled: true,
                            fillColor: Colors.grey.shade200,
                            border: OutlineInputBorder(
                              borderRadius: BorderRadius.circular(10),
                              borderSide: BorderSide.none,
                            )),
                      ),
                    ),
                    ElevatedButton(
                        onPressed: () async {
                          try {
                            await FirebaseAuth.instance
                                .createUserWithEmailAndPassword(
                                    email: email.text, password: password.text);
                          } on FirebaseAuthException catch (e) {
                            if (e.code == 'invalid-email') {
                              ScaffoldMessenger.of(context).showSnackBar(
                                SnackBar(
                                  content: Text('メールアドレスのフォーマットが正しくありません'),
                                ),
                              );
                            } else if (e.code == 'user-disabled') {
                              ScaffoldMessenger.of(context).showSnackBar(
                                SnackBar(
                                  content: Text('現在指定したメールアドレスは使用できません'),
                                ),
                              );
                              print('現在指定したメールアドレスは使用できません');
                            } else if (e.code == 'user-not-found') {
                              ScaffoldMessenger.of(context).showSnackBar(
                                SnackBar(
                                  content: Text('指定したメールアドレスは登録されていません'),
                                ),
                              );
                            } else if (e.code == 'wrong-password') {
                              ScaffoldMessenger.of(context).showSnackBar(
                                SnackBar(
                                  content: Text('パスワードが間違っています'),
                                ),
                              );
                            }
                          }
                        },
                        child: Text('新規登録'))
                  ],
                ),
              ),
            );
          }
        });
  }
}

撮影した動画

https://youtu.be/NWQHO7H9itI

全体のソースコード
https://github.com/sakurakotubaki/AuthStateUI

使用したパッケージは、go_router, firebase_core, firebase_authだけです。
ソースコードは機能は少ないですが、ファイルが多いので記事には全部書いてないです。すいません🙇‍♂️

git clone して、pub getした後に、Firebase CLIを使用して、アプリとFirebaseを接続すれば、すぐに動作を試すことできるので、コピー&ペーストするよりもこっちの方が楽です。
もし、うまく行かなければ、Flutterのバージョンを合わせたり、新規作成したプロジェクトのlibディレクトリにサンプルコードを全てコピーして、コードを修正すれば使えるはずです。

まとめ

今回考えたロジックなんですけど、いつものように調べながら作ったわけではなくて、最近話題のChatGPTにコードを書いてもらって、ちょっと改造してデモアプリを作りました。
こんなもんがあったら、多分プログラミングスクール行かなくてもいい気がします😅

Discussion