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

# モチベーション
* 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)

```

## 最初に観測された現象
```dart
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  debugPrint('currentUser: ${FirebaseAuth.instance.currentUser?.email}');
  runApp(MyApp());
}
```

* Chromeでのデバッグ中にHotReloadする分には特におかしな挙動はなかった。
  * まぁもともとHot Reloadちょいちょい失敗するのは置いといて

### しかし、ブラウザをリロードすると`currentUser`が`null`になる
```
currentUser: null
renderLoginPage ←ログインページがbuildされた
```

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

```dart
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`を観測してみる
```dart
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`も観測してみる

```dart
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秒待ってみる
```dart
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が飛んでこない」***ことに


### 結論

```dart
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直後は`currentUser`は`null`
* しかし1.5秒程でひっそりとcurrentUserは戻っている。
* このとき`userChanges`は反応するが、`authStateChanges`には何も来ない
* `authStateChanges`は、ブラウザリロード直後は信用ならない
  * initalizeAppをindex.htmlでやってるから？？

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

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