借助FlutterFire CLI实现Flutter与Firebase的多环境配置

在Flutter应用开发过程中,如果应用支持多种环境配置(如开发、预发布、生产环境),并且需要与Firebase进行集成,那么就需要额外进行一些配置工作,确保每种环境配置都对应不同的Firebase环境。

最佳实践是为每种环境配置创建独立的Firebase项目,这样能够将开发、预发布和生产环境相互隔离。

在使用自定义后端或像Supabase这样的Dart SDK时,我们可以通过切换URL和API密钥,使应用在不同环境配置下连接到正确的后端环境。然而,Firebase的集成情况有所不同,它没有提供直接的Dart SDK,并且还需要进行一些特定于平台的设置,这使得多环境配置过程变得更加复杂。

幸运的是,FlutterFire CLI可以帮助我们解决这些难题。接下来,我将逐步引导你了解如何使用它为Flutter和Firebase应用配置多环境,让整个过程变得轻松简单。

本文将涵盖以下内容:

  • 为什么需要使用FlutterFire?
  • Firebase和FlutterFire CLI的安装方法
  • 多环境配置下FlutterFire的配置语法
  • 利用Shell脚本简化配置流程
  • 在应用启动时初始化Firebase(适用于iOS、Android和Web平台)

读完本文后,你将能够熟练地将Firebase集成到多环境的Flutter应用中,节省开发时间 ,同时避免常见的配置问题

前提条件

请注意:本文假设你已经拥有一个Flutter应用,并且该应用能够通过dev(开发)、stg(预发布)和prod(生产)这几种环境配置,在iOS和Android平台上正常运行,具体运行命令如下:

bash 复制代码
flutter run --flavor dev
flutter run --flavor stg
flutter run --flavor prod

此外,你还需要准备三个对应的Firebase项目,例如:

如果大家对Flutter多环境有兴趣,可以在评论区留言,我可以再做一期多环境相关的。

一切准备就绪?那我们开始吧!🚀

为什么需要FlutterFire?

过去,Firebase的集成本身就是一个相当繁琐的过程,你需要为每个平台下载配置文件,比如iOS平台的GoogleService-Info.plist文件和Android平台的google-services.json文件。

而现在,这个过程变得简单多了。你只需要运行flutterfire configure命令,然后按照一些交互式提示进行操作即可。完成操作后,这些文件会自动添加到项目中,包括:

  • lib/firebase_options.dart
  • ios/Runner/GoogleService-Info.plist
  • android/app/google-services.json

不过,在处理多环境配置时,情况就变得复杂起来。你需要为每种环境配置准备不同版本的这些文件,并且将它们存储在不同位置,以防止在配置过程中出现文件覆盖的问题。

幸运的是,FlutterFire 1.0.0版本增加了对多环境配置的支持。下面我们就来详细了解一下如何使用这项功能。

安装Firebase和FlutterFire CLI

官方文档中详细介绍了安装Firebase和FlutterFire CLI的所有步骤。

Firebase CLI可以作为一个独立的库安装,也可以通过npm安装。对于会使用npm的开发者来说,npm安装的方式更友好且可靠:

bash 复制代码
npm install -g firebase-tools

我最终选择安装的二进制,因为我的node并不兼容riebase CLI,而我又不太想折腾版本,毕竟我不会nodejs:

bash 复制代码
curl -sL https://firebase.tools | bash

安装完成后,可通过运行firebase --version命令检查安装是否成功。

接下来,运行firebase login命令进行登录,执行命令后会出现如下提示:

bash 复制代码
? Allow Firebase to collect CLI and Emulator Suite usage and error reporting information? Yes

此时会自动打开一个浏览器窗口,你需要使用与目标Firebase项目关联的Google账号进行登录。选择账号登录后,会出现相关授权提示。

点击"Allow"并关闭浏览器窗口,至此Firebase CLI就完成了登录操作。

注意:务必使用与目标Firebase项目关联的Google账号登录。如果使用了错误的账号,可以先运行firebase logout命令退出登录,然后再次运行firebase login重新登录。

安装FlutterFire CLI

安装FlutterFire CLI的命令如下:

bash 复制代码
dart pub global activate flutterfire_cli

安装完成后,运行flutterfire --version命令,检查是否安装了1.0.0或更高版本。

多环境配置下FlutterFire的配置语法

以生成dev环境配置文件的命令为例:

bash 复制代码
flutterfire config \
  --project=flutter-ship-dev \
  --out=lib/firebase_options_dev.dart \
  --ios-bundle-id=com.jarvanmo.dev \
  --ios-out=ios/flavors/dev/GoogleService-Info.plist \
  --android-package-name=com.jarvanmo.dev \
  --android-out=android/app/src/dev/google-services.json

各参数说明如下:

  • --project:指定要使用的Firebase项目(注意:需传入项目ID,而非别名)。
  • --out:Firebase配置文件的输出路径。
  • --ios-bundle-id:iOS应用的Bundle ID((打开Xcode,然后通过以下顺序找到Bundle ID:Runner>General>Identity>Bundle Identifier))。
  • --ios-out:iOS平台GoogleService-Info.plist文件的输出路径。
  • --android-package-name:Android应用的包名(在android/app/build.gradle.kts中,找到applicationId对应的值)。
  • --android-out:Android平台google-services.json文件的输出路径。

要了解所有可用的选项,可以运行flutterfire config --help

使用该命令时,可按以下步骤操作:

  1. 将命令复制到终端中。
  2. 根据应用实际情况,更新projectios-bundle-idandroid-package-name参数。
  3. 运行命令,并按照交互式提示进行操作(后续会详细介绍这些提示)。

但是,你需要对stgprod环境配置重复上述操作,这样既耗时又容易出错

下面我们来看看如何实现这个过程的自动化。👇

利用Shell脚本简化配置流程

虽然flutterfire config命令已经帮我们完成了大部分工作,但仍需要为每种环境配置分别运行该命令,并设置不同的参数。

为了简化操作,我们可以在项目根目录下创建一个flutterfire-config.sh脚本,内容如下:

bash 复制代码
#!/bin/bash 
# 用于为不同环境/flavor生成Firebase配置文件的脚本 
# 欢迎复用和修改此脚本以适配你的项目
if [[ $# -eq 0 ]]; then  
  echo "错误:未指定环境。请使用'dev'、'stg'或'prod'。"  
  exit 1 
fi 

case $1 in  
  dev)    
    flutterfire config \
      --project=flutter-ship-dev \
      --out=lib/firebase_options_dev.dart \
      --ios-bundle-id=com.codewithandrea.flutterShipApp.dev \
      --ios-out=ios/flavors/dev/GoogleService-Info.plist \
      --android-package-name=com.codewithandrea.flutter_ship_app.dev \
      --android-out=android/app/src/dev/google-services.json
    ;;  
  stg)    
    flutterfire config \
      --project=flutter-ship-stg \
      --out=lib/firebase_options_stg.dart \
      --ios-bundle-id=com.codewithandrea.flutterShipApp.stg \
      --ios-out=ios/flavors/stg/GoogleService-Info.plist \
      --android-package-name=com.codewithandrea.flutter_ship_app.stg \
      --android-out=android/app/src/stg/google-services.json
    ;;  
  prod)    
    flutterfire config \
      --project=flutter-ship-prod \
      --out=lib/firebase_options_prod.dart \
      --ios-bundle-id=com.codewithandrea.flutterShipApp \
      --ios-out=ios/flavors/prod/GoogleService-Info.plist \
      --android-package-name=com.codewithandrea.flutter_ship_app \
      --android-out=android/app/src/prod/google-services.json
    ;;  
  *)    
    echo "错误:指定的环境无效。请使用'dev'、'stg'或'prod'。"    
    exit 1    
    ;; 
esac

有了这个脚本,虽然仍需要为项目设置正确的参数,但只需要设置一次。

之后,生成所有Firebase配置文件就变得非常轻松,无需背下每个参数了。

现在,是时候运行这个脚本了。👇

为每种环境配置运行FlutterFire脚本

若要配置dev环境,运行以下命令:

bash 复制代码
./flutterfire-config.sh dev

运行命令后,会出现提示,首先选择要配置的平台:

bash 复制代码
? Which platforms should your configuration support (use arrow keys & space to select)? ›
✔ android                                      
✔ ios                                      
  macos                                        
✔ web                                      
  windows

接下来,选择iOS上的Build configuration

bash 复制代码
? You have to choose a configuration type. Either build configuration (most likely choice) or a target set up. ›                    
❯ Build configuration
  Target

接着,选择Debug-dev构建配置:

bash 复制代码
? Please choose one of the following build configurations ›
  Debug                                        
  Release                                        
  Profile                                        
❯ Debug-dev                                      
  Profile-dev                                        
  Release-dev                                        
  Debug-stg                                        
  Profile-stg                                        
  Release-stg                                        
  Debug-prod                                        
  Profile-prod                                        
  Release-prod

当然你可以通过重复运行该脚本实现更细致的Build configuration配置

注意:如果遇到"Failed to list Firebase projects"错误,可先运行firebase logout命令,然后再运行firebase login命令,之后重新尝试。

这一步可能会花费一些时间,因为CLI需要在Firebase中注册必要的应用。如果配置成功,会看到类似以下的确认信息:

bash 复制代码
✔ You have to choose a configuration type. Either build configuration (most likely choice) or a target set up. · Build configuration
✔ Please choose one of the following build configurations · Debug-dev
i Found 40 Firebase projects. Selecting project flutter-ship-dev.         
✔ Which platforms should your configuration support (use arrow keys & space to select)? · android, ios, web
i Firebase android app com.codewithandrea.flutter_ship_app.dev is not registered on Firebase project flutter-ship-dev.
i Registered a new Firebase android app on Firebase project flutter-ship-dev.
i Firebase ios app com.codewithandrea.flutterShipApp.dev is not registered on Firebase project flutter-ship-dev.
i Registered a new Firebase ios app on Firebase project flutter-ship-dev. 
i Firebase web app flutter_ship_app (web) is not registered on Firebase project flutter-ship-dev.
i Registered a new Firebase web app on Firebase project flutter-ship-dev. 

Firebase configuration file lib/firebase_options_dev.dart generated successfully with the following Firebase apps:

Platform  Firebase App Id
web       1:424176442589:web:c86e231d1eeaba0e90cf34
android   1:424176442589:android:c5841ba53606b4c490cf34
ios       1:424176442589:ios:592b56a800affa4e90cf34

Learn more about using this file and next steps from the documentation:
 > https://firebase.google.com/docs/flutter/setup

接下来,通过运行以下命令为stg环境配置重复相同的操作:

bash 复制代码
./flutterfire-config.sh stg

最后,为prod环境配置运行:

bash 复制代码
./flutterfire-config.sh prod

完成上述操作后,项目中会生成以下新文件:

  • lib/firebase_options_dev.dart
  • lib/firebase_options_stg.dart
  • lib/firebase_options_prod.dart
  • ios/flavors/dev/GoogleService-Info.plist
  • ios/flavors/stg/GoogleService-Info.plist
  • ios/flavors/prod/GoogleService-Info.plist
  • android/app/src/dev/google-services.json
  • android/app/src/stg/google-services.json
  • android/app/src/prod/google-services.json

Firebase配置文件是否应提交到Git?

上述生成的配置文件本身并不包含敏感信息,因此从安全角度来说,将它们提交到Git仓库是可行的。

FlutterFire配置完成✅

如果按照上述所有步骤操作且未出现错误,此时所有的Firebase配置文件应该都已正确生成并添加到项目中。

在使用Firebase运行应用之前,还需要完成以下几个步骤:

  1. 安装firebase_core包,并验证应用在Android和iOS平台上能否正常运行。
  2. 在应用启动时初始化Firebase。

下面我们逐步来看。👇

安装firebase_core

在终端中运行以下命令,添加firebase_core包:

bash 复制代码
flutter pub add firebase_core
flutter pub get

在Android平台运行应用

如果你的Android应用是使用FlutterFire CLI 1.1.0或更高版本进行配置的,理论上应该可以正常运行,不会出现错误。

但如果FlutterFire设置不正确,可能会遇到以下错误:

bash 复制代码
Plugin [id: 'com.google.gms.google-services'] was not found in any of the following sources:

- Gradle Core Plugins (plugin is not in 'org.gradle' namespace)
- Included Builds (No included builds contain this plugin)
- Plugin Repositories (plugin dependency must include a version number for this source)

要解决这个问题,打开android/settings.gradle.kts文件,确保已将com.google.gms.google-services作为插件添加,内容如下:

kotlin 复制代码
plugins {    
  id("dev.flutter.flutter-plugin-loader") version "1.0.0"    
  id("com.android.application") version "8.7.0" apply false    
  // START: FlutterFire Configuration   
  id("com.google.gms.google-services") version("4.3.15") apply false    
  // END: FlutterFire Configuration
  id("org.jetbrains.kotlin.android") version "1.8.22" apply false 
}

同样,在android/app/build.gradle.kts文件的plugins块中,也应包含该插件:

kotlin 复制代码
plugins {    
  id("com.android.application")    
  // START: FlutterFire Configuration    
  id("com.google.gms.google-services")    
  // END: FlutterFire Configuration    
  id("kotlin-android")    
  // Flutter Gradle插件必须在Android和Kotlin Gradle插件之后应用    
  id("dev.flutter.flutter-gradle-plugin") 
}

应用此修复后,Android应用应该就能正常运行了。

注意:你可以在Google的Maven仓库中找到com.google.gms.google-services的最新版本。

在iOS平台运行应用

在运行iOS应用之前,打开ios/Podfile文件,确保平台版本设置为13.0或更高,内容如下:

ruby 复制代码
# Uncomment this line to define a global platform for your project
platform :ios, '13.0'

运行pod install命令后,应该就可以在iOS平台上正常运行应用了。

在应用启动时初始化Firebase

根据官方文档,应将Firebase初始化代码添加到lib/main.dart文件中,示例如下:

dart 复制代码
import 'package:flutter_ship_app/firebase_options.dart'; 

// 在main()函数中
await Firebase.initializeApp(  
  options: DefaultFirebaseOptions.currentPlatform, 
);

然而,这种默认设置并不适用于我们的多环境配置场景,因为我们为每种环境配置都创建了单独的配置文件:

那么,该如何解决这个问题呢?

方案1:集中式Firebase初始化逻辑

一种解决方法是创建一个firebase.dart文件,内容如下:

dart 复制代码
// firebase.dart
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_ship_app/firebase_options_prod.dart' as prod;
import 'package:flutter_ship_app/firebase_options_stg.dart' as stg;
import 'package:flutter_ship_app/firebase_options_dev.dart' as dev;

Future<void> initializeFirebaseApp() async {  
  // 根据构flavor确定使用哪个Firebase选项  
  final firebaseOptions = switch (appFlavor) {    
    'prod' => prod.DefaultFirebaseOptions.currentPlatform,    
    'stg' => stg.DefaultFirebaseOptions.currentPlatform,    
    'dev' => dev.DefaultFirebaseOptions.currentPlatform,    
    _ => throw UnsupportedError('无效的构建风味: $flavor'),  
  };  
  await Firebase.initializeApp(options: firebaseOptions


  };
}
通过这种方式,它会根据`appFlavor`常量的值进行判断,返回对应环境配置的`FirebaseOptions`对象。

需要注意的是,在Flutter web上使用`--flavor`选项运行时,会收到环境配置不完全支持的警告,但`appFlavor`常量依然能返回正确的值。

此时,在`lib/main.dart`中只需简单调用`await initializeFirebaseApp()`即可完成初始化,`lib/main.dart`依旧作为应用的单一入口点:
```dart
import 'firebase.dart';

void main() async {  
  WidgetsFlutterBinding.ensureInitialized();  
  await initializeFirebaseApp();  
  runApp(const MainApp()); 
}

采用这种设置,Flutter应用就能根据不同的环境配置,初始化并连接到对应的Firebase项目。

然而,这种方案存在一个隐患。👇

所有Firebase配置文件都会被打包(无法进行Tree Shaking)

仔细查看firebase.dart文件可以发现,虽然会根据flavor选择正确的Firebase配置,但三个firebase_options_*.dart文件都被导入了:

dart 复制代码
// firebase.dart
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
// 注意:三个文件均被导入
import 'package:flutter_ship_app/firebase_options_prod.dart' as prod;
import 'package:flutter_ship_app/firebase_options_stg.dart' as stg;
import 'package:flutter_ship_app/firebase_options_dev.dart' as dev;

Future<void> initializeFirebaseApp() async {
  // 根据flavor确定使用哪个Firebase选项
  final firebaseOptions = switch (appFlavor) {
    'prod' => prod.DefaultFirebaseOptions.currentPlatform,
    'stg' => stg.DefaultFirebaseOptions.currentPlatform,
    'dev' => dev.DefaultFirebaseOptions.currentPlatform,
    _ => throw UnsupportedError('无效的构建风味: $flavor'),
  };
  await Firebase.initializeApp(options: firebaseOptions);
}

这意味着在构建过程中,由于switch操作是在运行时进行的,无法通过Tree shaking去除未使用的代码,三个配置文件都会被编译并打包进最终的应用。

从理论上讲,如果有人对应用进行逆向工程,就有可能获取到开发或预发布环境的详细信息,而这些环境的安全性通常不如生产环境。

对于不涉及敏感数据的应用,这或许不是大问题,但它始终是一个潜在风险。如果想完全规避该风险,可以考虑采用更安全的方案。👇

方案2:使用多个入口点

如前所述,以下代码存在一定问题:

dart 复制代码
import 'package:flutter_ship_app/firebase_options_prod.dart' as prod;
import 'package:flutter_ship_app/firebase_options_stg.dart' as stg;
import 'package:flutter_ship_app/firebase_options_dev.dart' as dev;

更安全的做法是创建三个独立的入口点文件------main_dev.dartmain_stg.dartmain_prod.dart,示例如下:

dart 复制代码
// main_dev.dart
import 'package:flutter_ship_app/firebase_options_dev.dart';
import 'main.dart';

void main() async {
  runMainApp(DefaultFirebaseOptions.currentPlatform);
}

这些文件仅负责导入对应的firebase_options_*.dart文件,并将配置参数传递给main.dart中的函数,由该函数执行实际的初始化操作。main.dart文件内容示例如下:

dart 复制代码
// main.dart
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';

void runMainApp(FirebaseOptions firebaseOptions) async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(options: firebaseOptions);
  runApp(const MainApp());
}

这种方式确保每个构建版本仅包含所需的Firebase配置文件,是管理多环境配置的安全高效方案。

那么,如何使用正确的环境配置运行应用呢?👇

使用特定环境配置运行应用

若采用上述第二种方案,项目中会有四个相关文件:

  • main_dev.dartdev环境配置的入口点
  • main_stg.dartstg环境配置的入口点
  • main_prod.dartprod环境配置的入口点
  • main.dart:包含应用初始化核心代码

可以通过以下命令以特定环境配置运行应用:

bash 复制代码
flutter run --flavor dev -t lib/main_dev.dart
flutter run --flavor stg -t lib/main_stg.dart
flutter run --flavor prod -t lib/main_prod.dart

如此一来,每种环境配置都能使用对应的入口点,确保应用启动时连接到正确的Firebase环境。

若采用此方案,记得更新本地配置文件(如.vscode/launch.json)和CI/CD脚本,以适配这些改动。

该选择哪种方案?

两种方案各有利弊,具体选择需结合项目需求:

  • 方案1:集中式Firebase初始化 。该方案实现起来更简单快捷,允许使用单一的main.dart文件,通过运行时的动态逻辑处理不同环境配置下的Firebase选项。但由于所有Firebase配置文件都会被打包进最终应用,从安全角度考虑,并非最佳选择。
  • 方案2:为每种环境配置设置多个入口点。此方案前期配置工作较多,需要为每种环境配置创建独立的入口点文件。不过,它仅会为每个构建版本打包必要的Firebase配置文件,能有效防止攻击者获取其他环境的详细信息,安全性更高。

尽管方案2前期配置相对繁琐,但对于使用Firebase的多环境Flutter应用,我更推荐采用该方案。👍

方案1更适用于非Firebase应用场景,此时可以使用--dart-define-from-file,并在.env.dev.env.stg.env.prod等单独文件中为每种环境配置定义环境变量。想了解更多内容,可阅读我之前写过的《flutter工程化之动态配置》。

总结

通过将FlutterFire与简单的Shell脚本相结合,原本复杂易错的多环境配置流程得到了极大简化。现在,无需再为每种环境配置手动配置Firebase,仅需使用一个脚本就能为所有环境生成所需文件,既节省时间,又降低了出错概率。

集中式Firebase初始化方案提供了一种快速便捷的方法,借助单一的main.dart文件和运行时逻辑,使应用在启动时连接到正确的Firebase项目。然而,该方案会将所有Firebase配置文件打包,对于安全性要求较高的应用不太适用。

多入口点方案则确保每个构建版本仅包含必要的Firebase配置,在处理敏感数据或开发生产级应用时,是更优的选择。

无论采用哪种方案,Flutter应用都能自动连接到对应的Firebase环境(devstgprod),确保应用在开发和生产的各个阶段都能稳定运行。

这种配置方式让Flutter与Firebase应用的多环境管理变得轻松许多,我自己在生产应用中也一直在使用,效果非常好。✅

相关推荐
同志3271330 分钟前
用HTML+CSS做了一个网易云音乐客户端首页
前端·css
小猪欧巴哟31 分钟前
pnpm install 安装项目依赖遇到 illegal operation on a directory, symlink 问题
前端·vue.js
独角仙梦境32 分钟前
🚀🚀🚀学习这个思路,你也能手撸自己的专属vip脚手架🚀🚀🚀
前端
CJWbiu35 分钟前
Github Action + docker 实现自动化部署
前端·自动化运维
关山35 分钟前
在TS中如何在子进程中动态实例化一个类
前端
吃瓜群众i36 分钟前
兼容IE8浏览器的8个实用知识点
前端·javascript
前端烨39 分钟前
vue3子传父——v-model辅助值传递
前端·vue3·组件传值
Mintopia1 小时前
Three.js 在数字孪生中的应用场景教学
前端·javascript·three.js
夕水1 小时前
自动化按需导入组件库的工具rust版本完成开源了
前端·rust·trae