在真实项目开发中,多环境切换是每个开发者都会遇到的常见需求。无论是开发、测试、预发布还是正式环境,甚至是白标产品的构建,都需要能够快速、可靠地在不同配置间切换。
手动修改配置文件虽然简单直接,但随着项目复杂度增加,这种方式容易出错且效率低下。而使用Flavor,可以将不同环境的配置完全隔离,通过构建时选择特定flavor,确保每次构建都使用正确的配置,大大降低了人为错误的风险,提高了开发效率和发布安全性,显著提升 CI/CD 流程效率与开发团队协作体验。
什么是Flutter flavor?
Flutter flavor本质上类似于Android的Build Variant和iOS的Scheme。它允许开发者从同一代码库构建多个具有不同配置的应用版本,每个版本可以拥有独立的:
- 应用标识(包名/Bundle ID)
- 资源文件(图标、字符串等)
- 构建配置
- 运行时参数
其典型应用场景包括:
- 不同环境间的配置隔离(dev/staging/prod)
- 灰度测试或白标化需求
- 免费版/付费版功能差异化
这样我们便可以在构建时自动区分包名与资源、简化构建流程、减少手动出错风险、便于团队协作与 CI/CD 自动部署。
在Android上配置flavor
根据 Flutter 官方文档,在Android上配置Flavor主要通过修改android/app/build.gradle
文件实现:
arduino
android {
flavorDimensions "default"
productFlavors {
dev {
dimension "default"
applicationIdSuffix ".dev"
resValue "string", "app_name", "MyApp Dev"
}
staging {
dimension "default"
applicationIdSuffix ".stg"
resValue "string", "app_name", "MyApp Stg"
}
prod {
dimension "default"
applicationIdSuffix ".prod"
resValue "string", "app_name", "MyApp"
}
}
}
配置项说明:
其中flavorDimensions定义了 flavor 所属的维度名称。在 Android Gradle 插件(3.0.0 及以上)中,每个 flavor 必须指定所属维度,否则会报错。 **dimension "default":**将当前 flavor(如 dev)归属到 default 维度中。所有 flavors 必须指定维度,且同一维度内的 flavors 为互斥条件(仅能选其中一个构建)。
**applicationIdSuffix ".dev":**设置包名后缀,如果基础包名为 com.example.app,dev 版本实际的包名为 com.example.app.dev。这样我们可以在同一设备上同时安装不同 flavor 的 APK,便于开发、测试不同环境版本 。
resValue "string", "app_name", "MyApp Dev": 动态生成一个 Android string 资源,资源名为 app_name,值为 MyApp Dev。这样当我们将AndroidManifest.xml 中的 android:label 设置为 @string/app_name之后,我们启动不同的flavor的应用后,每个app将会显示各自的app名称。 之后运行命令:
arduino
flutter run --flavor dev -t lib/main_dev.dart
flutter run --flavor staging -t lib/main_staging.dart
flutter run --flavor prod -t lib/main_prod.dart
就可以分别运行安装不同配置多个版本的app,同时app名称和包名保持独立。
在iOS上配置flavor
iOS上的Flavor配置主要通过配置Scheme以及Build Configuration来实现:
- 打开
ios/Runner.xcworkspace
- 在 Xcode 中通过 Product → Scheme → New Scheme 添加
dev
、staging
、prod
- 在 Info → Configurations 中新建多种配置
Debug-dev
,Release-dev
等

- 为每个 Scheme 指定不同的 Bundle Identifier 和 Display Name(通过
APP_DISPLAY_NAME
自定义变量绑定到 Info.plist)
我们可以创建不同的资源目录如(DevAssets,StagingAssets等),在Build Settings中为每个Configuration设置不同的图标集以及名称。
运行时配置
flavors 适合构建时配置(如包名、图标、App 名称),而 runtime 参数 (如 API URL、调试标识、公钥私钥)推荐用**---dart-define**或 .env/JSON 配置文件方式加载:
---dart-define:
arduino
flutter run --flavor dev --dart-define API_URL=https://dev.api.com
运行期间,在代码中可通过 const String.fromEnvironment('API_URL') 获取对应值。
.env/JSON:
- 创建环境特定配置文件(如
config_dev.json
,config_prod.json
) - 在应用启动时加载对应配置:
arduino
Future<void> main() async {
const flavor = String.fromEnvironment('FLAVOR', defaultValue: 'dev');
final config = await loadConfig('config_$flavor.json');
runApp(MyApp(config: config));
}
Firebase 多环境配置
如果 App 使用 Firebase,flavor 通常需要配置独立的 Firebase 项目与配置文件。推荐使用 FlutterFire CLI:
- 为每个 flavor(dev/staging/prod)分别创建 Firebase 项目
- 使用 flutterfire configure --project --out-lib firebase_options_.dart
- 每个 flavor 对应不同的配置文件,并且可通过独立入口文件(main_dev.dart 等)加载
在这里我的建议是为每个 flavor 设置独立入口文件,以确保只有当前 flavor 的配置被打包。
常见的问题
问题 | 建议解决方式 |
---|---|
构建混乱、资源覆盖 | 引入统一管理工具(flutter_flavorizr / FlutterFire CLI)并生成文档 |
flavor 命名规范 | 建议统一使用 dev/staging/prod 命名,Bundle ID 后缀匹配(如 .dev, .stg) |
敏感配置泄露风险 | 使用多个入口文件,避免全部配置都打包在同一个 main.dart 中 |
CI/CD 集成不统一 | 使用 shell 脚本或 YAML 配置统一构建流程,避免人为误操作 |
通过使用flavor,我们可以在同一代码库中轻松管理多个 App 环境,从 Android 与 iOS 构建配置入手,再结合运行时参数与 Firebase 设置,将 App 的构建流程标准化、自动化。结合 CI/CD 自动发布能力,我们的项目将具备真正的工程级管理能力,适应复杂真实项目场景。