FlutterでGoogleService-Info.plistを切り替える

## 概要
Flutterでアプリをビルドする際には、本番環境と開発環境で`GoogleService-info.plist`等の設定ファイルの切り替えが必要となるケースが多いと思います。
この切替には複数のSchemeを用意する方法、`Flavor`を使う方法、ビルド時の`--release`と`--debug`を使って切り分ける方法がありますが、Flutter 1.17からビルド引数の`--dart-define`で定数を渡せるようになりました。
この`--dart-define`を使って`GoogleService-info.plist`を切り替えてみましょう。

### 前提
- Flutterのサンプルプロジェクトを作成しシミュレーターで実行できる
- Flutter 1.20以降
- `--dart-define`の設定ができる（[こちらの記事](https://qiita.com/yana1316/items/542dd4f787779d154f45)を参照）

### 環境
```
$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 1.22.4, on Mac OS X 10.15.7 19H2 darwin-x64, locale en-JP)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 12.1)
[✓] Android Studio (version 4.1)
[✓] Connected device (1 available)      
```

## iOS用の設定
iOSの場合、本番環境用と開発環境用の`GoogleService-info.plist`をビルド時のスクリプトで切り替える方法となります。

### Flutterの設定
以下のように、コンパイル引数で渡します。
開発用
`flutter build ios --dart-define=DEFINE_BUILD_ENV=dev`
本番用
`flutter build ios --dart-define=DEFINE_BUILD_ENV=prod`
※ --dart-define の基本的な設定については[こちらの記事](https://qiita.com/yana1316/items/542dd4f787779d154f45)を見てください。

### Xcodeの設定
`Runner`の中に`Firebase`というフォルダを作り、その中に`GoogleService-Info.plist`を（開発用は`GoogleService-Info-Dev.plist`、本番用は`GoogleService-Info-Prod.plist`という名前で）追加します。
![GoogleService-Info_plist.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315258/3fb634c5-c978-23fe-6f47-d6372a928a1b.png)
また`Runner`ディレクトリに`GoogleService-Info.plist`を追加しTargetに追加しておきます。（中身は開発用でも本番用でも、どちらでも大丈夫で、Keyだけ残して値は消してしまっても問題ありません。）

次に`Build Phases`を開き`+`を押して`New Run Script Phase`を追加します。名前をわかりやすいように変え、既存の`Run Script`の下に移動させます。（Script名を`Select Firebase Environment`としました。）
![Runner_xcodeproj.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315258/e74182a2-5d29-5f85-1747-59186a9fc19b.png)
Scriptに以下を追加します。定数については`--dart-define`を使っています。設定については[こちらの記事](https://qiita.com/yana1316/items/542dd4f787779d154f45)を参考にしてください。

```sh
if [[ $DEFINE_BUILD_ENV == *"dev"* ]]; then
    cp $PRODUCT_NAME/Firebase/GoogleService-Info-Dev.plist $PRODUCT_NAME/GoogleService-Info.plist
elif [[ $DEFINE_BUILD_ENV == *"prod"* ]]; then
    cp $PRODUCT_NAME/Firebase/GoogleService-Info-Prod.plist $PRODUCT_NAME/GoogleService-Info.plist
else
    echo "configuration didn't match to 'dev' or 'prod'"
    echo "Your DEFINE_BUILD_ENV is '$DEFINE_BUILD_ENV'"
    exit 1
fi
```

### 動作確認
`DEFINE_BUILD_ENV`の値を開発用、本番用、どちでもない、で確認してみてください。どちらでもない値の場合はビルドエラーが発生し、エラーメッセージが表示されると思います。


## android用の設定
androidの場合も、本番環境用と開発環境用の`google-services.json`をビルド時のスクリプトで切り替える方法となります。

### Flutterの設定
以下のように、コンパイル引数で渡します。
開発用
`flutter build apk --dart-define=DEFINE_BUILD_ENV=dev`
本番用
`flutter build apk --dart-define=DEFINE_BUILD_ENV=prod`
※ --dart-define の基本的な設定については[こちらの記事](https://qiita.com/yana1316/items/542dd4f787779d154f45)を見てください。

### gradleの設定
`[project]/android/app/src/config`を作成し、その中に`dev`と`prod`ディレクトリを作成します。その中に、開発用、本番用の`google-services.json`を設置します。
![flutter_define.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315258/329e076f-cfbd-8b5b-e42d-c675e97eb211.png)



`[project]/android/app/build.gralde`を以下のように編集します。

```gradle
// 〜〜 省略 〜〜
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
    flutterVersionName = '1.0'
}

// ここから追記 --
def dartEnvironmentVariables = [
        DEFINE_BUILD_ENV: 'dev',
];

if (project.hasProperty('dart-defines')) {
    dartEnvironmentVariables = dartEnvironmentVariables + project.property('dart-defines')
            .split(',')
            .collectEntries { entry ->
                def pair = URLDecoder.decode(entry).split('=')
                [(pair.first()): pair.last()]
            }
}

// firebaseファイルをコピーして android/app ディレクトリに移動
def googleServicesJson = "src/config/${dartEnvironmentVariables.DEFINE_BUILD_ENV}/google-services.json"
task copyGoogleServicesJson(type: Copy) {
    from googleServicesJson
    into './'
    if(inputs.sourceFiles.empty){
        throw new StopExecutionException("No files found: ${googleServicesJson}")
    }
}

// firebaseファイルのコピータスクを待ってからファイルが必要な処理を行うよう設定
tasks.whenTaskAdded { task ->
    if (task.name == 'processDebugGoogleServices') {
        task.dependsOn copyGoogleServicesJson
    }
    if (task.name == 'processReleaseGoogleServices') {
        task.dependsOn copyGoogleServicesJson
    }
}

// ここまで追記 --

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
```


### 動作確認
`DEFINE_BUILD_ENV`の値を開発用、本番用、どちでもない、で確認してみてください。どちらでもない値の場合はビルドエラーが発生し、エラーメッセージが表示されると思います。

## 参考
- [Flutterで環境ごとにビルド設定を切り替える — iOS編](https://medium.com/flutter-jp/flavor-b952f2d05b5d)
- [dart-defineでFlutterアプリのFirebase開発環境と本番環境を使い分ける Android編](https://qiita.com/tetsufe/items/29cc779592171dcfb7aa)