👌

dart-defines-from-fileがFlutter3.19以降で使えないとお嘆きの貴兄へ

2024/05/21に公開

Flutterのビルドオプションに dart-defines-from-fileというものがあります。JSON形式で環境変数をセットしておくと、Dartはもちろんのこと、AndroidやiOSのネイティブまで自動的に環境変数がセットされるというナイスな機構。Flavorによる開発・検証・本番の切り分けがシンプルでした。

しかし、Flutter3.19から、ネイティブに自動で環境変数が渡るコマンドが削除され、環境の切り替えができなくなってしまいました。正確には、dart-defines-from-fileでセットした変数名がネイティブでビルドする時に連携されなくなった。XCodeで($AAA)とか書いていた変数が認識されません。

調べたところ、ちょっとした改修で今まで通りdart-defines-from-fileが使えたので、共有します。
dart-defines-from-fileというオプションは削除されていないので、今まで通り利用できます。

iOSの設定

dart-defines-from-fileで設定したJSONは、BASE64でエンコードされDART_DEFINESというコマンドライン引数として、各ネイティブのプラットフォームに渡ります。

3.16までは、ios/Flutter/Generated.xcconfigに、自動的に変数が展開されていました。それが、3.19以降はなくなってしまった。それだけの話。

そのため、Generated.xcconfigではない別のファイルを作り、今まで同様に変数を展開してそれを参照してビルドできるようにします。

下記に書いてあるiOSの用の設定と同じことをやります。
https://zenn.dev/altiveinc/articles/separating-environments-in-flutter#スクリプトファイル保存

ちなみに、上記の環境変数の展開そのものは3.17未満では不要です。Generated.xcconfigに展開されるものを二重に展開してるだけなので。

DART_DEFINESの中身を展開するシェルを作る

extract_dart_defines.sh
#!/bin/sh

OUTPUT_FILE="${SRCROOT}/Flutter/DartDefine.xcconfig"
: > $OUTPUT_FILE

function decode_url() { echo "${*}" | base64 --decode; }

IFS=',' read -r -a define_items <<<"$DART_DEFINES"

for index in "${!define_items[@]}"
do
    item=$(decode_url "${define_items[$index]}")
    lowercase_item=$(echo "$item" | tr '[:upper:]' '[:lower:]')
    if [[ $lowercase_item != flutter* ]]; then
        echo "$item" >> "$OUTPUT_FILE"
    fi
done

やってることは、DART_DEFINESで渡ってきた変数(コマンドライン引数)をカンマ区切りで分割し、配列 define_itemsにいれて、1個ずつBASE64でデコードして、aa=bb形式に1行ずつ書き込んでいるだけです。その内容が、ios/Fltter/DartDefines.xcconfigに書き込まれます。

flutterから始まる変数も何個かあって、これを除去しないとこのシェルがこける(base64デコードでコケる)ようなので、除去されています。

Debug.xcconfigとRelease.xcconfigにinclude

#include "DartDefine.xcconfig"を追加するだけ。

Debug.xcconfig
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
#include "DartDefine.xcconfig"
Release.xcconfig
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
#include "DartDefine.xcconfig"

XCodeのPre Build Scriptに追加

Xcodeで、Product > Scheme > Edit Scheme -> Build -> Pre-actions を選択しして、
「New Run Script Action」です。

「Provide build settings from」は Runner を選択!これが抜けてると環境変数渡らない!

上記で作ったシェルのパスを指定するだけ。${SRCROOT}/scripts/extract_dart_defines.shとなります。

おわり。

Androidの設定

gradleにDART_DEFINESを展開する関数を追加

android/app/build.gradle

def dartEnvironmentVariables = [:];
if (project.hasProperty('dart-defines')) {
    dartEnvironmentVariables = dartEnvironmentVariables + project.property('dart-defines')
        .split(',')
        .collectEntries { entry ->
            def pair = new String(entry.decodeBase64(), 'UTF-8').split('=')
            pair.size() == 2 ? [(pair.first()): pair.last()] : [:]
        }
}

これで、dartEnvironmentVariables経由でdart-defines-from-fileで渡ってきた変数を参照できます。

こんな感じですね。

applicationIdSuffix dartEnvironmentVariables.APP_ID_SUFFIX
resValue "string", "app_name", dartEnvironmentVariables.APP_NAME

Androidはこれだけでした。

今日、Flutter3.22でも同様の設定で対応できたので、dart-define-from-fileがネックになっていたとしたら上記の変更を入れたら、今まで通りに生きていけるはずです。

Discussion