Flutter 如何根據 Flavor 多環境載入對應的 Firebase Config

Yii Chen
Flutter Formosa
Published in
13 min readFeb 16, 2023

--

本文的內容應該很多人都會碰到,我們很常為了加速開發,會使用許多 Firebase 服務,例如:Authentication、Analytics 和 Crashlytics 等等,這時候就需要處理 Flutter 專案與 Firebase 的集成,而當我們有多個環境時如何綁定對應的 Firebase 專案?如何決定要載入哪個環境的設定檔,才能與 Firebase 服務溝通。其中比較麻煩是針對 Android、iOS(macOS相同) 有不同處理方式,但也大同小異,趕緊往下滑跟著實作吧!

Firebase Console

本文範例設置了兩個環境,分別為 DevProd,所以 Firebase Project 也區分開來,之後在設定及維護上會方便許多。當然如果你還有 staging 或是其他環境那就在創建自己的專案,我們需要從每個專案裡拿到設定檔

FlutterFire Helper

為了不需要手動為每個平台一個一個新增到專案,我們需要 FlutterFire 工具的幫忙,它的出現就是為了讓開發者可以快速將 Flutter 專案集成 Firebase,替我們節省非常多時間

設定過程我們都需要 flutterfire_cli 指令操作,在 Terminal 透過幾個指令就能完成了,需要先進行安裝

dart pub global activate flutterfire_cli

接著在 Terminal 移動到 Flutter 專案的所在位置,使用 flutterfire configure 指令,最後產出幾個設定firebase_options 檔案

  • project 為 Firebase 專案名稱
  • out 為輸出的位置與檔案名稱
  • ios-bundle-id 為 Bundle ID
  • android-app-id 為 Package Name
  • macos-bundle-id 為 Bundle ID
  • web-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.plistfirebase_app_id_file.json
  • Flutter 需要 firebase_options.dart

而在每次執行 flutterfire configure 指令後就先將檔案進行移動,避免之後在生成其他環境的檔案時造成覆蓋,要移動到哪呢?可以跳到後面的 Setup 操作。

以下為 Firebase 專案完成平台設定的情況,一下子就完成了,接下來只需要讓程式知道在每個環境下要載入對應的檔案和資料

Android Setup

當我們設置好 Flavor 之後 android 目錄會有各環境的資料夾,我們需要將 google-services.json 放置到對應環境底下,此範例為 devprod

Android 會在 app 資料夾載入 google-services.json 檔案,所以我們需要在 Build 階段將運行環境的檔案複製到 app 這層,這樣才會正常運行哦

iOS Setup

當我們設置好 Flavor 之後 iOS 目錄會有 GoogleService-Info.plistfirebase_app_id_file.json兩個檔案,一個會在 Runner 資料夾裡面,需要將他們放置到 flavor 資料夾,此範例為 devprod。不過這都沒有固定,你想放到哪裡都可以,最主要是好管理就好

此範例有創建 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 囉

main.dart

當有看到 Making request to crashlyticsreports domain 的時候就代表運作正常,此時在 Crashlytics 後台可以看到原本的添加SDK文字消失,並顯示分析儀表板

到這邊看到有資料進來就代表服務運作沒問題了,也記得檢查和測試每個環境是否都正常,如果你在設定 Firebase 過程中還是有遇到問題,也很歡迎留言跟我討論~

About

Contribution

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

https://www.buymeacoffee.com/yiichenhi

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

--

--

Yii Chen
Flutter Formosa