SKILL.md
Setup Variants Skill
This skill configures dev, stg, and prd build variants for both Android and iOS in a standard Flutter project.
Reference Guide: Setup Development Environments Guide
Prerequisites
- A standard Flutter project structure
secureFilesdirectory with signing keys and flavor configurations (json)
Steps
1. Android Setup
- Modify
android/app/build.gradle.kts:
- Apply the flavor and signing configuration. - Use the template from local resources: .agent/skills/setupvariants/resources/android/buildgradle_flavors.kts. - Ensure signingConfigs map to secureFiles correctly.
- Update .gitignore:
- Add keystore.properties to android/.gitignore (defensive, in case it is copied locally).
- Verify Android Build:
- Run ./gradlew bundleRelease (or assembleRelease) to ensure gradle syncs and builds correctly.
2. iOS Setup
The iOS config lives in ios/Runner.xcodeproj/project.pbxproj (build configurations), ios/Runner.xcodeproj/xcshareddata/xcschemes/ (schemes), and ios/Flutter/*.xcconfig. One idempotent script does the pbxproj + xcconfig work; the schemes and Info.plist are edited once by hand.
- Copy scripts to
ios/scripts/andchmod +xthe shell one:
- setupiosflavors.py (pbxproj build configs + per-flavor xcconfigs + the GoogleService build phase) - copygoogleserviceplist.sh > Do not reintroduce extractdartdefines.sh / updateproject.py - a build-time > extraction script runs after Flutter probes the bundle id, so flutter run targets a > stale id; and update_project.py was not idempotent (re-runs duplicated every config).
- Reset a mangled
project.pbxprojfirst (only if it was previously processed).
setupiosflavors.py expects a stock Flutter pbxproj. If flavor configs already exist (possibly duplicated), restore the pristine file from git history, e.g. git checkout <first-commit> -- ios/Runner.xcodeproj/project.pbxproj, then re-run below. Flutter re-adds its SPM package reference automatically on the next build; pod install re-adds the CocoaPods phases.
- Run (from the repo root):
python3 ios/scripts/setupiosflavors.py
For each {Debug,Release,Profile}-{dev,stg,prd} it adds a build configuration to the Project / Runner / RunnerTests lists, sets the Runner config's PRODUCTBUNDLEIDENTIFIER to <base id><APPIDSUFFIX> (read from secureFiles/<flavor>/environment-configs.json), points its base xcconfig at Flutter/<Mode>-<flavor>.xcconfig, and adds a "Copy GoogleService-Info.plist" build phase after Resources. It also (re)writes Flutter/<Mode>-<flavor>.xcconfig with the Pods include, Generated.xcconfig, and DARTDEFINESAPP_NAME from the JSON. Safe to re-run. Validate: plutil -lint ios/Runner.xcodeproj/project.pbxproj.
ios/Flutter/Define-defaults.xcconfig: create from the resourceApp-defaults.xcconfig
(fallback DARTDEFINESAPP_NAME for the plain, no-flavor Debug/Release/Profile configs). Debug.xcconfig / Release.xcconfig should #include it.
ios/Runner/Info.plist:
- CFBundleDisplayName = $(DARTDEFINESAPPNAME) - CFBundleIdentifier = $(PRODUCTBUNDLEIDENTIFIER) (the full per-flavor id is set in the build configs - do not append $(DARTDEFINESAPPID_SUFFIX), since an empty suffix, e.g. prd, is dropped by xcodebuild -showBuildSettings).
- Schemes
xcshareddata/xcschemes/{dev,stg,prd}.xcscheme: each must reference
Debug-<flavor> / Release-<flavor> / Profile-<flavor> and carry the standard Flutter <PreActions> "Run Prepare Flutter Framework Script" block. Keep Runner.xcscheme as a no-flavor fallback.
ios/Podfile: map every new configuration in theproject 'Runner', { ... }hash
('Debug-dev' => :debug, 'Release-stg' => :release, ...) and set platform :ios, '13.0'.
ios/.gitignore: ignore onlyFlutter/Generated.xcconfigand
Flutter/flutterexportenvironment.sh - the <Mode>-<flavor>.xcconfig files are committed source. Keep Podfile / Podfile.lock tracked (CocoaPods is still the SPM fallback for plugins without a Package.swift).
- GoogleService-Info.plist: run the
@copysecureconfigurationsskill first so
ios/Runner/Firebase/GoogleService-Info.<flavor>.plist exist. The build phase added in step 3 copies the right one into the bundle per configuration.
- Verify:
flutter clean && flutter pub get && (cd ios && pod install), then for each
flavor flutter build ios --flavor <f> --dart-define-from-file=secureFiles/<f>/environment-configs.json --no-codesign and assert CFBundleDisplayName / CFBundleIdentifier / bundled GoogleService-Info.plist in build/ios/iphoneos/Runner.app. Switch flavors without flutter clean between to confirm no stale carry-over.
3. VS Code Configuration
- Create
launch.json:
- Create .vscode/launch.json using the template from resources.
4. Verification
Run the following commands to verify the setup for each environment. These match the configurations in .vscode/launch.json.
Dev Environment:
flutter run --flavor dev --dart-define-from-file=secureFiles/dev/environment-configs.json
Staging Environment:
flutter run --flavor stg --dart-define-from-file=secureFiles/stg/environment-configs.json
Production Environment:
flutter run --flavor prd --dart-define-from-file=secureFiles/prd/environment-configs.json