app-config

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Flavor Architecture

构建风味架构

  • Define three flavors:
    dev
    ,
    staging
    ,
    prod
  • Use a single
    main.dart
    entry point for all flavors
  • Pass flavor-specific configuration via
    --dart-define-from-file
    :
    bash
    flutter run --flavor dev --dart-define-from-file=config/dev.json
    flutter run --flavor staging --dart-define-from-file=config/staging.json
    flutter run --flavor prod --dart-define-from-file=config/prod.json
  • NEVER create separate
    main_dev.dart
    ,
    main_staging.dart
    ,
    main_prod.dart
    entry points
  • 定义三种构建风味:
    dev
    staging
    prod
  • 所有构建风味共用**单一
    main.dart
    **作为入口文件
  • 通过
    --dart-define-from-file
    参数传入对应风味的配置:
    bash
    flutter run --flavor dev --dart-define-from-file=config/dev.json
    flutter run --flavor staging --dart-define-from-file=config/staging.json
    flutter run --flavor prod --dart-define-from-file=config/prod.json
  • 绝对不要为不同构建风味单独创建
    main_dev.dart
    main_staging.dart
    main_prod.dart
    这类入口文件

Config JSON Structure

配置JSON结构

  • Store per-flavor JSON config files in a
    config/
    directory at project root:
    config/
    ├── dev.json
    ├── staging.json
    └── prod.json
  • Example
    config/dev.json
    :
    json
    {
      "FLAVOR": "dev",
      "BASE_URL": "https://api-dev.example.com",
      "APP_NAME": "MyApp Dev",
      "ENABLE_LOGGING": "true",
      "ENABLE_CRASHLYTICS": "false"
    }
  • NEVER put secrets (API keys, signing credentials) in these JSON files — use CI-injected env vars or
    flutter_secure_storage
  • Add
    config/*.json
    to
    .gitignore
    if they contain any environment-specific secrets; otherwise commit them for team convenience
  • 各风味对应的JSON配置文件统一存放在项目根目录的
    config/
    文件夹下:
    config/
    ├── dev.json
    ├── staging.json
    └── prod.json
  • 示例
    config/dev.json
    json
    {
      "FLAVOR": "dev",
      "BASE_URL": "https://api-dev.example.com",
      "APP_NAME": "MyApp Dev",
      "ENABLE_LOGGING": "true",
      "ENABLE_CRASHLYTICS": "false"
    }
  • 绝对不要在这些JSON文件中存放密钥(API密钥、签名凭证等),请使用CI注入的环境变量或者
    flutter_secure_storage
    存储敏感信息
  • 如果
    config/*.json
    文件包含任意环境专属的密钥,请将其加入
    .gitignore
    ;否则可以提交到代码仓库方便团队成员使用

Entry Point Pattern

入口文件模式

dart
// lib/main.dart
void main() {
  const flavor = String.fromEnvironment('FLAVOR', defaultValue: 'dev');
  AppConfig.init(flavor: Flavor.fromString(flavor));
  runApp(const App());
}
  • Read all compile-time values via
    String.fromEnvironment('KEY')
    or
    bool.fromEnvironment('KEY')
  • Use a sealed
    Flavor
    enum:
    sealed class Flavor { dev, staging, prod }
  • AppConfig
    is a singleton holding flavor, base URL, feature flags, and Firebase config
dart
// lib/main.dart
void main() {
  const flavor = String.fromEnvironment('FLAVOR', defaultValue: 'dev');
  AppConfig.init(flavor: Flavor.fromString(flavor));
  runApp(const App());
}
  • 通过
    String.fromEnvironment('KEY')
    或者
    bool.fromEnvironment('KEY')
    读取所有编译期常量
  • 使用密封类
    Flavor
    枚举:
    sealed class Flavor { dev, staging, prod }
  • AppConfig
    是一个单例类,用于存储构建风味、基础请求地址、功能开关和Firebase配置

Environment Configuration

环境配置

  • Store per-flavor config in a centralized
    AppConfig
    class:
    • baseUrl
      — API endpoint per environment
    • enableLogging
      — verbose logging for dev only
    • enableCrashlytics
      — disabled in dev
    • appName
      — display name per flavor (e.g., "MyApp Dev", "MyApp")
  • All values come from the JSON file via
    --dart-define-from-file
    — NO hardcoded per-flavor logic in Dart code
  • NEVER put secrets in the JSON config files — use
    flutter_secure_storage
    or CI-injected env vars
  • 各构建风味的配置统一存放在中心化的
    AppConfig
    类中:
    • baseUrl
      — 对应环境的API接口地址
    • enableLogging
      — 仅开发环境开启详细日志
    • enableCrashlytics
      — 开发环境关闭崩溃上报
    • appName
      — 对应构建风味的App显示名称(例如:"MyApp Dev"、"MyApp")
  • 所有配置值都通过
    --dart-define-from-file
    参数从JSON文件中读取,Dart代码中绝对不要硬编码任何和构建风味相关的逻辑
  • 绝对不要在JSON配置文件中存放密钥,请使用
    flutter_secure_storage
    或者CI注入的环境变量

Platform-Specific Flavor Setup

各平台专属构建风味配置

Android

Android

  • Define
    flavorDimensions
    and
    productFlavors
    in
    android/app/build.gradle
    :
    groovy
    flavorDimensions "environment"
    productFlavors {
        dev { dimension "environment"; applicationIdSuffix ".dev"; resValue "string", "app_name", "MyApp Dev" }
        staging { dimension "environment"; applicationIdSuffix ".staging"; resValue "string", "app_name", "MyApp Staging" }
        prod { dimension "environment"; resValue "string", "app_name", "MyApp" }
    }
  • android/app/build.gradle
    中定义
    flavorDimensions
    productFlavors
    groovy
    flavorDimensions "environment"
    productFlavors {
        dev { dimension "environment"; applicationIdSuffix ".dev"; resValue "string", "app_name", "MyApp Dev" }
        staging { dimension "environment"; applicationIdSuffix ".staging"; resValue "string", "app_name", "MyApp Staging" }
        prod { dimension "environment"; resValue "string", "app_name", "MyApp" }
    }

iOS

iOS

  • Create Xcode schemes for each flavor (Dev, Staging, Prod)
  • Use xcconfig files for per-flavor bundle ID, display name, and signing
  • Map Flutter flavors to Xcode schemes in
    ios/Runner.xcodeproj
  • 为每个构建风味创建对应的Xcode scheme(开发、预发布、生产)
  • 使用xcconfig文件配置各风味对应的包ID、App显示名称和签名信息
  • ios/Runner.xcodeproj
    中将Flutter构建风味和Xcode scheme做映射

Firebase Per-Flavor

多环境Firebase配置

  • Use separate Firebase projects per flavor (dev, staging, prod)
  • Place
    google-services.json
    (Android) and
    GoogleService-Info.plist
    (iOS) in flavor-specific directories
  • Use
    flutterfire configure
    with
    --project
    flag for each environment
  • 每个构建风味使用独立的Firebase项目(开发、预发布、生产)
  • 将Android端的
    google-services.json
    和iOS端的
    GoogleService-Info.plist
    存放到对应风味的专属目录下
  • 针对每个环境使用
    flutterfire configure
    命令加上
    --project
    参数进行配置

Build Commands

构建命令

bash
undefined
bash
undefined

Development

开发环境

flutter run --flavor dev --dart-define-from-file=config/dev.json
flutter run --flavor dev --dart-define-from-file=config/dev.json

Staging

预发布环境

flutter run --flavor staging --dart-define-from-file=config/staging.json
flutter run --flavor staging --dart-define-from-file=config/staging.json

Production release

生产环境打包

flutter build appbundle --flavor prod --dart-define-from-file=config/prod.json --release flutter build ipa --flavor prod --dart-define-from-file=config/prod.json --release
undefined
flutter build appbundle --flavor prod --dart-define-from-file=config/prod.json --release flutter build ipa --flavor prod --dart-define-from-file=config/prod.json --release
undefined

Rules

规范

  • NEVER hardcode environment-specific values (URLs, API keys, feature flags)
  • NEVER commit production secrets to the repository
  • Every team member MUST be able to run any flavor locally with a single command
  • CI/CD pipelines MUST specify both
    --flavor
    and
    --dart-define-from-file
    explicitly
  • Use a single
    main.dart
    — NEVER create separate entry points per flavor
  • 绝对不要硬编码和环境相关的配置值(接口地址、API密钥、功能开关等)
  • 绝对不要将生产环境的密钥提交到代码仓库
  • 所有团队成员必须可以通过单条命令在本地运行任意构建风味的App
  • CI/CD流水线必须显式指定
    --flavor
    --dart-define-from-file
    两个参数
  • 仅使用单一
    main.dart
    作为入口,绝对不要为每个构建风味单独创建入口文件