Flutter+Firebaseで匿名ログイン/匿名アカウントと特定アカウントの紐付けの実装

## はじめに
今現在、メールアドレス・パスワード認証、Twitter連携認証、googleアカウント連携認証、を実装している。
このアプリに、

- 匿名ログイン機能
- 匿名アカウントとその他の認証情報の紐付け

を実装する。

## 匿名ログインの実装
次のページを参考にする。

https://qkuronekop.hatenablog.jp/entry/2019/03/15/140330

### Firebaseの設定

有効にするだけ。
![スクリーンショット 2021-10-18 12.12.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270898/c747f133-783f-df91-dc83-520da4a293f2.png)

### コードの記述
匿名ログインしたい場所で以下を呼び出すだけ。

```dart:
FirebaseAuth.signInAnonymously();
```

今回は、`sign_in_screen.dart`に新たなボタンを追加し、そこで実行するメソッドを上記にする。

```dart:sign_in_screen.dart
  // ~前略~

  Future<void> _onSignInWithAnonymousUser() async {
    final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
    try{
      await firebaseAuth.signInAnonymously();

      Navigator.of(context).pushReplacement(
          MaterialPageRoute(
            builder: (_) => PhotoListScreen(),
          )
      );
    } catch(e) {
      await showDialog(
          context: context,
          builder: (context) {
            return AlertDialog(
              title: Text('エラー'),
              content: Text(e.toString()),
            );
          }
      );
    }
  }

  // ~中略~

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Form(
        key: _formKey, //validation用
        child: Center(
          child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 24),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [

                //　~中略~
                
                // ここから新規追加
                SizedBox(height: 10),
                SizedBox(
                  width: double.infinity,
                  child: ElevatedButton(
                    onPressed: () => _onSignInWithAnonymousUser(),
                    child: Text('登録せず利用'),
                  ),
                ),
                // ここまで新規追加
              ],
            ),
          ),
        ),
      ),
    );
  }
```

### 実際の挙動
シミュレータ上で、「登録せず利用ボタン」をタップすると、きちんと画面遷移する。
<img width=300 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270898/ffae8289-d1d4-a7c5-812c-244e474c3981.jpeg"><img width=300 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270898/beb729f5-ace5-894f-7645-5f19ed8d9a96.png">

Firebase上にきちんと情報も入っている！！
![スクリーンショット_2021-10-18_12_28_35.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270898/36db1ed4-9f72-352f-f1e1-ea9ed2f96ae3.jpeg)

## 匿名アカウントとの紐付け

以下のページを参考に実装する。

https://firebase.google.com/docs/auth/ios/anonymous-auth?hl=ja

### 分岐処理の追加
匿名ユーザログイン時は、

- 「ログアウトボタン」のアイコンを変更
- 「ログアウトボタン」タップ時にログアウト処理を行わず、popではなくpushで`sign_in_screen`を表示
- 「ログアウトボタン」をタップしてsign_in_screenに遷移した際は、画面の文言を変更
- `sign_in_screen`でアカウント登録した際は、サインインではなくアカウントリンク処理を行う
- 登録後の画面遷移は、pushReplacementではなくてpop

という処理を追加する。

### 実際の挙動

匿名ユーザログイン時はログアウトボタンではなくアカウントリンクボタンを表示
<img width=300 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270898/1f1f86f2-7d39-a936-ffe7-77112580ab5a.jpeg">

アカウントリンクボタンをクリックした時は、`sign_in_screen`の表示や処理を変える
<img width=300 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270898/37ffa3c0-d725-41a3-c1fb-02e41d05611e.jpeg"><img width=300 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270898/0c64b605-c068-2010-3a03-095846c97a12.png">

アカウントリンク後は、ログアウトボタンの表示及び匿名ユーザ時に保存していたデータをそのまま見ることができる
<img width=300 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270898/9a235dee-4918-140f-ce53-659af1de1b8e.png">

きちんとリンクされている
![photo-app_-_Authentication_-_Firebase_コンソール.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270898/1d5cca23-5c76-d6d8-93cb-4c48f5c37fd0.jpeg)

### コードの記述

この記事による変更を、PRで載せておく。

https://github.com/ninoko1995/FlutterFirebaseDemo/pull/1

ちなみにだが、`photo_list_view`の表示の切り替え判定にprovider内の`userProvider`を用いると、アカウントをリンクしてもprovider内の値が更新されないため表示が切り替わらない、という問題が発生した。

なので、providerから値を取ってくるのではなく、widgetの内部で以下のようにcurrentUserを定義することとした。

```dart:
currentUser = FirebaseAuth.instance.currentUser;
```