【Flutter 3.7未満】Dart-defineのみを使って開発環境と本番環境を分ける
| 開発環境 | ステージング環境 | 本番環境 |
|---|---|---|
![]() |
![]() |
![]() |
こんにちは、Flutterでのアプリ開発をメインとしている「Altive株式会社」の村松龍之介(@riscait)です!

Flutterにおいて、その環境分けの方法はいくつかありますが、今回は Dart-defines 1つのみを使用して実現する方法で実行してみたのでご紹介します。
少しXcodeやAndroidのbuild.gradleに追記したりが必要ですが、ビルドコマンドがシンプルになることや、
パッケージを使う場合と比べて自動生成ファイルが少なく、取り回しがしやすいことが大きな利点です。
pub.devのパッケージを使用したり、 --flavor オプションを使用する方法と比べたメリットとデメリットは以下の通りです。
この記事のメリット
- Flutterの環境ごとのビルド設定切り替え方法が分かる
- Flavor分けにパッケージの導入が不要(アイコン生成には使用します)
-
--dart-defineのみでiOS, AndroidネイティブへもFlavor(環境)を伝播できる -
--dart-define=FLAVORのみで Bundle ID (Package name) 等を切り替えられる -
main.dartは1つのままで良い。環境ごとに分ける必要がない - iOSの
schemeやConfigurationを作成する必要がない
ちょっと面倒なところ
- Androidでは、
build.gradle,AndroidManifest.xmlに追記が必要 - iOSでは、
Build PhaseとBuild Pre actionにスクリプトを追加する必要がある - Flutter SDKのバージョンアップグレードで
Dart-definesのエンコード方法が変わり、対応する必要が出てくる可能性がある
前提
- この記事では以下の3環境に分けていきます
(あくまで一例なので、数や名前は適宜変更してください)- dev (ローカルの開発環境)
- stg (疑似的なDB等を用意し、テストを行うステージング環境)
- prod (ストアで実際に公開するアプリで使用する本番環境)
- Flutter 2.2 以上を想定
- 当記事の手法はFlutter 1.17 以上で利用可能ですが、ネイティブでDart-defineを受け取る際のデコード処理に差異があります。(詳しくは後述)
- 対応OS
- ✅ Android
- ✅ iOS
- ⬜️ Web(ToDo)
当記事で例として使用する環境分け情報
- アプリ名
- dev: Flutter AT.dev
- stg: Flutter AT.stg
- prod: Flutter AT
- アプリID (Bundle ID, Package name)
- dev: jp.co.altive.fat.dev
- stg: jp.co.altive.fat.stg
- prod: jp.co.altive.fat
dev, stg環境の場合はアプリ名とアプリIDにドット繋ぎで環境名を足したいと思います。
もちろん、「アプリ名の方は(dev)のように括弧で表現する」といったことも自由なので、適宜ご調整ください。
必要な下準備
- まだの方は、環境分けを行いたいFlutterアプリを作成、Clone等してください。
- Firebaseを使用している場合は、環境の数分Firebaseプロジェクトを作成し、iOS用の
GoogleService-Info.plistとgoogle-services.jsonをダウンロードしておく。
Iconを環境別に分ける場合に必要な準備
※ Icon画像を環境別に分けない場合は読み飛ばしてください。

環境別のアイコン画像をフォルダに配置します
※ 画像は適宜用意してください
assets/launcher_icon/icon-dev.png
assets/launcher_icon/icon-stg.png
assets/launcher_icon/icon-prod.png
flutter_launcher_iconsパッケージをインストール
flutter pub add flutter_launcher_icons --dev
もしくは pubscpec.yaml に追記して pub get します。
dev_dependencies:
flutter_launcher_icons: ^0.9.2 # インストール時点での最新版を推奨
Flavorごとの設定ファイルを作成
flutter_launcher_icons-dev.yamlflutter_launcher_icons-stg.yamlflutter_launcher_icons-prod.yaml
# flutter_launcher_icons-dev.yaml
flutter_icons:
android: true
ios: true
image_path: "assets/launcher_icon/icon-dev.png"
pubspec.yaml にも設定を追記
# pubspec.yaml
flutter_icons:
android: true
ios: true
アイコン画像の書き出し実行
flutter pub run flutter_launcher_icons:main
✓ Successfully generated launcher icons for flavors と表示されれば成功です。
iOSでは ios/Runner/Assets.xcassets/AppIcon-{Flavor名}.appiconset/
Androidでは android/app/src/{Flavor名}/mipmap**/launcder.png
に出力されているはずです👍
以上、「Flavor(環境)別アイコンの準備」は終わりです。
ビルド時にコマンドで環境を指定する
アプリ起動(run)やビルド(build)時に環境を分けるために、 --dart-define というオプションを指定します。
下記の例では、 FLAVOR=dev (開発環境)という定義を行なっています。
# アプリ起動
flutter run --dart-define=FLAVOR=dev
# アプリビルド
flutter build ios --dart-define=FLAVOR=dev
VS Code や Android Studio を使用している場合は、ボタンやショートカットからアプリ起動することも多いと思います。
その場合は --dart-define=FLAVOR=dev の設定を追加してください。
VS Code の設定例
VS Code では、 launch.json で起動コマンドを編集できます。
すでにファイルがある場合は、既存ファイルを編集し、ない場合は .vscode ディレクトリを作成し、 launch.json を追加しましょう。
以下のように args にて --dart-define を指定可能です。
dev, stg, prod 3環境分用意した例です。
launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Run dev",
"request": "launch",
"type": "dart",
"args": [
"--dart-define=FLAVOR=dev"
]
},
{
"name": "Run stg",
"request": "launch",
"type": "dart",
"args": [
"--dart-define=FLAVOR=stg"
]
},
{
"name": "Run prod",
"request": "launch",
"type": "dart",
"args": [
"--dart-define=FLAVOR=prod"
]
}
]
}
お好みで --release を付与したReleaseビルドバージョンも追加してください。
Android Studio の設定例
Daigoさんが書いてくださいました🙌
Android Studioでの設定方法は↓を確認してください👍
FlutterアプリでFlavorを取得して使いたい場合
まず、Flutterアプリ側でFlavorを取得したい場合の設定を解説します。
例えば、環境ごとにAPI の Base URL や API key を変更したりするのに使うかと思います。
また、起動/ビルドコマンドで指定した dart-define がきちんと反映されているかも確認することができるので試しておきましょう。
// `--dart-define=FLAVOR=dev` と指定した場合
const flavor = String.fromEnvironment('FLAVOR');
print(flavor) // dev
Android対応
単に dart-define で環境(Flavor)を指定しただけでは、 Android側に伝わりません。
build.gradle と AndroidManifest.xml ファイルを編集する必要があります。
build.gradle を編集して dart-define を受け取る
android/app/build.gradle を編集します。
ビルド時に指定した dart-define をデコードして受け取ります
// android/app/build.gradle
// dart-define を入れる変数を宣言しています。
// `Key: Value` 形式で初期値を設定することもできます
def dartEnvironmentVariables = [:];
if (project.hasProperty('dart-defines')) {
// カンマ区切りかつBase64でエンコードされている dart-defines をデコードして変数に格納します。
dartEnvironmentVariables = dartEnvironmentVariables + project.property('dart-defines')
.split(',')
.collectEntries { entry ->
def pair = new String(entry.decodeBase64(), 'UTF-8').split('=')
[(pair.first()): pair.last()]
}
}
defaultConfigに applicationIdSuffix とアプリ名を追加します
// android/app/build.gradle
defaultConfig {
minSdkVersion 16
targetSdkVersion 30
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
+ if (dartEnvironmentVariables.FLAVOR != 'prod') {
+ applicationIdSuffix ".${dartEnvironmentVariables.FLAVOR}"
+ }
+ resValue "string", "app_name", "FlutterAT" +
+ (dartEnvironmentVariables.FLAVOR == 'prod' ? '' : ".${dartEnvironmentVariables.FLAVOR}")
}
- 本番環境以外の
applicationIdSuffixに.+FLAVORを設定。 - アプリ名として使う変数
string/app_nameの後ろにも.+FLAVORを追加(同じく本番環境以外)
defaultConfigで設定したアプリ名を使用するようにする
android/app/src/main/AndroidManifest.xml を使用して、 build.gradle で設定したアプリ名を使用するようにします。
<!-- AndroidManifest.xml -->
- android:label="flutter_app_template"
+ android:label="@string/app_name"
これで環境によってアプリ名が変わるようになりました。
アイコンを切り替えるためのタスクを追加する
flutter_launcher_icons パッケージを使ってFlavorごとのアイコンを作成しておきます。
src/{flavor_name}/res ディレクトリに複数の mipmap-xxx ディレクトリがあり、 ic_launcher.png が生成されています。
Flavorにより、これらのアイコンを切り替えたいため、 build.gradle に以下を追記します。
// android/app/build.gradle
task copySources(type: Copy) {
from "src/${dartEnvironmentVariables.FLAVOR}/res"
into 'src/main/res'
}
tasks.whenTaskAdded { task ->
task.dependsOn copySources
}
やっていることは、ビルド時にFlavorディレクトリの res を src/main/res にコピーしています。
.gitignore ファイルに追加
android/app/src/main/res/mipmap*/ は、ビルド時に生成されてFLAVORにより内容が変わるので、Git管理対象外にします。
+ **/android/app/src/main/res/mipmap*/
Firebase対応 (Android)
android/app/src に各環境(Flavor)と同名のディレクトリ(フォルダ)を作成。
-
android/app/src/dev/,android/app/src/stg/,android/app/src/prod/
各Firebaseプロジェクトに作ったAndroidアプリ用の google-services.json をそれぞれのディレクトリに配置します。
android/app/build.gradle に追記
環境別ディレクトリに配置した google-services.json を android/app にコピーするタスクを定義しています。
// android/app/build.gradle
task selectGoogleServicesJson(type: Copy) {
from "src/${dartEnvironmentVariables.FLAVOR}/google-services.json"
into './'
}
tasks.whenTaskAdded { task ->
if (task.name == 'processDebugGoogleServices' || task.name == 'processReleaseGoogleServices') {
task.dependsOn selectGoogleServicesJson
}
}
.gitignore ファイルに追加
android/app/google-services.json は、ビルド時に生成されてFLAVORにより内容が変わるので、Git管理対象外にします。
+ **/android/app/google-services.json
iOS対応
Flavorに対応する xcconfig ファイルを作成
Xcodeにて File -> New -> File (⌘N) で新規ファイルを追加します。
ファイル形式は Configuration Settings File を選択します。(config でフィルタリングするとすぐ出てきます)

ファイル保存場所は ios/Flutter 、ファイル名は dev.xcconfig のように {Flavor名}.xcconfing とします。
Flavorの数だけ新規作成しましょう。例: stg.xcconfig , prod.xcconfig ...
{flavor}.xcconfig の中に FLAVORの値とその他使いたい変数を設定します。

ここでは、 FLAVOR= の他に、 Bundle IDやアプリ名の末尾等に追加したい APP_ID_SUFFIX を定義しました。
アプリ名の末尾には別のSuffixを追加したい場合等は、適宜定義を増やして使ってください。
※ DartDefines.xcconfig は、後ほどシェルスクリプトで生成するファイルなので気にしないでください。
Dart define を受け取る Pre Actionを追加
ビルド時に指定した --dart-define をiOSで受け取るために、ビルド直前に実行されるスクリプトを追加する必要があります。
もちろん、スクリプトをXcode上から直接書き込んでも良いですが、
-
Runner.xcschemeに改行がない状態で書き込まれるので差分が見にくい - コメント等で日本語を書くとエンコードされて読めない
というデメリットがあり、新規ファイルを作成して使うことで、好きなエディタ(VS Codeなど)のハイライト機能等を利用しながら編集できる利点もあります。
スクリプトファイル保存
ios/scripts/retrieve_dart_defines.sh というパスとファイル名で以下の内容の sh ファイルを保存します。
retrieve_dart_defines.sh
#!/bin/sh
# Dart-defineを書き込んだり、Flavorごとのxcconfigをincludeするファイル
OUTPUT_FILE="${SRCROOT}/Flutter/DartDefines.xcconfig"
# Flutter 2.2 以降で必要な、Dart-Definesのデコード処理
function decode_url() { echo "${*}" | base64 --decode; }
# 最初にファイル内容をいったん空にする
: > $OUTPUT_FILE
IFS=',' read -r -a define_items <<<"$DART_DEFINES"
for index in "${!define_items[@]}"
do
# Flutter 2.2 以降で必要なデコードを実行する
item=$(decode_url "${define_items[$index]}")
# FLAVORが含まれるDart Defineの場合
if [ $(echo $item | grep 'FLAVOR') ] ; then
# FLAVORの値(=の右側)
value=${item#*=}
# FLAVORに対応したXCConfigファイルをincludeさせる
echo "#include \"$value.xcconfig\"" >> $OUTPUT_FILE
fi
done
ここでは…
-
$DART_DEFINESという変数に格納されているdart-defineを取得して -
FLAVORの場合、その値(dev等)を取り出し、DartDefines.xcconfigに{Flavor名}.xcconfigをインクルードさせています。
$DART_DEFINES のすべての変数を書き込んでしまえば楽なのですが、ビルドできなくなってしまったので、 FLAVOR に絞って書き込んでいます🧐
XcodeのBuild Pre-actions に作成したスクリプトを登録する
- Scheme (Runner) をクリックして
Edit scheme-> Build を展開してPre-actionsを選択します。 - 「+」ボンタンを押して「New Run Script Action」を選択します。
- 「Provide build settings from」は
Runnerを選択します。 - 先ほど保存したファイルのパスである
${SRCROOT}/scripts/retrieve_dart_defines.shを書き込みます。

スクリプトファイルに実行権限を与える
そのままビルドしてもスクリプトファイルが実行されません。
以下のコマンドで実行限限を与えておきましょう。
chmod 755 ios/scripts/retrieve_dart_defines.sh
各種xcconfigファイルで DartDefines.xcconfig をインポート
前項のスクリプトで生成される DartDefines.xcconfig がDebug, Releaseビルド両方で使われるように、
両方の ***.xcconfig でインクルードします。
ios/Flutter/Debug.xcconfig
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
+ #include "DartDefines.xcconfig"
ios/flutter/release.xcconfig
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
+ #include "DartDefines.xcconfig"
.gitignore ファイルに追加
DartDefines.xcconfig は、ビルド時に生成され、FLAVORにより内容が変わるので、Git管理対象外にします。
+ **/ios/Flutter/DartDefines.xcconfig
アプリ表示名を環境によって変える
ios/Runner/Info.plist を編集します。
アプリ名に使われる CFBundleDisplayName にアプリ名 + APP_ID_SUFFIX を指定します。
<key>CFBundleName</key>
- <string>flutter_app_template</string>
+ <string>FlutterAT$(APP_ID_SUFFIX)</string>
+ <key>CFBundleDisplayName</key>
+ <string>FlutterAT$(APP_ID_SUFFIX)</string>
環境ごとに以下のようなアプリ名が表示されるようになりました👍
- dev: FlutterAT.dev
- stg: FlutterAT.stg
- prod: FlutterAT
のようになります。
Bundle IDを環境によって変える
ios/Runner.xcodeproj/project.pbxproj を PRODUCT_BUNDLE_IDENTIFIER で検索するか、
Xcode > Runner > TARGETS Runner > Build Settings の Product Bundle Identifier を表示して、
$(APP_ID_SUFFIX) を末尾に追加します。

画像で使っている FLAVOR_SUFFIX は古い名前です。 APP_ID_SUFFIX に変更しました
忘れずに Debug, Profile, Release すべてに接尾辞を追加して共通の値になるように注意しましょう。
# ios/runner.xcodeproj/project.pbxproj
- PRODUCT_BUNDLE_IDENTIFIER = "jp.co.altive.fat";
+ PRODUCT_BUNDLE_IDENTIFIER = "jp.co.altive.fat$(APP_ID_SUFFIX)";
これで、環境ごとにアプリのBundle IDが変わるようになりました👏
Appアイコンを環境によって変える
ios/Runner.xcodeproj/project.pbxproj を ASSETCATALOG_COMPILER_APPICON_NAME で検索するか、
Xcode > Runner > TARGETS Runner > Build Settings の Primary App Icon Set Name を表示して、
AppIcon と指定されている値を AppIcon-$(FLAVOR) に変更します。

忘れずに Debug, Profile, Release すべて共通の値になるようにしましょう。
# ios/runner.xcodeproj/project.pbxproj
- ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon";
+ ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-$(FLAVOR)";
これで、環境ごとにアプリのアイコンが変わるようになりました👏
Firebase対応 (iOS)
Firebaseを使用していてかつ環境ごとにプロジェクトを分ける場合は、GoogleService-Info.plist を環境ごとに使い分ける必要があります。
ios ディレクトリに各環境(Flavor)と同名のディレクトリ(フォルダ)を作成。
-
ios/dev/,ios/stg/,ios/prod/
各Firebaseプロジェクトに作ったiOSアプリ用の GoogleService-Info.plist をそれぞれのディレクトリに配置します。
Select GoogleService-Info.plist
ビルド時に、環境に対応した GoogleService-Info.plist を使用できるようにするスクリプトを追加します。
-
Build phases->New run scriptを選択して新しいRun Scriptを追加 - 名前をわかりやすいように
Select GoogleService-Info.plistに変更 - スクリプトを記述
-
Output Filesに$(SRCROOT)/GoogleService-Info.plistを追加 - 既存Scriptである
Copy Bundle Resourcesより上に移動
\cp -f ${SRCROOT}/${FLAVOR}/GoogleService-Info.plist ${SRCROOT}/GoogleService-Info.plist

GoogleService-Info.plistへの参照を追加する
(@akaboshinit さん、追加情報ありがとうございます!)
実際には、スクリプトによってコピーされた GoogleService-Info.plist が使用されることになるのですが、このままでは ${SRCROOT}/GoogleService-Info.plist ファイルへの参照がなくXcodeで認識できません。
Xcode上でRunnerディレクトリへ GoogleService-Info.plist ファイル(どの環境のものでも良い)をドラッグ&ドロップで追加してファイルと参照を追加しましょう。

Downloadフォルダ等から、Runnerフォルダ内へドラッグ&ドロップ (Copy items if needed にチェック)

Runnerフォルダ内に追加されました。
このファイルはビルド時に任意の環境用の GoogleService-Info.plist に置き換わります🙆♂️
.gitignore ファイルに追加
ios/GoogleService-Info.plist は、ビルド時に生成されてFLAVORにより内容が変わるので、Git管理対象外にします。
+ **/ios/GoogleService-Info.plist
Flutterアプリを起動して、Flavorがきちんと伝わっているか確かめる
--dart-define=Flavor がきちんとネイティブに伝わり、アプリ名やBundle IDがFlavorごとに変更されていることを手軽に確かめるためには、 package_info_plus パッケージを使用します。
PackageInfo の下記メソッドを使用して確認できます。
-
.appName- iOS: アプリ名 (
CFBundleDisplayName) - Android:
android:label="@string/app_name"
- iOS: アプリ名 (
-
.packageName- iOS: Bundle ID (
CFBundleIdentifier) - Android:
applicationId
- iOS: Bundle ID (
宣伝
Altive株式会社では、Flutterアプリの開発・運営を承っております。
お気軽にお問い合わせください🫡
Riverpod の実践入門本を公開中です📘
参考記事



Discussion