Flutter 使用 — dart-define 設置環境變數,簡單與雙平台溝通

Yii Chen
Flutter Formosa
Published in
9 min readMay 6, 2023

當我們在開發 Flutter 時,有時候各平台可能有用到一些服務,會需要 API Key 或是 Token,例如 Android 會在 AndroidManifest.xml、iOS 在 AppDelegate.swift 設置,進行一些三方服務的初始化。存取前會將它們從 base64 格式轉換,才能取得正常內容

可能有些服務需要在 APP 啟動時進行初始化,這時候會透過各平台去設置,需要取得 TOKEN、KEY 等等相關的機密資料,能透過 —dart-define 給予

flutter run --dart-define FLAVOR=development, API_KEY=xxxxxxxxxxxxx

Android 存取

android/app/build.gradle 裡,解析 — dart-define 給予得參數。首先進行基本的字串切分,接著從 base64 格式轉換為正常字串,最後透過 Key & Value 紀錄下來

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

為了讓 AndroidManifest.xml 可以存取和使用,需要將變數設置到 defaultConfigmanifestPlaceholders,以大家習慣的 Key Value pair 去設置,Key 通常都是全大寫

manifestPlaceholders = [ 
API_KEY: dartEnvironmentVariables.API_KEY
]

AndroidManifest.xml

<meta-data 
android:name="com.google.android.geo.API_KEY"
android:value="${API_KEY}" />

為了在 string 資源檔或是程式執行檔存取,需要將變數加到 defaultConfigs

defaultConfig {
...
resValue "string", "api_key", dartEnvironmentVariables.API_KEY
}

strings.xml

<string name="apiKey" translatable="false">@string/api_key</string>

MainActivity.kt

var apiKey = getString(R.string.api_key)
YiiService.start(apiKey)

iOS 存取

首先要調整 ios/Runner/Info.plist 設定檔,將 DART_DEFINES Key 加入,這樣就能存取 -dart-define 內容

<key>DART_DEFINES</key> 
<string>$(DART_DEFINES)</string>

接著在 AppDelegate.swift 裡的 didFinishLaunchingWithOptions() 解析 — dart-define。跟 Android 做的事情差不多,一樣取得原始內容後,進行字串切分和格式轉換,然後以 Key & Value 儲存

let dartDefinesString = Bundle.main.infoDictionary!["DART_DEFINES"] as! String
var dartEnvironmentVariablesDictionary = [String:String]()

for definedValue in dartDefinesString.components(separatedBy: ",") {
let decoded = String(data: Data(base64Encoded: definedValue)!, encoding: .utf8)!
let values = decoded.components(separatedBy: "=")
dartEnvironmentVariablesDictionary[values[0]] = values[1]
}

一開始進行服務的初始化,載入私密資訊,例如:API Key、Token

YiiService.start(withApiKey: dartEnvironmentVariablesDictionary["API_KEY"]!, in:application, withLaunchOptions:launchOptions)

Flutter 3.7

在 Flutter 3.7 版本後多了一個新的參數 — dart-define-from-file ,能直接傳遞 Json 檔案,包含所有的機密資料,我們就不需要使用很長的指令了,方便許多

// keys.json
{
"FLAVOR": "development",
"API_KEY": "xxxxxxxxxxxxx"
}
flutter run --dart-define-from-file=keys.json

在程式裡讀取初始設置的變數

const flavor = String.fromEnvironment('FLAVOR');
const apiKey = String.fromEnvironment('API_KEY');

Android

個別針對 Key 讀取資料

def dartEnvironmentVariables = [
FLAVOR: project.hasProperty('FLAVOR')
? FLAVOR
: 'dev',
API_KEY: project.hasProperty('API_KEY')
? API_KEY
: null,
]

iOS

新增 Default.xcconfig,並給予預設值,在 — dart-define-from-file 讀取不到資料時可以使用

FLAVOR=dev 
API_KEY=xxxxxxxxxx

Debug.xcconfigRelease.xcconfig 匯入Default.xcconfig

Conclusion

到這裡,你已經成功讓專案不同了,我們可以輕鬆設置第三方服務的 SDK、API,甚至不用怕秘密會裸露出來,只需要在 CI 流程裡將資料注入即可,也記得要處理未取得資料的情況,讓專案維持高品質。

Reference

Articles

About

Contribution

如果覺得文章不錯的話可以贊助,讓我有更多動力和熱情分享學習紀錄和生活!請我喝一杯咖啡吧~

https://www.buymeacoffee.com/yiichenhi

希望有幫助到你/妳,歡迎追蹤我,方便瀏覽最新的文章~

--

--

Yii Chen
Flutter Formosa

Flutter Lover || Organizer FlutterTaipei || Writer, Speaker || wanna make Flutter strong in Taiwan. https://linktr.ee/yiichenhi