LoginSignup
17
10

More than 3 years have passed since last update.

Flutter Web+Firebase Authにおけるブラウザリロード問題を解消してみた

Last updated at Posted at 2020-12-08

モチベーション

  • Flutter WebでのFirebaseAuthライブラリ(firebase_auth_web)の挙動が気色悪いため、場当たり的な解決になってしまった
  • バグっぽい挙動にも思えるが、一旦現在の状態で解決索を備忘しておく

環境

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel beta, 1.24.0-10.2.pre, on Mac OS X 10.15.7 19H15 darwin-x64, locale ja-JP)
[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
[✓] Xcode - develop for iOS and macOS (Xcode 12.2)
[✓] Chrome - develop for the web
[✓] Android Studio (version 4.1)
[✓] VS Code (version 1.51.1)
[✓] Connected device (4 available)

最初に観測された現象

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  debugPrint('currentUser: ${FirebaseAuth.instance.currentUser?.email}');
  runApp(MyApp());
}
  • Chromeでのデバッグ中にHotReloadする分には特におかしな挙動はなかった。
    • まぁもともとHot Reloadちょいちょい失敗するのは置いといて

しかし、ブラウザをリロードするとcurrentUsernullになる

currentUser: null
renderLoginPage ←ログインページがbuildされた

firebase_auth_webにはsetPersistentがあったのを思い出す

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // 追加
  await FirebaseAuth.instance.setPersistence(Persistence.LOCAL);
  debugPrint('currentUser: ${FirebaseAuth.instance.currentUser?.email}');
  runApp(MyApp());
}

ログイン後にブラウザリロードしてみる

currentUser: null
renderLoginPage

だめじゃん🤔

よくわからないのでStream<User> authStateChangesを観測してみる

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await FirebaseAuth.instance.setPersistence(Persistence.LOCAL);
  // 追加
  FirebaseAuth.instance.authStateChanges().listen((event) {
    debugPrint('authStateChanges: ${event?.email}');
  });
  debugPrint('currentUser: ${FirebaseAuth.instance.currentUser?.email}');
  runApp(MyApp());
}

ログイン後にブラウザリロードすると

currentUser: null
authStateChanges: null
renderLoginPage

うん、知ってた

しかし、この後にHotRestartをすると...

currentUser: rr.yamamoto@gmail.com
authStateChanges: rr.yamamoto@gmail.com
renderTodoPage ←ログイン後の画面ちゃんと出た

🤔🤔🤔🤔🤔🤔???

  • setPersistence(Persistence.LOCAL)は一応、効いてるっぽい?

よくわからないのでStream<User> userChangesも観測してみる

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await FirebaseAuth.instance.setPersistence(Persistence.LOCAL);
  FirebaseAuth.instance.authStateChanges().listen((event) {
    debugPrint('authStateChanges: ${event?.email}');
  });
  // 追加
  FirebaseAuth.instance.userChanges().listen((event) {
    debugPrint('userChanges: ${event?.email}');
  });
  debugPrint('currentUser: ${FirebaseAuth.instance.currentUser?.email}');
  runApp(MyApp());
}

ログイン後にブラウザリロードすると

currentUser: null
authStateChanges: null
userChanges: null
renderLoginPage
userChanges: rr.yamamoto@gmail.com ←🤔🤔🤔🤔🤔🤔🤔🤔

画面的にはログイン画面が出たまま。
つまり、レンダリング後にcurrentUserが復活している
→currentUserの復帰処理が、runApp()に間に合ってない...?

そんなら雑にFuture.delayedで2秒待ってみる

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await FirebaseAuth.instance.setPersistence(Persistence.LOCAL);
  FirebaseAuth.instance.authStateChanges().listen((event) {
    debugPrint('authStateChanges: ${event?.email}');
  });
  FirebaseAuth.instance.userChanges().listen((event) {
    debugPrint('userChanges: ${event?.email}');
  });
  debugPrint('currentUser: ${FirebaseAuth.instance.currentUser?.email}');

  // 追加してみるが、ほんまかいな?
  await Future.delayed(Duration(seconds: 2));

  runApp(MyApp());
}

ほんまやった

currentUser: null
authStateChanges: null
userChanges: null
userChanges: rr.yamamoto@gmail.com 
renderTodoPage ←ちゃんとログイン後ページ出てる

そして...

みなさんお気づきだろうか...「authStateChangesが飛んでこない」ことに

結論

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await FirebaseAuth.instance.setPersistence(Persistence.LOCAL);

  if (FirebaseAuth.instance.currentUser == null) {
    // 2秒過ぎるかcurrentUserが復帰してきたらレンダリング開始
    await Future.any([
      FirebaseAuth.instance.userChanges().firstWhere((u) => u != null),
      Future.delayed(Duration(milliseconds: 2000)),
    ]);
  }

  runApp(MyApp());
  • ブラウザリロードされると、HotRestartしたのと同じ状態になる
  • setPersistenceしててもmain直後はcurrentUsernull
  • しかし1.5秒程でひっそりとcurrentUserは戻っている。
  • このときuserChangesは反応するが、authStateChangesには何も来ない
  • authStateChangesは、ブラウザリロード直後は信用ならない
    • initalizeAppをindex.htmlでやってるから??

とりあえずリロードでどっかいっちゃう問題は解消したが、どうみても嘘くさい解決方法なので真似しないように

  • 本格的に本家のソース追っかけないとわからんちん誰かよろしく
17
10
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
10