dart-defineでFlutterアプリのFirebase開発環境と本番環境を使い分ける Android編

[dart-defineでFlutterアプリのFirebase開発環境と本番環境を使い分ける iOS編](https://qiita.com/tetsufe/items/3f2257ac12f812d3f2d6)のAndroid版です。dart-define自体の説明やflavorなどとの比較に関してはこちらの記事に書きましたので、こちらも参照していただければと思います。

本記事では、dart-defineで環境変数を設定することでFirebaseの開発用と本番用プロジェクトを使い分ける方法をご紹介します。また、dart-defineを使った方法は、flavorと同様、3種類以上のプロジェクトを使い分けるように拡張することも可能です。

## Firebaseプロジェクトの作成、ファイル配置

まず、Firebaseプロジェクトは以下のように作成しましょう。

- 開発用: com.example.test.dev
- 本番用: com.example.test

次に、以下のようにFirebaseファイルを配置します。
- android/app/src/dev/google-services.json （開発環境用Firebase設定ファイル）
- android/app/src/prod/google-services.json （本番環境用Firebase設定ファイル）


## ビルド設定

続いて、android/app/build.gradle を以下のように編集します。このように設定することで、dart-defineを使って環境変数に応じて以下の2つのことができるようになります。

- Firebaseプロジェクトの使い分け
- アプリ名の使い分け


### Flutter 1.17系の場合
```gradle
/*
略
*/
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
    flutterVersionName = '1.0'
}

// 環境変数の読み込み
def dartEnvironmentVariables = [
        APP_SUFFIX: null,
        BUILD_ENV: ''
];
if (project.hasProperty('dart-defines')) {
    dartEnvironmentVariables = dartEnvironmentVariables + project.property('dart-defines')
            .split(',')
            .collectEntries { entry ->
                def pair = entry.split('=')
                [(pair.first()): pair.last()]
            }
}

// firebaseファイルをコピーして android/app ディレクトリに移動
task copyGoogleServicesJson(type: Copy) {
    from "src/${dartEnvironmentVariables.BUILD_ENV}/google-services.json"
    into './'
}

// 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"
apply plugin: 'com.google.gms.google-services'

// 環境変数でアプリidを変更
android {
    defaultConfig {
        applicationId "com.example.test"
        applicationIdSuffix dartEnvironmentVariables.APP_SUFFIX
        minSdkVersion 23
        targetSdkVersion 28
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }
/*
略
*/
```

### Flutter 1.20系の場合
```gradle
/*
略
*/
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
    flutterVersionName = '1.0'
}

// 環境変数の読み込み
def dartEnvironmentVariables = [
        APP_SUFFIX: null,
        BUILD_ENV: ''
];
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 ディレクトリに移動
task copyGoogleServicesJson(type: Copy) {
    from "src/${dartEnvironmentVariables.BUILD_ENV}/google-services.json"
    into './'
}

// 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"
apply plugin: 'com.google.gms.google-services'

// 環境変数でアプリidを変更
android {
    defaultConfig {
        applicationId "com.example.test"
        applicationIdSuffix dartEnvironmentVariables.APP_SUFFIX
        minSdkVersion 23
        targetSdkVersion 28
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }

/*
略
*/
```

### Flutter 2.2系の場合
「Flutter 1.20系の場合」の

```gradle
                def pair = URLDecoder.decode(entry).split('=')
```

を

```gradle
                def pair = new String(entry.decodeBase64(), 'UTF-8').split('=')
```

に置き換えてください。
    
## ビルド
```
$ flutter build apk --debug --dart-define=BUNDLE_ID_SUFFIX=Dev --dart-define=BUILD_ENV=dev --dart-define=APP_SUFFIX=dev
```

本番環境では、 `--dart-define=BUILD_ENV=prod`、`--dart-define=APP_SUFFIX`はパラメータごと消去してください。


## 参考
- [Flutter 1.17 — no more Flavors, no more iOS Schemas. Command argument that changes everything](https://itnext.io/flutter-1-17-no-more-flavors-no-more-ios-schemas-command-argument-that-solves-everything-8b145ed4285d)
  - [サンプルリポジトリ](https://github.com/TatsuUkraine/flutter_define_example)
- [Flutter 1.20 — What you should know before you upgrade your project that uses compile-time variables](https://medium.com/@tatsu.ukraine/what-you-should-know-before-you-upgrade-flutter-in-your-project-with-compile-time-variables-3ec3d2e9ba79)
- [Flutterで環境ごとにビルド設定を切り替える — iOS編](https://medium.com/flutter-jp/flavor-b952f2d05b5d)
- [flutter + firebaseで本番環境と開発環境を切り替える](https://qiita.com/ampersand-Dev/items/39f7f6286058435a6bed)
- [XcodeでDevelop/Staging/Release環境を上手に切り分ける方法](https://qiita.com/Todate/items/a2e6a26731c79bd23e02#targets---build-settings%E3%81%AE%E4%BF%AE%E6%AD%A3)
- [Dart: String.fromEnvironment()の値が取得できない](https://qiita.com/kentt/items/3c2cc5d779ce17b785fd)