【Flutter】STG・商用環境を簡単に分ける方法(dart-define-from-file)

menu事業部 フロントエンドエンジニアの坂井田です。
業務で開発する際、開発環境と本番環境を分けたいという場面があるのではないでしょうか。
今回は、それをFlutterで実現する方法についてご紹介します!

環境(Flavor)ごとに設定を分ける方法

ズバリ、dart-define-from-file というオプションを使用することで簡単に実現できます!
dart-define-from-file は Flutter 3.7 (2023/1/25 リリース) から利用可能になった機能で、ビルド時に指定したjsonファイルを使って各種設定を切り替えてビルドすることができます。

この記事では、以下を実現するために必要な設定について説明します。

  • 1️⃣ アイコンをFlavorごとに分ける
  • 2️⃣ ホーム画面に表示される名前をFlavorごとに分ける
  • 3️⃣ 別アプリとしてインストールできるようにする

はじめに:Flavorを用意する

dart_defines ディレクトリを作成し、その中に以下2つのファイルを作り渡したい値を記述します。
ここでは、アプリ名・アイコン・別アプリとしてインストールするための設定を記述しています。

stg.json

{
    "flavor": "stg",
    "appName": "(stg) FlavorSplit",
    "appIdSuffix": ".stg"
}

prod.json

{
    "flavor": "prod",
    "appName": "FlavorSplit",
    "appIdSuffix": ""
}

こうすることで、ビルド時にオプションで指定すると各jsonをもとに設定を切り替えることができるようになります。
STG環境でapkをビルドする例:flutter build apk --dart-define-from-file=dart_defines/stg.json

便利な設定:VSCodeデバッグするFlavorを選択できるようにする

.vscode/launch.jsonconfigurations に選択肢の設定を記述します。
それぞれ args の部分で dart-define-from-file のオプションを指定してください。

{
    "version": "0.2.0",
    "configurations": [
      {
        "name": "Debug dev",
        "request": "launch",
        "type": "dart",
        "flutterMode": "debug",
        "preLaunchTask": "create_env_dev",
        "args": [
          "--dart-define-from-file=dart_defines/dev.json",
        ]
      },
      {
        "name": "Debug stg",
        "request": "launch",
        "type": "dart",
        "flutterMode": "debug",
        "preLaunchTask": "create_env_stg",
        "args": [
          "--dart-define-from-file=dart_defines/stg.json",
        ]
      },
      {
        "name": "Debug prod",
        "request": "launch",
        "type": "dart",
        "flutterMode": "debug",
        "preLaunchTask": "create_env_prod",
        "args": [
          "--dart-define-from-file=dart_defines/prod.json",
        ]
      },
    ]
}

1️⃣ アイコンをFlavorごとに変える

各OSごとに必要なサイズのアイコンを自動生成してくれるパッケージ flutter_launcher_icons を利用すると便利です。

pub.dev

以下のような各Flavorのアイコンを用意し、assets/launcher_icon 内に配置します。

icon-stg.png icon-prod.png

次に自動生成の設定ファイルを作成します。

flutter_launcher_icons-stg.yaml

flutter_icons:
  android: true
  ios: true
  image_path: "assets/launcher_icon/icon-stg.png"

flutter_launcher_icons-prod.yaml

flutter_icons:
  android: true
  ios: true
  image_path: "assets/launcher_icon/icon-prod.png"

pubspec.yaml に以下の記述を追加します。

flutter_icons:
  android: true
  ios: true

あとは以下のコマンドを実行すると、設定に沿って必要なアイコンを自動生成できます。

flutter pub run flutter_launcher_icons:main

ただ、これだけでは各Flavorのアイコンが適用されないため、OSごとにネイティブの設定を変更する必要があります。

🍏 Android

android/app/build.gradle の最後に以下のコードを追加します。

task copySources(type: Copy) {
   from "src/$flavor/res"
   into 'src/main/res'
}
tasks.whenTaskAdded { task ->
   task.dependsOn copySources
}

🍎 iOS

ios/Runner.xcodeproj/project.pbxproj にあるASSETCATALOG_COMPILER_APPICON_NAME の記述をすべて以下のように変更します。

ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-$(flavor)";

2️⃣ ホーム画面に表示される名前をFlavorごとに分ける

jsonで設定したアプリ名になるようにします。

🍏 Android

android/app/build.gradle にある defaultConfig の中に、以下の記述を追加します。

resValue "string", "app_name", appName

また、android/app/src/main/AndroidManifest.xml にある label を以下のように変更します。

android:label="@string/app_name"

🍎 iOS

ios/Runner/Info.plist にある CFBundleNameCFBundleDisplayName のstringを、以下のように変更します。

<string>$(appName)</string>

3️⃣ 別アプリとしてインストールできるようにする

Androidでいうパッケージ名、iOSでいうバンドルIDをFlavorごとに変更することで、一つの端末に別アプリとしてインストールできるようになります。
Androidのアイコンの周りに余白がありますが、これについては別の記事で説明します。

🍏 Android

android/app/build.gradle にある defaultConfig の中に、以下の記述を追加します。

applicationIdSuffix appIdSuffix

🍎 iOS

ios/Runner.xcodeproj/project.pbxproj にある PRODUCT_BUNDLE_IDENTIFIER の部分を以下のように変更します。
RunnerTestsの行はそのままにする

PRODUCT_BUNDLE_IDENTIFIER = "<元々のバンドルID>$(appIdSuffix)";

これで、主要3点の設定は完了です。

ビルドしてみる

いつものビルドコマンドに、オプションとして dart-define-from-file とビルドに使用するjsonファイルのパスを書くことでビルドすることができます。
例えば、STG環境のAndroidアプリをビルドする場合は以下のコマンドになります。

flutter build apk --dart-define-from-file=dart_defines/stg.json

必要に応じてMakefileを作ると便利かもしれません。

build-android:
    flutter build apk --dart-define-from-file=dart_defines/$(flavor).json

上記Makefileを作成すると、以下のコマンドでビルドできるようになります。

make build-android flavor=stg

あとは、dart-define-from-file で設定した値をアプリ内で受け取る方法について説明します。

アプリ内でFlavorを判別する

やり方は簡単で、String.fromEnvironment('<設定した値のキー>') と書くだけで取得できます。
例えば、Flavor名を取得したい場合は以下のように記述します。

const flavorName = String.fromEnvironment('flavor');

これを表示してみると、Flavorごとに異なる値が表示されることを確認できます。

STG 商用

最後に

いかがでしょうか。
この他にも任意の値をビルドごとに分けることができるので、必要に応じて値を追加してみてください。