Flutter+VScodeでFirebase認証の構築が爆速で作れた話

# 概要
Flutterアプリに認証を入れたいのでFirebaseと連携することにしてみたら思いのほか爆速で作れた話。

FlutterとFirebaseの連携を調べてたら、やれSDKを入れるやら、google-service.jsonを入れるやらの情報が出てきたりでめんどくさい印象だったんですが、、、
公式の手順をそのままやったら爆速でできました。

https://firebase.google.com/docs/flutter/setup?hl=ja&platform=android#initialize-firebase

~~いや、そりゃ公式の手順に沿ってやれよって話なんですけどね~~

# 環境
基本的には以下の公式手順から「Test Drive」まで済ませています。

https://docs.flutter.dev/get-started/install/windows

また、以下の環境で進めて行きます。
- OS：Windows11
- エディタ：VScode
- アプリの実行はAndroidエミュレータを使用

# 流れ
基本的には公式の手順のとおりですが。
## Flutterプロジェクトの作成
VScodeのコマンドパレットから「Flutter: New Project」でプロジェクトを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2420694/992b15e9-0bcb-c3e0-f68b-0153244f1985.png)
作成できたらF5（もしくは```flutter run```コマンド)でAndroidエミュレータ上でFlutterアプリが動くか確認してください。
## FirebaseとFlutterの連携
FirebaseとFlutterを連携できるようにします。
### Firebase-CLIの導入
まずCLIツールを以下を参考に導入します。

https://firebase.google.com/docs/cli?hl=ja#setup_update_cli

VScode上で開発する都合があるので、npmからインストールします。
```
npm install -g firebase-tools
```
インストールできたら、手順に従いログインとプロジェクト一覧が表示できることを確認します。
```
$ firebase login
>> ブラウザからログインする
# firebase projects:list
>> プロジェクト一覧が表示されることを確認
```

### FlutterにFirebaseを追加
以下の手順を参考に導入します。

https://firebase.google.com/docs/flutter/setup?hl=ja&platform=android

**以降はFlutterプロジェクトディレクトリ直下で実行してください**
まず、FlutterとFirebaseを連携するために必要なflutterfire_cliをインストールします。
このCLIツールがつよつよです。
```
dart pub global activate flutterfire_cli
```
次にFirebaseとの連携をセットアップします。
```
flutterfire configure
```
#### Firebaseプロジェクトの作成
```create a new project```を選択し、新規にFirebaseプロジェクトを作成します（既存のものも選択可能）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2420694/4ae1fb19-68fa-5808-6e77-94e9e46128f0.png)

:::note warn
プロジェクト名が既に使われている場合は作成できないので注意
:::

#### セットアップするプラットフォームの選択
対象のアプリがどのプラットフォーム（Android / iOSとか）をサポートするかを選択します。
選択したプラットフォームがFirebase上に自動的にセットアップされます。

今回はAndroidアプリなので```android```のみを残します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2420694/c7f67e32-b78a-a4bd-5e95-d972a2bc4e99.png)

成功するとその旨がターミナル上に表示され、```lib/firebase_options.dart```が作成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2420694/01667f73-31fe-909c-14fc-ca3bec74db0a.png)

続いて、最低限必要な```firebase_core```をインストールしておきます。
```
flutter pub add firebase_core
```

また、Firebaseの管理画面上にプロジェクトとAndroidアプリが登録されていることが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2420694/452953c2-8e03-fc91-2967-3366db686cee.png)

ここまででFlutterでFirebaseを使うすべての準備は完了です。

:::note
汎用的な手順でやろうとすると、GUIからプロジェクト作ってアプリ登録して、SDKを入れるためにgoogle-service.jsonをDLしてFlutterプロジェクトに配置して、、という感じですが、それらの手順をすべて省くことができます。
:::

### 動作確認
```main.dart```を以下のようにします。コードのポイントとしては以下のとおり

- main()にFirebaseの初期化コード```Firebase.initializeApp```を追記
- ```Firebase.initializeApp```が非同期で処理するので```main()```に```async```を追加
- 非同期処理の初期化のため```WidgetsFlutterBinding.ensureInitialized();```を実行するようにする

```dart:main.dart
import 'package:flutter/material.dart';
// firebase関連のパッケージをインポート
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';

// Firebaseの初期化を非同期で行うので、main()にasyncを追加
void main() async {
  // 非同期処理の初期化
  WidgetsFlutterBinding.ensureInitialized();
  // Firebaseの初期化
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Hello Flutter'),
        ),
        body: Center(
          child: Text('Hello Flutter'),
        ),
      ),
    );
  }
}
```

一旦これで動くことを確認してください。
ひとまずエラーなくアプリの画面が出ればOKです。

:::note warn
VScodeのF5（デバッグモード）でうまく動作しない場合、ターミナルから直接```flutter run```コマンドを打って立ち上げてください。何故かそういう現象が発生したときがありました。環境の問題とは思いますが･･･
:::

## Firebaseの認証を利用できるようにする
Firebaseで認証ができるようにします。まずは画面を作ります。

### ログイン画面の作成
```main.dart```を以下のように修正します。ポイントは以下のとおりです。

- ログインID・パスワードの入力欄を作成
- **ログインIDとパスワードの入力値を取得して使わないといけないので、MyAppをStatefullWidgetに変更します。**
- 入力欄の変更を検知し、入力内容を変数に格納します。

```dart:main.dart
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';

void main() async {
  // Firebaseの初期化を待機
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(MyApp());
}

// ログインID・パスワードの値を使うのでStatefullWidgetに変更
class MyApp extends StatefulWidget {
  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  // ログインID
  String LoginEmail = "";
  // パスワード
  String LoginPassword = "";
  // ログインメッセージ
  String LoginMessage = "Not Login";

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Hello Flutter'),
        ),
        body: Container(
          padding: EdgeInsets.all(16),
          child: Column(
            children: [
              // メールアドレス入力欄
              TextFormField(
                decoration: InputDecoration(labelText: "メールアドレス"),
                onChanged: (String inputValue) {
                  // 値が変更された際に入力内容を変数に保持
                  setState(() {
                    LoginEmail = inputValue;
                  });
                },
              ),
              // パスワード入力欄
              TextFormField(
                decoration: InputDecoration(labelText: "パスワード"),
                obscureText: true,
                onChanged: (String inputValue) {
                  // 値が変更された際に入力内容を変数に保持
                  setState(() {
                    LoginPassword = inputValue;
                  });
                },
              ),
              ElevatedButton(
                onPressed: ()  {
                },
                child: Text("Auth"),
              ),
              Text(LoginMessage)
            ],
          ),
        ),
      ),
    );
  }
}

```

実行できるか確認します。以下のような画面で立ち上がればOKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2420694/a1d87b53-88df-bed8-9c2a-efe978773b3d.png)


### ログイン機能を追加する
#### Firebaseのログイン処理の追加
```ElevatedButton```の```onPress```処理にFirebaseを使って認証をする処理を追加します。
認証には```FirebaseAuth.instance.signInWithEmailAndPassword```を使います。

```
import 'package:firebase_auth/firebase_auth.dart';

FirebaseAuth.instance.signInWithEmailAndPassword(email: [メールアドレス], password: [パスワード])
```

まず、必要なパッケージをインストールします。
```
flutter pub add firebase_auth
```

次に、ログインボタンの```onPressed```に処理をFirebase認証処理を追加します。
コードのポイントとしては、以下のとおりです。

- Firebaseへの認証を非同期```(await)```で実行するので、```onPressed```でasync関数を実行します。
- 認証結果をUserCredentialプロパティで受け取ります。
- 認証成功時にはUserプロパティでログインユーザーを取得します。
- try-catchで認証失敗時には例外処理を実装しています。

```dart:main.dart

//...
ElevatedButton(
    onPressed: () async {
      try {
        // Firebase認証処理
        final UserCredential result = await FirebaseAuth.instance
            .signInWithEmailAndPassword(
                email: LoginEmail, password: LoginPassword);
        // ログイン成功時にはユーザーを取得
        final User user = result.user!;
        setState(() {
          LoginMessage = "LOGIN SUCCEED - ${user.email}";
        });
      } catch (e) {
        // ログイン失敗時
        setState(() {
          LoginMessage = "LOGIN FAILED - ${e.toString()}";
        });
      }
    },
    child: Text("Auth"),
  ),
//...
```

### Firebaseにログインアカウントを設定する
Firebaseの管理画面からログインについての設定を行います。
「Authentication」の画面を開き、ログインプロバイダから**メール / パスワード**を選択して有効化します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2420694/7d1cbdbf-605d-36fb-0434-4fa4dea44780.png)

次にメール・パスワードの追加を行います。「User」タブからメール / パスワードユーザーを追加します。
値はひとまずダミーのもので構いません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2420694/c14b2e41-0c23-c8a0-d647-f0ac4167cb65.png)

## 動作確認
準備が整ったのでFirebaseによる認証を検証します。
Flutterアプリを立ち上げ、メールアドレスとパスワードを入れて認証します。

### 正しいメールアドレスとパスワードで認証
**LOGIN SUCCEED**となれば認証成功です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2420694/41b3415c-62d0-884d-c14a-e20ce8a07e81.png)

### 間違ったメールアドレスとパスワードで認証
**LOGIN FAILED**となり、認証失敗のエラーが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2420694/0553af6b-5424-c53a-0833-545a9535c969.png)

## ここからできること
この記事では書きませんけど、ここまでできたら以下のこともできます。

- Firebaseの他のサービスの利用（Database / Storage / Functionなどなど）
- ログイン処理後にナビゲーションを設定して他の画面に遷移


# おわり
ネットでかじった情報じゃなくて公式の手順読めよっていうだけの記事なんですけどね。
ほら、新しい電化製品買っても説明書は読まないですよね？~~俺は読まない~~