[Flutter x Firebase] カウンターアプリに認証機能を追加する 〜自動ログイン〜

4 min読了の目安(約3800字TECH技術記事

やること

  • 前回作成したソーシャルログイン機能つきのカウンターアプリに自動ログイン機能を追加する

前回記事

[Flutter x Firebase] カウンターアプリに認証機能を追加する

前回は、flutter createで生成されるカウンターアプリを、Firebase Authenticationを使って、Googleアカウントによるソーシャルログインができるカウンターアプリへと進化(?)させた。

前回作成したカウンターアプリのいけてない点

  • アプリを再起動するたびにログインしないとカウンターが使えない
    →アプリの初期画面がログイン画面になっている。
    →Firebaseから見たログイン状態に応じて、初期画面を切り替える必要がある。

成果物

動作イメージ

前回と同じ。ログアウトせずに再起動したときに、右から2番目のカウンター画面が表示されるようにしたい。

ソースコード

ポイント

  • ログインしているかどうかのチェック
  • ログイン状態に応じて、ログイン画面かカウンター画面のどちらを表示するかを切り替える

雑な絵

ログイン状態のチェック

参考記事: 【Flutter備忘録】Firebaseを使って再起動後もログイン状態を保持する - Qiita

FirebaseAuth.instance.currentUserで現在ログイン中のユーザーの情報を取ってくることができる。

ログアウトせずにアプリを閉じた場合はcurrentUserでユーザー情報を取ってこれるので、初期化時にcurrentUserをチェックすることでログイン状態の判別ができる。

auth_model.dart
class AuthModel extends ChangeNotifier {
  User _user;

  final FirebaseAuth _auth = FirebaseAuth.instance;

  AuthModel() {
    final User _currentUser = _auth.currentUser;

    if (_currentUser != null) {
      _user = _currentUser;
      notifyListeners();
    }
  }

  User get user => _user;
  bool get loggedIn => _user != null;
  
  ~~
}
余談

currentUserの説明

Returns the current [User] if they are currently signed-in, or null if not.
You should not use this getter to determine the users current state, instead use [authStateChanges], [idTokenChanges] or [userChanges] to subscribe to updates.
--
現在サインインしている場合は現在の[User]を返し、そうでない場合は nullを返します。
このゲッターを使用してユーザーの現在の状態を判別するのではなく、[authStateChanges]、[idTokenChanges]、または[userChanges]を使用して更新をサブスクライブする必要があります。

「このゲッターを使用してユーザーの現在の状態を判別するのではなく・・・」とあるが、簡単のため今回はcurrentUserを使ってログイン状態を判別する。
authStateChanges,idTokenChanges,userChangesについてもどう使ってどう動くのか実験してみたい。

ログイン状態に応じて画面を切り替える

上記でログインしているかどうかを知ることができたが、最初の画面をログイン画面に設定しているので、結局毎回ログインしないといけない。。。

なので、初期画面として新たなWidget(_LoginCheck)を用意し、その中でFirebaseのログイン状態に応じて、ログイン画面を表示するか・カウンター画面を表示するかを切り替えることにした。

main.dart
class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => AuthModel(),
      child: MaterialApp(
        ~~
        home: _LoginCheck(), // <- 初期画面を _LoginCheck ウィジェットに設定
        ~~
      ),
    );
  }
}

// 新たに追加
class _LoginCheck extends StatelessWidget {
  
  Widget build(BuildContext context) {
    // ログイン状態に応じて、画面を切り替える
    final bool _loggedIn = context.watch<AuthModel>().loggedIn;
    return _loggedIn
        ? MyHomePage(
            title: 'カウンター',
          )
        : LoginForm();
  }
}

また、ログイン状態によって画面を切り替える_LoginCheckウィジェットを間に挟むことにより、ログイン画面とカウンター画面間でNavigatorによる画面遷移をせずとも画面が切り替わるようになった。
そのため、不要なNavigator.pushAndRemoveUntilは消してもOK。(消さなくても動く)

login_form.dart
   Future<bool> _login(BuildContext context) async {
    bool loggedIn = false;
    EasyLoading.show(status: 'loading...');
    if (await context.read<AuthModel>().login()) {
      loggedIn = true;
      // Navigator.pushAndRemoveUntil(
      //   context,
      //   MaterialPageRoute(builder: (_) => MyHomePage(title: "カウンター")),
      //   (_) => false,
      // );
    }
    EasyLoading.dismiss();
    return loggedIn;
main.dart
  Future<void> _logout() async {
    await context.read<AuthModel>().logout();
    // Navigator.pushAndRemoveUntil(
    //   context,
    //   MaterialPageRoute(
    //     builder: (_) => LoginForm(),
    //   ),
    //   (route) => false,
    // );
  }
余談

初期画面をログイン画面(またはカウンター画面)にしておいて、ログイン状態に応じてカウンター画面(またはログイン画面)に遷移させる、というやり方もあったが、初期画面で画面遷移が起きるのがなんとなく気に入らなかったので、切り替える方式を選択。