概要
FlutterでiOSアプリをビルドする際には、本番環境と開発環境でGoogleService-info.plist
やBundle Identifier
の切り替えが必要となるケースが多いと思います。
この切替には複数のSchemeを用意する方法、Flavor
を使う方法、ビルド時の--release
と--debug
を使って切り分ける方法がありますが、Flutter 1.17からビルド引数の--dart-define
で定数を渡せるようになりました。今回はこのビルド変数の渡し方を説明します。
Android編はこちら
前提
- Flutterのサンプルプロジェクトを作成しシミュレーターで実行できる
- Flutter 1.20以降
環境
$ 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)
サンプルコード
前半の設定
前半では、FlutterからXcodeへ変数を渡し確認してみます。
Flutterプロジェクトの用意
既存のプロジェクトでも、新規にプロジェクトを作成しても問題ありません。
一度ビルドして、シミュレーター上で実行できることを確認しておいてください。
Xcodeの設定
Android StudioのProject Windowから下記の[project]/ios
を右クリックしFlutter
内のOpen iOS module in Xcode
をクリックします。すると該当ProjectのXcodeが開きます。
XcodeメニューのProduct
>Scheme
>Edit Scheme
をクリックします。
開いたウィンドウのBuild
>Pre-actions
を開き、Provide build setting from
を設定し、画面下部の+
を押してNew Run Script Action
を追加します。
以下のスクリプトを追加します。urldecode()
を挟むことで、文字列がURLデコードされ見やすくなります。${PROJECT_DIR}
はFlutterのios
ディレクトリです。$DART_DEFINES
はFlutterからXcodeに渡される定数です。
そして、このスクリプトが実行されると[project]/ios/prebuild.log
が自動的に生成され、その中にFlutterから渡したビルド引数が書き込まれます。
function urldecode() { : "${*//+/ }"; echo "${_//%/\\x}"; }
exec > ${PROJECT_DIR}/prebuild.log 2>&1
echo $(urldecode "$DART_DEFINES")
Flutterの設定
Flutter側はビルド時に指定します。
flutter build ios --dart-define=DEFINE_HELLO=world --dart-define=DEFINE_FOO=bar
Android Studio を使っている場合Run/Debug Configurations
にて設定可能です。
前半の動作確認
Flutterをビルドすると[project]/ios/prebuild.log
が出力されていると思いますので確認してみてください。以下のように出力されていれば、ここまでは成功です。
DEFINE_HELLO=world,DEFINE_FOO=bar
後半の設定
後半では、実際にXcodeへ渡した変数の値をFlutter上で確認してみます。
Xcodeの設定
Flutterから渡された変数をXcode上で扱えるように設定していきます。
まずFlutterから渡された変数のデフォルト値を作成します。
※ このデフォルト値の設定について、私は必要性を理解できませんでした。参照元のDenisさんの記事では以下のようにだけ書かれています。もしかすると、複数人で開発をしたとき、うっかりビルド引数を指定することを忘れるとエラーとなってしまうのを避けるためかもしれません。
Consider putting Defineexample.xcconfig after Defineexample-defaults.xcconfig, so values from Flutter Run command could override default values.
XcodeのFlutterディレクトリで右クリックしNew File
を追加します。
Configuration Settings File
を選択しTargets
にチェックを入れ、DartDefineDefaults.xcconfig
という名前でFlutter
ディレクトリに保存します。(名称は何でも大丈夫です)
次に作成したDartDefineDefaults.xcconfig
を開きデフォルト値を設定します。
その後Debug.xcconfig
とRelease.xcconfig
に、先程つくったDartDefineDefaults.xcconfig
と、このあと設定するDartDefine.xcconfig
をinclude
する設定を追加していきます。
このときDartDefineDefaults.xcconfig
が先に読み込まれ、その後にDartDefine.xcconfig
(後のスクリプトで自動生成される)が読み込まれるようにしてください。こうすることで、例えば先にDEFINE_FOO=bar
が定義され、後からDEFINE_FOO=hoge
が定義された場合、後からの値で上書きすることができます。後からの値が定義されていなければ、デフォルト値のみが定義されるという仕組みです。
このような形にスクリプトを修正します。(Denisさんの元記事ではビルド引数のPrefixでgrepし、特定の引数のみを読み込むように工夫されています) [2020/12/31 下記に修正]
工夫されていますではなく、対応したほうが良さそうです。
下の例のように定数のPrefixでgrepするのが良さそうです。これをやらないとdefine_items[@]
の中にはflutter.inspector.structuredErrors=true
などが入ってきます。これが原因なのかは定かではありませんが、意図しない値がDartDefine.xcconfig
に書き込まれることで、うまくコンパイルできないケースがありました。(実際には落ちずに中途半端な形でコンパイルが通ってしまい、原因を特定するのにすごく時間がかかりました。)
function urldecode() { : "${*//+/ }"; echo "${_//%/\\x}"; }
#exec > ${PROJECT_DIR}/prebuild.log 2>&1
#echo $(urldecode "$DART_DEFINES")
IFS=',' read -r -a define_items <<< "$DART_DEFINES"
for index in "${!define_items[@]}"
do
define_items[$index]=$(urldecode "${define_items[$index]}");
done
#printf "%s\n" "${define_items[@]}" > ${SRCROOT}/Flutter/DartDefine.xcconfig
printf "%s\n" "${define_items[@]}"|grep '^DEFINE_' > ${SRCROOT}/Flutter/DartDefine.xcconfig
Xcodeの設定の最後にBundle name
とBundle identifer
に定義した変数が反映するよう修正します。
これでXcode側の設定は完了です。
Flutterの設定
基本的には、下記のサンプルコードを参考にしてもらえれば大丈夫なはずです。
https://github.com/yana1316/flutter_define
pubspec.yaml
に追記します。
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.0
package_info: ^0.4.3+2 # 追加
あとはmain.dart
でパッケージ名を表示させます。
Text(
'Package Name',
style: Theme.of(context).textTheme.headline5,
),
FutureBuilder<PackageInfo>(
future: PackageInfo.fromPlatform(),
builder: (context, value) {
if (!value.hasData) {
return Container();
}
return Text(
value.data.packageName,
style: Theme.of(context).textTheme.headline6,
);
}
),
後半の動作確認
うまく行けば、アプリの名前の後ろに指定した値(DEFINE_FOO=bar
としたならbar)が付き、画面上ではパッケージ名の後ろに値がついていると思います。