FlutterFire CLIを利用してFlutter×Firebaseの環境構築をする

Flutter 2.8のアップデートと共に、Flutter×Firebaseの更新が多数なされました。

その中でも、Flutter×Firebaseの環境構築を自動化してくれる、
**FlutterFire CLI**の追加は目を引くアップデートだったのではないでしょうか。

今回は、FlutterFire CLIを利用してFlutter×Firebaseの環境構築をする方法について記載します。

参考にする公式ドキュメントはこちらです。

https://firebase.flutter.dev/docs/cli/

:::note warn
2022/1/26追記
Firebase Analytics, Crashlytics,Performance Monitoringを使う場合、
動作しない、または正常通り動作しないようで、従来の手法での環境構築が推奨されています。
また、google_sign_inを使ったFirebase_authを使う場合もGoogle services JSONの従来の手法でのセットアップが必要となります。
詳細は上記ドキュメントのUsageをご確認ください。
:::


##前準備##

事前準備としてFirebase CLIがインストールされている必要があります。

以下のページを参考にインストールしてください。

https://firebase.google.com/docs/cli#sign-in-test-cli

##FlutterFire CLIのインストール##

以下のコマンドを、ターミナル(コマンドプロンプト)で実行してください。

```zsh
dart pub global activate flutterfire_cli
```

:::note warn
公式ドキュメントには実行場所について記載されていません。
今回は念の為Root（ユーザー名のフォルダ）にて実行して確認しています。
:::

###ワーニングが発生した場合###

上記の結果、以下のワーニングが発生する場合があります。

<img width="700" alt="スクリーンショット 2021-12-12 18.16.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1694109/1bff8aa1-2601-114e-0e65-5d027d212add.png">

この場合、Flutter のプロジェクトファイルのルートで``flutterfire``コマンドを実行しても、
``zsh: command not found: flutterfire``となり、実行できません。

この場合、ワーニングに記載の処理を実行しましょう。
具体的には``.bashrc``,または``.zshrc``等のファイルに、以下のコードを追加します。

```zsh
export PATH="$PATH":"$HOME/.pub-cache/bin"
```

追加の仕方については以下の記事が参考になりました。

https://qiita.com/k3ntar0/items/eb8cdbd8eba9da388def

以上でFlutterFire CLIのインストールは完了となります。

##FlutterFire CLI の利用##

実際にFlutterFire CLIを使い、Firebaseの環境構築をしてみましょう。

ターミナルからFlutter プロジェクトのルートで以下のコードを実行してください。

```zsh
flutterfire configure
```
実行すると以下のようにFirebaseのProject選択画面に移ります。
(Firebase CLIの``firebase login``でログインしたアカウントのプロジェクトが表示されます。)

<img width="700" alt="スクリーンショット 2021-12-12 19.12.39のコピー.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1694109/07ba60d3-e6e4-2166-8788-93b7b13cb14e.png">

今回は``create a new project``で新しくプロジェクトを作成してみましょう。
Enterを押すと、プロジェクト名を入力するよう求められます。

<img width="900" alt="スクリーンショット 2021-12-12 19.13.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1694109/7f0003b0-0d0b-f947-002f-64343d62facf.png">

入力しプロジェクト作成が終わると、プラットフォームの選択肢が表示されます。
spaceキーでチェックを入れ、Enterキーで決定しましょう。
今回は、AndroidとiOSを選択してみます。

<img width="900" alt="スクリーンショット 2021-12-12 19.14.25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1694109/1531fe6c-43ac-ef75-f3a6-27c143b3c3d2.png">

Firebaseプロジェクトに対し、Androidのアプリ登録が実行されます。
Androidのbundle IDがFlutter Project内に既に記載されていれば、そのIDでアプリが登録されます。
今回は記載されていたので、そのまま登録されました。

<img width="1200" alt="スクリーンショット 2021-12-12 19.16.56のコピー.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1694109/a0d49ada-df74-7e41-142b-ab6bed04e3ef.png">

次にiOSのアプリ登録が実行されます。
同様に、iOSの　bundle ID がFlutter Project内に既に記載されていれば、そのIDでアプリが登録されます。
今回は記載されていなかったため、 bundle　IDの入力を求められました。

<img width="1200" alt="スクリーンショット 2021-12-12 19.18.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1694109/660f5ce2-0176-75d9-d835-020534c13aa8.png">

入力が終わると、入力したbundle IDでアプリが登録されます。
登録が完了すると、``lib``フォルダ内に、``firebase_options.dart``が生成され、CLIツールが終了します。

<img width="1200" alt="スクリーンショット 2021-12-12 19.19.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1694109/416dc2b8-6a0c-9ffa-708b-6c9ebe63d005.png">

以上で環境構築は完了です。

:::note
実際にFirebase Consoleを覗いてみると、
プロジェクトが作成され、設定したbundle IDのアプリが登録されていることがわかります。
:::

###firebase_options.dartについて###

生成された``firebase_options.dart``がCLIツールを使った環境構築のキモです。
このファイルには、Firebaseを取り扱うのに必要な``apiKey``等の情報が盛り込まれています。
``google-services.json``等から必要な情報を抜き出したファイルのようです。

また、このファイル内のクラスのメソッドで、プラットフォームで条件分岐して必要な情報を取れるようになっています。
このメソッドを初期化時に呼び出すことで、Firebaseにアクセスし、Flutterプロジェクトを初期化する分けです。

実際に生成された``firebase_options.dart``が以下となります。
（``apiKey``等は ``test``に置き換えています）

```dart
// File generated by FlutterFire CLI.
// ignore_for_file: lines_longer_than_80_chars
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart'
    show defaultTargetPlatform, kIsWeb, TargetPlatform;

/// Default [FirebaseOptions] for use with your Firebase apps.
///
/// Example:
/// ```dart
/// import 'firebase_options.dart';
/// // ...
/// await Firebase.initializeApp(
///   options: DefaultFirebaseOptions.currentPlatform,
/// );
/// ```
class DefaultFirebaseOptions {
  static FirebaseOptions get currentPlatform {
    if (kIsWeb) {
      throw UnsupportedError(
        'DefaultFirebaseOptions have not been configured for web - '
        'you can reconfigure this by running the FlutterFire CLI again.',
      );
    }
    // ignore: missing_enum_constant_in_switch
    switch (defaultTargetPlatform) {
      case TargetPlatform.android:
        return android;
      case TargetPlatform.iOS:
        return ios;
      case TargetPlatform.macOS:
        throw UnsupportedError(
          'DefaultFirebaseOptions have not been configured for macos - '
          'you can reconfigure this by running the FlutterFire CLI again.',
        );
    }

    throw UnsupportedError(
      'DefaultFirebaseOptions are not supported for this platform.',
    );
  }

  static const FirebaseOptions android = FirebaseOptions(
    apiKey: 'test',
    appId: 'test',
    messagingSenderId: 'test',
    projectId: 'flutter-fire-cli-test-project',
    storageBucket: 'flutter-fire-cli-test-project.appspot.com',
  );

  static const FirebaseOptions ios = FirebaseOptions(
    apiKey: 'test',
    appId: 'test',
    messagingSenderId: 'test',
    projectId: 'flutter-fire-cli-test-project',
    storageBucket: 'flutter-fire-cli-test-project.appspot.com',
    iosClientId: 'test',
    iosBundleId: 'com.example.flutterfireclitest',
  );
}
```

###初期化処理###

``flutter_core``パッケージをインストールしていない場合は、先にインストールしましょう。
初期化処理を記載する前に、記載するファイルで以下の``import``文を記載します。

```dart
import 'firebase_options.dart';
```

初期化処理は以下の通りです。

```dart
await Firebase.initializeApp(
  options: DefaultFirebaseOptions.currentPlatform,
);
```

``firebase_options.dart``の``DefaultFirebaseOptions.currentPlatform``を呼び出していることがわかります。

:::note warn
main.dartのrunAppより前で実行する場合は、Firebase.initializeより前で、
WidgetsFlutterBinding.ensureInitialized();
を実行してください。
:::

##従来との比較##

環境構築の内容を比較してみます。
``Firebase.initialize``を記載する手前までの手順で比較します。

###従来###

1. Firebase ConsoleからFirebase　プロジェクトを作成
2. Firebase　プロジェクト　でAndroid　のアプリを作成
3. ``google-services.json``をダウンロードし、Flutterのプロジェクト内の適切な場所に配置
4. ``build.gradle``に``apply plugin: 'com.google.gms.google-services'``等の文を追記
5. Firebase　プロジェクト　でiOSのアプリを作成
6. ``GoogleService-info.plist``をダウンロードし、xcodeを立ち上げ、 Flutterのプロジェクト内の適切な場所に配置
7. 完了！

###CLIを使用###

1. ターミナルからFlutter Projectのルートで``firebase configure``を実行
2. プロジェクト選択画面で``create a new project`` を選択、Project名を入力
3. 作成するプラットフォーム(iOS,Androidなど)を選択
4. iOSのBundle Nameを入力
5. 完了！

CLIツールの方が、手順少なく環境構築を完了できます。
また、ファイルを行ったり来たりせず、ターミナル上で完結するのも良い点ですね。

##課題とまとめ##
このツールでできるのは、あくまでFirebaseの環境構築であり、
FirestoreやFirebaseAuthの初期設定を行なってくれるものではありません。
これらはConsole上から設定する必要があります。

とはいえ、Firebaseのプロジェクト作成から環境構築まで一つのツールでできるようになったのは、
本当に素晴らしいことだと思います。

従来と勝手が違うことに戸惑うかもしれませんが、積極的に使ってみることをお勧めします。

以上、読んでいただきありがとうございました。