flutter_flavor_orchestrator 0.4.0
flutter_flavor_orchestrator: ^0.4.0 copied to clipboard
A build-time orchestrator for managing Flutter flavors, native configurations, and provisioning files across Android and iOS platforms.
Flutter Flavor Orchestrator #
☕ Buy me a coffee on Ko-fi - If this package helps you, consider supporting my work!
Build-time orchestration for Flutter flavors across Android and iOS. Configure environment-specific app identity, native metadata, provisioning files, and resource mappings from a single YAML source.
What's New (v0.4.0) #
plancommand — Preview operations without mutating files:flutter_flavor_orchestrator plan --flavor dev--output json— Machine-readable plan output:plan --flavor dev --output jsonFlavorOrchestrator.planFlavor()— New public method returning anExecutionPlanwithout executing it
Previous: What's New (v0.3.0) #
- Typed Operation Models —
OperationKind,PlannedOperation,ExecutionPlanadded as public API; each operation is now a first-class immutable value withtoJson()support - Shared Planning Foundation —
FlavorOrchestratorbuilds anExecutionPlanbefore executing any apply; now exposed publicly viaplanFlavor()for theplancommand AssetProcessor.planFileMappings()— generate file-mapping operations without touching the file system (preview without side-effects)
Previous: What's New (v0.2.0) #
- Dry-run Apply Mode (
--dry-run) - Execute full apply processing without changing files - Destination Presence Validation - Dry-run validates destination files/directories exist for every write/copy path
- Safer Preflight for CI/CD - Validate flavor application end-to-end before running a real apply
✨ Features #
- Flavor management - Configure dev, staging, production, and custom environments
- Cross-platform native updates - Apply changes to Android and iOS project files
- Native file processors - Update AndroidManifest, Gradle/Gradle KTS, Info.plist, and Xcode project settings
- Format preservation - Keep original AndroidManifest indentation and structure
- Provisioning support - Manage
google-services.jsonandGoogleService-Info.plist - File mappings - Copy flavor-specific files and recursive directories
- Atomic directory replacement - Backup/restore-safe replacement of destination directories
- Safe operations - Automatic backup and rollback on failures
- YAML-driven configuration - Single declarative config for all flavors
- Typed execution plan -
ExecutionPlan,PlannedOperation,OperationKindmodels withtoJson() - CLI workflow -
apply,plan,list,info, andvalidatecommands plancommand — Preview operations without file mutations; text and JSON output- Validation and error handling - Pre-checks for config, files, and required fields
- Documentation and examples - Full example project and practical guides
📋 Table of Contents #
- Installation
- What's New (v0.4.0)
- Previous: What's New (v0.3.0)
- Quick Start
- Pub.dev Workflow
- Configuration
- CLI Usage
- What Gets Modified
- Advanced Configuration
- Architecture
- Examples
- Example Project Hints
- API Documentation
- Contributing
- Roadmap
- License
🚀 Installation #
Add flutter_flavor_orchestrator to your pubspec.yaml dev dependencies:
dev_dependencies:
flutter_flavor_orchestrator: ^0.4.0
Then run:
flutter pub get
Activate the CLI tool globally (optional):
dart pub global activate flutter_flavor_orchestrator
⚡ Quick Start #
1. Create Configuration File #
Create a flavor_config.yaml file in your project root:
dev:
bundle_id: com.example.myapp.dev
app_name: MyApp Dev
metadata:
API_URL: https://dev-api.example.com
provisioning:
android_google_services: configs/dev/google-services.json
ios_google_service: configs/dev/GoogleService-Info.plist
production:
bundle_id: com.example.myapp
app_name: MyApp
metadata:
API_URL: https://api.example.com
provisioning:
android_google_services: configs/production/google-services.json
ios_google_service: configs/production/GoogleService-Info.plist
2. Apply a Flavor #
# From your project root
flutter pub run flutter_flavor_orchestrator apply --flavor dev
# Alternative (Dart-native invocation)
dart run flutter_flavor_orchestrator apply --flavor dev
3. Build Your App #
flutter clean
flutter pub get
flutter build apk # or flutter build ios
That's it! Your app is now configured for the dev flavor.
Pub.dev Workflow #
Use this sequence when integrating the package into a real app:
# 1) Install deps
flutter pub get
# 2) Validate configuration before applying
flutter pub run flutter_flavor_orchestrator validate
# 3) Inspect available flavors
flutter pub run flutter_flavor_orchestrator list
# 4) Apply flavor
flutter pub run flutter_flavor_orchestrator apply --flavor dev --verbose
# 5) Build or run
flutter run
Recommended for CI/CD:
- Run
validateas an early pipeline step - Use explicit flavor names (
dev,staging,production) in build jobs - Keep flavor-specific files under
configs/,assets/, andresources/ - Pass
--configto load a YAML file from a secure external path (for example Jenkins workspace/secret mounts)
⚙️ Configuration #
Configuration File Location #
You can place your flavor configuration in either:
- Dedicated file:
flavor_config.yamlin your project root (recommended) - In pubspec.yaml: Under a
flavor_configsection - External file path: Any YAML file passed at runtime with
--config
External path examples:
flutter_flavor_orchestrator apply --flavor production --config /secure/jenkins/flavor_config.yaml
flutter_flavor_orchestrator validate --config ./ci/flavor_config.yaml
Configuration Options #
Each flavor supports the following configuration options:
| Option | Type | Required | Description |
|---|---|---|---|
bundle_id |
String | ✅ | Bundle identifier (iOS) / Package name (Android) |
app_name |
String | ✅ | Display name of the application |
icon_path |
String | ❌ | Path to app icon assets |
metadata |
Map | ❌ | Custom key-value pairs to inject into manifests |
assets |
List | ❌ | Flavor-specific asset paths |
dependencies |
Map | ❌ | Flavor-specific dependency overrides |
provisioning |
Object | ❌ | Provisioning file configuration |
android_min_sdk_version |
Integer | ❌ | Android minimum SDK version |
android_target_sdk_version |
Integer | ❌ | Android target SDK version |
android_compile_sdk_version |
Integer | ❌ | Android compile SDK version |
ios_min_version |
String | ❌ | iOS minimum deployment target |
custom_gradle_config |
Map | ❌ | Custom Gradle configuration snippets |
custom_info_plist_entries |
Map | ❌ | Custom Info.plist entries |
file_mappings |
Map | ❌ | Flavor-specific file/folder copying (source→destination) |
replace_destination_directories |
Boolean | ❌ | Replace existing directories completely (default: false) |
Provisioning Configuration #
provisioning:
android_google_services: path/to/google-services.json
ios_google_service: path/to/GoogleService-Info.plist
additional_files:
destination/path: source/path
File Mappings (New in v0.1.7) #
Copy flavor-specific files and folders from source to destination paths. Supports both individual files and recursive directory copying:
dev:
bundle_id: com.example.app.dev
app_name: MyApp Dev
# Enable complete directory replacement (optional, default: false)
replace_destination_directories: true
file_mappings:
# Copy individual configuration files
'lib/config/app_config.dart': 'configs/dev/app_config.dart'
'lib/config/constants.dart': 'configs/shared/constants.dart'
# Copy flavor-specific icons
'assets/app_icon.svg': 'assets/icons/dev/app_icon.svg'
# Recursively copy entire directories
'android/app/src/main/res/drawable': 'assets/dev/android/drawables'
'ios/Runner/Assets.xcassets': 'assets/dev/ios/assets'
# Replace entire theme directory (useful with replace_destination_directories)
'lib/theme': 'resources/dev/themes'
production:
bundle_id: com.example.app
app_name: MyApp
replace_destination_directories: true
file_mappings:
'lib/config/app_config.dart': 'configs/production/app_config.dart'
'lib/config/constants.dart': 'configs/shared/constants.dart'
'assets/app_icon.svg': 'assets/icons/production/app_icon.svg'
'lib/theme': 'resources/production/themes'
Features:
- 📁 Copy individual files or entire directory trees recursively
- 🔄 Automatically replaces existing files at destination
- 📂 Creates destination directories if they don't exist
- 🔍 Detailed logging for each file operation
- ⚠️ Skips non-existent source paths with warnings
- 🔙 Full backup and rollback support
Directory Replacement Mode:
When replace_destination_directories: true:
- 🔒 Safe Backup: Existing destination directory is temporarily renamed
- 📋 Copy New: Complete directory tree is copied from source
- ✅ Success: Backup directory is removed
- ❌ Failure: Original directory is automatically restored
This ensures atomic directory replacement - the destination is either completely replaced or left unchanged.
See example/assets/icons/README.md and example/resources/README.md for practical examples.
Complete Example #
See example/flavor_config.yaml for a comprehensive configuration example.
🎮 CLI Usage #
The package provides a powerful command-line interface:
Apply Command #
Apply a flavor configuration to your project:
# Apply to both platforms
flutter_flavor_orchestrator apply --flavor dev
# Apply using an external YAML config file
flutter_flavor_orchestrator apply --flavor production --config /secure/jenkins/flavor_config.yaml
# Apply to Android only
flutter_flavor_orchestrator apply --flavor staging --platform android
# Dry-run apply (execute checks without changing files)
flutter_flavor_orchestrator apply --flavor dev --dry-run
# Apply to iOS only
flutter_flavor_orchestrator apply --flavor production --platform ios
# Enable verbose output
flutter_flavor_orchestrator apply --flavor dev --verbose
Output includes:
file_mappingscount for the selected flavorreplace_destination_directoriesvalue- Mapping details (
destination <- source) when--verboseis enabled - In
--dry-run, all operations are validated and no files are modified
Plan Command #
Preview the operations that would be performed for a flavor, without mutating any files:
# Preview both platforms (text output)
flutter_flavor_orchestrator plan --flavor dev
# Preview as machine-readable JSON
flutter_flavor_orchestrator plan --flavor dev --output json
# Preview Android-only plan
flutter_flavor_orchestrator plan --flavor staging --platform android
# Preview from an external YAML config
flutter_flavor_orchestrator plan --flavor production --config /secure/jenkins/flavor_config.yaml
Output includes a per-platform section listing each operation with its kind (copyFile, copyDirectory, writeFile, skip), description, and source/destination paths.
JSON output (--output json) returns the serialised ExecutionPlan with stable top-level keys:
flavor— flavor nameplatforms— target platformstotal_operations,active_operations,skipped_operations— operation countsoperations— ordered list of operation objects
List Command #
List all available flavors:
flutter_flavor_orchestrator list
# List flavors from external YAML
flutter_flavor_orchestrator list --config ./ci/flavor_config.yaml
Output includes, for each flavor:
file_mappingscountreplace_destination_directoriesvalue
Info Command #
Display detailed information about a specific flavor:
flutter_flavor_orchestrator info --flavor production
# Inspect a flavor from external YAML
flutter_flavor_orchestrator info --flavor production --config ./ci/flavor_config.yaml
Output includes:
- Full
file_mappingsentries (destination <- source) replace_destination_directoriesvalue- Explanation of when directory replacement applies and rollback behavior
Validate Command #
Validate all flavor configurations:
flutter_flavor_orchestrator validate
# Validate external YAML
flutter_flavor_orchestrator validate --config ./ci/flavor_config.yaml
Output includes, for each flavor:
file_mappingscountreplace_destination_directoriesvalue- A note when directory replacement is enabled for directory mappings
Help #
Display help information:
flutter_flavor_orchestrator --help
🔨 What Gets Modified #
Android #
When you apply a flavor, the following Android files are automatically updated:
android/app/src/main/AndroidManifest.xml
- ✏️ Package name (
packageattribute) - ✏️ Application label (
android:label) - ✏️ Metadata entries (
<meta-data>tags) - 🎨 Preserves original formatting - Maintains exact indentation, whitespace, and structure
android/app/build.gradle or android/app/build.gradle.kts
- ✏️ Application ID (
applicationId) - ✏️ SDK versions (
minSdkVersion,targetSdkVersion,compileSdkVersion) - ✏️ Custom Gradle configuration
- 🔄 Supports both Groovy (
.gradle) and Kotlin (.gradle.kts) build scripts
android/app/google-services.json
- 📋 Copied from configured path
iOS #
ios/Runner/Info.plist
- ✏️ Bundle display name (
CFBundleDisplayName) - ✏️ Bundle identifier (
CFBundleIdentifier) - ✏️ Minimum OS version (
MinimumOSVersion) - ✏️ Custom plist entries
ios/Runner.xcodeproj/project.pbxproj
- ✏️ Product bundle identifier (
PRODUCT_BUNDLE_IDENTIFIER) - ✏️ Deployment target (
IPHONEOS_DEPLOYMENT_TARGET)
ios/Runner/GoogleService-Info.plist
- 📋 Copied from configured path
🏗️ Advanced Configuration #
Custom Gradle Configuration #
Inject custom Gradle snippets:
custom_gradle_config:
defaultConfig: |
buildConfigField "String", "API_URL", "\"https://api.example.com\""
buildConfigField "boolean", "DEBUG_MODE", "false"
buildTypes: |
release {
shrinkResources true
minifyEnabled true
}
Custom Info.plist Entries #
Add custom iOS configuration:
custom_info_plist_entries:
NSAppTransportSecurity:
NSAllowsArbitraryLoads: true
UIBackgroundModes:
- fetch
- remote-notification
ITSAppUsesNonExemptEncryption: false
Metadata Injection #
Add custom metadata to both platforms:
metadata:
API_URL: https://api.example.com
API_KEY: your_api_key
FEATURE_FLAG_X: true
MAX_RETRIES: 3
Android: Added as <meta-data> tags in AndroidManifest.xml
iOS: Added as custom entries in Info.plist
🏛️ Architecture #
The package follows Clean Architecture principles:
lib/
├── src/
│ ├── models/ # Data models
│ │ ├── execution_plan.dart # Ordered plan of PlannedOperations (v0.3.0)
│ │ ├── flavor_config.dart
│ │ ├── operation_kind.dart # OperationKind enum (v0.3.0)
│ │ ├── planned_operation.dart # Single step descriptor (v0.3.0)
│ │ └── provisioning_config.dart
│ ├── processors/ # Platform processors
│ │ ├── android_processor.dart
│ │ ├── asset_processor.dart # planFileMappings() added (v0.3.0)
│ │ └── ios_processor.dart
│ ├── utils/ # Utilities
│ │ ├── file_manager.dart
│ │ └── logger.dart
│ ├── config_parser.dart # Configuration parsing
│ └── orchestrator.dart # Main orchestrator (_buildExecutionPlan, planFlavor, v0.4.0)
└── flutter_flavor_orchestrator.dart # Public API
Key Components #
- FlavorOrchestrator: Coordinates the entire process; builds an
ExecutionPlanbefore applying - ConfigParser: Parses and validates YAML configurations
- AndroidProcessor: Handles Android-specific modifications
- IosProcessor: Handles iOS-specific modifications
- AssetProcessor: Handles file-mapping copy and planning operations
- ExecutionPlan / PlannedOperation / OperationKind: Typed, immutable operation models with JSON serialisation
- FileManager: Provides safe file operations with backup/rollback
📚 Examples #
Check out the example directory for a complete working example with:
- ✅ Multiple flavor configurations
- ✅ Firebase integration
- ✅ Custom metadata
- ✅ Platform-specific configurations
- ✅ Complete Flutter app
- ✅ File mappings and safe directory replacement
- ✅ Visual flavor verification in the running UI
Example Project Hints #
The example app is designed so you can immediately see flavor changes on screen.
Quick demo in example/ #
cd example
flutter pub get
# Apply development flavor
flutter pub run flutter_flavor_orchestrator apply --flavor dev --verbose
flutter run
Then switch flavor and run again:
flutter pub run flutter_flavor_orchestrator apply --flavor staging --verbose
flutter run
What to look for in the UI:
- Flavor-specific SVG icon
- Environment/debug banner values
- Color and typography preview from copied theme files
- API/config values from copied
lib/config/app_config.dart
Useful example references:
- Full config: example/flavor_config.yaml
- File mapping assets: example/assets/icons/README.md
- Theme replacement resources: example/resources/README.md
- Gradle syntax notes: example/CUSTOM_GRADLE_CONFIG.md
📖 API Documentation #
Programmatic Usage #
You can also use the package programmatically in your Dart code:
import 'package:flutter_flavor_orchestrator/flutter_flavor_orchestrator.dart';
void main() async {
final orchestrator = FlavorOrchestrator(
projectRoot: '/path/to/project',
configPath: '/secure/jenkins/flavor_config.yaml',
verbose: true,
);
// Apply a flavor
final success = await orchestrator.applyFlavor(
'dev',
platforms: ['android', 'ios'],
);
// Preview operations without mutating files
final plan = await orchestrator.planFlavor(
'dev',
platforms: ['android', 'ios'],
);
// List flavors
final flavors = await orchestrator.listFlavors();
// Validate configurations
final valid = await orchestrator.validateConfigurations();
}
Core Classes #
- FlavorConfig: Represents a complete flavor configuration
- ProvisioningConfig: Provisioning file configuration
- ExecutionPlan: Ordered list of
PlannedOperations for a flavor; serialisable viatoJson() - PlannedOperation: Immutable descriptor of a single orchestration step with kind, paths, and platform
- OperationKind: Enum —
copyFile,copyDirectory,writeFile,skip - ConfigParser: Configuration parsing and validation
- FlavorOrchestrator: Main orchestration logic
See the API documentation for detailed class and method documentation.
🔒 Safety Features #
Automatic Backups #
The orchestrator automatically creates backups of all modified files before making changes. If an error occurs, all changes are automatically rolled back.
Validation #
All configurations are validated before being applied:
- ✅ Required fields presence
- ✅ Bundle ID format validation
- ✅ File existence checks
- ✅ YAML syntax validation
Error Handling #
Comprehensive error handling with clear, actionable error messages:
- 🔴 Missing configuration files
- 🔴 Invalid bundle ID formats
- 🔴 Missing native directories
- 🔴 File operation failures
🧪 Testing #
The package includes a comprehensive test suite:
# Run all tests
dart test
# Run with coverage
dart test --coverage
🤝 Contributing #
Contributions are welcome! Please read our Contributing Guide for details on our code of conduct and the process for submitting pull requests.
📄 License #
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Acknowledgments #
- Built with ❤️ by Alessio La Mantia
- Inspired by the need for better flavor management in Flutter projects
- Uses excellent packages:
args,yaml,xml,path
📞 Support #
- 📧 Report issues on GitHub Issues
- 💬 Ask questions on Stack Overflow
- 📖 Read the documentation
🗺️ Roadmap #
The full roadmap is in ROADMAP.md. Here is a compact summary of progress and upcoming milestones toward the v1.0.0 stable release.
Released #
| Version | Highlights |
|---|---|
| v0.1.x | Initial CLI (apply, list, info, validate), Android/iOS processors, file mappings, dry-run, external config path |
| v0.2.0 | Dry-run destination-presence validation, safer CI preflight |
| v0.3.0 ✅ | OperationKind, PlannedOperation, ExecutionPlan typed models · shared _buildExecutionPlan() in orchestrator · AssetProcessor.planFileMappings() |
Upcoming #
| Version | Theme | Key deliverable |
|---|---|---|
| v0.4.0 ✅ | Preview | plan command — preview operations without mutating files; planFlavor() public API; --output json |
| v0.5.0 | Safety | rollback command + timestamped backup before every non-dry-run apply |
| v0.6.0 | Conflict protection | Duplicate-target detection and --force guardrails |
| v0.7.0 | Automation contract | --output json for all commands; standardised exit codes |
| v0.8.0 | Schema hardening | schema_version enforcement, validate --strict, migration scaffold |
| v0.9.0 | Diagnostics | doctor command with categorised findings (error/warning/info) |
| v1.0.0 | Stable | Production-ready doctor, docs freeze, no breaking changes without migration path |
| v1.1.0 | Post-1.0 | Env-var interpolation (${VAR:-default}), init command |
Made with ❤️ for the Flutter community