Flutter 如何根據 Flavor 多環境載入對應的 Firebase Config
這篇寫於 2023 年。工具和 API 變化很快,部分步驟可能已經不適用。
本文的內容應該很多人都會碰到,我們很常為了加速開發,會使用許多 Firebase 服務,例如:Authentication、Analytics 和 Crashlytics 等等,這時候就需要處理 Flutter 專案與 Firebase 的集成,而當我們有多個環境時如何綁定對應的 Firebase 專案?如何決定要載入哪個環境的設定檔,才能與 Firebase 服務溝通。其中比較麻煩是針對 Android、iOS(macOS相同) 有不同處理方式,但也大同小異,趕緊往下滑跟著實作吧!
Firebase Console#
本文範例設置了兩個環境,分別為 Dev、Prod,所以 Firebase Project 也區分開來,之後在設定及維護上會方便許多。當然如果你還有 staging 或是其他環境那就在創建自己的專案,我們需要從每個專案裡拿到設定檔
FlutterFire Helper#
為了不需要手動為每個平台一個一個新增到專案,我們需要 FlutterFire 工具的幫忙,它的出現就是為了讓開發者可以快速將 Flutter 專案集成 Firebase,替我們節省非常多時間
FlutterFire | FlutterFireThe official Firebase plugins for Flutterfirebase.flutter.dev
設定過程我們都需要 flutterfire_cli 指令操作,在 Terminal 透過幾個指令就能完成了,需要先進行安裝
dart pub global activate flutterfire_cli
接著在 Terminal 移動到 Flutter 專案的所在位置,使用 flutterfire configure 指令,最後產出幾個設定firebase_options 檔案
project為 Firebase 專案名稱out為輸出的位置與檔案名稱ios-bundle-id為 Bundle IDandroid-app-id為 Package Namemacos-bundle-id為 Bundle IDweb-app-id為 Bundle ID
每個平台的 id 通常應該沒特別需求的話都會是相同,運行之後會詢問你要使用服務的平台,包含 ios、android、macos、web,最後產出幾個設定檔案
flutterfire config \
--project=flaver-dev \
--out=lib/firebase_options_dev.dart \
--ios-bundle-id=com.flaver.food.dev \
--android-app-id=com.flaver.food.dev \
--macos-bundle-id=com.flaver.food.dev \
--web-app-id=com.flaver.food.dev
- Android 需要
google-services.json - iOS 與 macOS 需要
GoogleService-Info.plist、firebase_app_id_file.json - Flutter 需要
firebase_options.dart
而在每次執行 flutterfire configure 指令後就先將檔案進行移動,避免之後在生成其他環境的檔案時造成覆蓋,要移動到哪呢?可以跳到後面的 Setup 操作。
以下為 Firebase 專案完成平台設定的情況,一下子就完成了,接下來只需要讓程式知道在每個環境下要載入對應的檔案和資料
Android Setup#
當我們設置好 Flavor 之後 android 目錄會有各環境的資料夾,我們需要將 google-services.json 放置到對應環境底下,此範例為 dev 和 prod
Android 會在 app 資料夾載入 google-services.json 檔案,所以我們需要在 Build 階段將運行環境的檔案複製到 app 這層,這樣才會正常運行哦
iOS Setup#
當我們設置好 Flavor 之後 iOS 目錄會有 GoogleService-Info.plist、firebase_app_id_file.json兩個檔案,一個會在 Runner 資料夾裡面,需要將他們放置到 flavor 資料夾,此範例為 dev 和 prod。不過這都沒有固定,你想放到哪裡都可以,最主要是好管理就好
此範例有創建 Config 資料夾,會了放置相關檔案,而因為要讓專案讀取的到,所以在 Build Phases 的 Copy Bundle Resources 需要加入資料夾
因為要讓 iOS 在 Build 階段將檔案移動到指定地點,跟 Android 一樣,需要多新增兩個任務處理。在 Build Phases 裡新增一個任務 Copy GoogleServices file(可以自行命名),在 Build 過程需要給予對應環境的 GoogleService-Info.plist,讓 APP 在每個環境運行時能載入正確的 Firebase 設定檔,才可以正常使用 Firebase 服務
environment="dev"
# Get flavor name
if [[ $CONFIGURATION =~ -([^-]*)$ ]]; then
environment=${BASH_REMATCH[1]}
fi
GOOGLESERVICE_INFO_PLIST_NAME=GoogleService-Info.plist
GOOGLESERVICE_INFO_PLIST_FILE_PATH=${PROJECT_DIR}/Config/${environment}/${GOOGLESERVICE_INFO_PLIST_NAME}
# Check GoogleService-Info.plist file are exist or not
if [ ! -f $GOOGLESERVICE_INFO_PLIST_FILE_PATH ]
then
echo "No GoogleService-Info.plist found. Please ensure it's in the proper directory."
exit 1
fi
PLIST_FILE_DESTINATION=${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist
# Copy GoogleService-Info.plist of prod for Release build
cp "${GOOGLESERVICE_INFO_PLIST_FILE_PATH}" "${PLIST_FILE_DESTINATION}"
echo "Copy ${GOOGLESERVICE_INFO_PLIST_FILE_PATH} to final destination: ${PLIST_FILE_DESTINATION}"
如果有使用到 Firebase Crashlytic 服務,Build Phases 會有 [firebase_crashlytics] Crashlytics Upload Symbols 這個任務,這時候需要針對擁有的 flavor 環境載入對應的檔案,可能會需要微調一下,將 Configuration 和路徑調整為自己專案的樣子
if [ "${CONFIGURATION}" == "Debug-dev" ] || [ "${CONFIGURATION}" == "Profile-dev" ] || [ "${CONFIGURATION}" == "Release-dev" ];
then
"$PODS_ROOT/FirebaseCrashlytics/upload-symbols" --flutter-project "$PROJECT_DIR/Config/dev/firebase_app_id_file.json"
elif [ "${CONFIGURATION}" == "Debug-prod" ] || [ "${CONFIGURATION}" == "Profile-prod" ] || [ "${CONFIGURATION}" == "Release-prod" ];
then
"$PODS_ROOT/FirebaseCrashlytics/upload-symbols" --flutter-project "$PROJECT_DIR/Config/prod/firebase_app_id_file.json"
fi
如果沒有使用到 Firebase Crashlytic,就不會使用到
firebase_app_id_file.json,也不需要額外設定
Flutter Setup#
在 flutterfire 生成檔案後,會在 lib 資料夾生成 firebase_options.dart,我們一樣將他們放置到其他資料夾存放,為了專案目錄架構的整潔,然後在檔名後方加上環境後綴
最後一步,在每個環境的入口,main() 使用對應的 Firebase 設定檔,讓我們在 Firebase 初始化時沒有問題,接下來就能安心開發了,很棒對吧
Firebase Service Verification#
本文使用 Firebase Crashlytics 作為範例,測試是否可以正常與 Firebase 服務互動。在 main() 使用 runZonedGuarded() 包裹,為了要捕捉未知的 Platform Error,我們在 runApp()之前就拋出測試用的例外,為了讓下方的 callback 可以被觸發,這時候就會將相關的錯誤以及 StackTrace 上傳給 Firebase Crashlytics 囉
當有看到 Making request to crashlyticsreports domain 的時候就代表運作正常,此時在 Crashlytics 後台可以看到原本的添加SDK文字消失,並顯示分析儀表板
到這邊看到有資料進來就代表服務運作沒問題了,也記得檢查和測試每個環境是否都正常,如果你在設定 Firebase 過程中還是有遇到問題,也很歡迎留言跟我討論~
Reference#
- https://codewithandrea.com/articles/flutter-flavors-for-firebase-apps/
- https://medium.com/@matt.goodson.business/separating-build-environment-configurations-in-flutter-with-firebase-doing-it-the-right-way-c72c3ad3621f
- https://stackoverflow.com/questions/30772201/google-services-json-for-different-productflavors
- https://stackoverflow.com/questions/37615405/use-different-googleservice-info-plist-for-different-build-schemes
- https://lohanidamodar.medium.com/flutter-separating-build-environment-with-multiple-firebase-environment-92e40e26d275
- 學會運用 Flutter Widgetbook,該管好自己和公司的元件庫了!
- 剛進入 Flutter 嗎?適合初學者食用,GetX 是否適合你呢!
- 教你為 Riverpod 2.0 撰寫 Flutter 測試 part.1
- 教你為 Riverpod 2.0 撰寫 Flutter 測試 part.2
- 輕鬆了解 Isar NoSQL DB,用它來實作 Flutter 資料庫吧!
- Flutter 輕鬆實作 i18n,使用 easy_localization_generator 就對了
- Flutter CICD 使用 Gitlab Runner 和 App Center 實作 part.1
- Flutter CICD 使用 Gitlab Runner 和 App Center 實作 part.2
- 使用 CodeMagic 和 Firebase 實現 Flutter CICD
- 輕鬆完成Flutter開發環境,最新版!
- 實作Flutter多變有趣的滾動效果CustomScrollView!
- 如何在Flutter使用 Makefile 節省你的時間?
- Easily understand StatefulWidget LifeCycle of Flutter
- “freezed” makes model class strong and easily
- 提高Flutter性能的小技巧!(一)
- 提高Flutter性能的小技巧!(二)
- 提高Flutter性能的小技巧!(三)
- What are Async and Isolates in Flutter?
- LoadBalancer is optimization for Isolates in Flutter
- Riverpod 輕鬆學,原來這麼好用!
- Riverpod 輕鬆學(二),一些進階用法!
About#
- GitHub: chyiiiiiiiiiiii
- Instagram: flutterluvr.yii
- Linkedin: yiichenhi
- Youtube: Yii
- Youtube: 一起饅頭(美食頻道)
- Email: ab20803@gmail.com
Contribution#
如果覺得文章不錯的話可以贊助,讓我有更多動力和熱情分享學習紀錄和生活!請我喝一杯咖啡吧~
希望有幫助到你/妳,歡迎追蹤我,方便瀏覽最新的文章~
本文原刊登於 Medium。