When developing apps, it’s very important to separate your development and production environments. App flavors allow us to create multiple versions of our app with the same codebase, making it easy to create and test new features without the risk of destroying production data.
You’ll learn how to create three Android and iOS flavors:
development
- Build and test new featuresstaging
- Test out the app in a production-like environmentproduction
- For your users
main.dart
files
Multiple We first have to create three entry points, one for each flavor: main_development.dart
, main_staging.dart
, and main_production.dart
.
To run a specific main file, we can use $ flutter run
.
E.g. $ flutter run --target lib/main_development.dart
Once we add our Android and iOS flavors configurations, we’ll also have to include the flavor we want to run with --flavor <flavor_name>
E.g. $ flutter run --flavor development --target lib/main_development.dart
However, there are two problems with this:
- It’s annoying to type this out whenever we need to run a different app build
- Running this command in the terminal won’t allow us to utilize VSCode’s debugger
Creating launch configurations in VSCode
Luckily, both of these are easily fixable with a launch.json
file. At the root of our project, create a launch.json
file inside a folder called .vscode
.
We have three configurations, one for each flavor. Each configuration has a name, request, type, program, and arguments.
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch development",
"request": "launch",
"type": "dart",
"program": "lib/main_development.dart",
"args": [
"--flavor",
"development",
"--target",
"lib/main_development.dart"
]
},
{
"name": "Launch staging",
"request": "launch",
"type": "dart",
"program": "lib/main_staging.dart",
"args": ["--flavor", "staging", "--target", "lib/main_staging.dart"]
},
{
"name": "Launch production",
"request": "launch",
"type": "dart",
"program": "lib/main_production.dart",
"args": ["--flavor", "production", "--target", "lib/main_production.dart"]
}
]
}
Now when we go to the Run
tab, we see our three launch configurations.
When we tap run, we get an error about not being able to use the --flavor
option, so let’s add our build flavors next.
Exception: The Xcode project does not define custom schemes. You cannot use the --flavor option.
Exited (sigterm)
Android Flavor Setup
Adding build flavors to Android is pretty straightforward. Inside of our app/build.gradle
, we add our product flavors for production, staging, and development. The name of the app for each build is defined in the resValue
line and our applicationId
has a suffix for staging and development.
// android/app/build.gradle
flavorDimensions "default"
productFlavors {
prod {
dimension "default"
resValue "string", "app_name", "Flavor Example"
applicationIdSuffix ""
}
stg {
dimension "default"
resValue "string", "app_name", "Stg Flavor Example"
applicationIdSuffix ".stg"
}
dev {
dimension "default"
resValue "string", "app_name", "Dev Flavor Example"
applicationIdSuffix ".dev"
}
}
Our current applicationId
is com.example.flavorsExample
.
This means our bundle identifiers will be:
com.example.flavorsExample
- productioncom.example.flavorsExample.stg
- stagingcom.example.flavorsExample.dev
- development
In our android/app/src/main/AndroidManifest.xml
, we set the android:label="@string/app_name"
to set our app’s app name.
<!-- android/app/src/main/AndroidManifest.xml-->
<application
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher">
...
</application>
iOS Flavor Setup
iOS build configurations are more complicated than Android and can only be done in Xcode, so be sure to follow along closely. In Xcode, let’s create two new schemes called development
and staging
that both target Runner
. Rename the original Runner to production
.
Next, we have to go into the Runner
project and duplicate the Debug
, Release
, and Profile
configurations for development and staging. Add the -production
suffix to the original configuration.
Now let’s go into Manage Schemes
and edit the development and staging schemes to point to the correct build configurations. The production scheme already points to our production configurations, which means we don’t have to change them.
In the Build Settings
of Target Runner
, make sure All
and Combined
are selected, and then search for bundle identifier
. Here we set each configuration’s bundle identifier.
Then set our app’s display name by creating a User-Defined variable
called APP_DISPLAY_NAME
. Note that if your app display name is longer than 12 characters, the spaces will be removed.
We use this variable in our Info.plist
by creating a new key CFBundleDisplayName
and setting it to APP_DISPLAY_NAME
wrapped in parentheses with a dollar sign.
Finally, set the FLUTTER_TARGET
in the Build Settings
to target each main.dart
entry point based on the selected flavor.
Generate app icons with the flutter_launcher_icons package
Drag your icons into assets/icons
. My icons are 1024x1024.
In our pubspec.yaml
, add the flutter_launcher_icons package as a dev dependency. This package will generate all of the icon sizes for Android and iOS and put them into the correct folders.
We have to create a configuration file for each of our flavors. Each file is named flutter_launcher_icons-
the flavor name .yaml
and points to the flavor icon in the assets/icons/
folder.
Set android
and ios
to true
to override the existing Flutter launcher icon for both platforms and define the icon image path.
When you submit your app to the iOS app store, make sure to remove the alpha channel from your app icons, so your app doesn’t get rejected. We can add remove_alpha_ios: true
to the yaml
files.
# flutter_launcher_icons-development.yaml
flutter_icons:
android: true
ios: true
image_path: 'assets/icons/development-icon.png'
remove_alpha_ios: true
# flutter_launcher_icons-staging.yaml
flutter_icons:
android: true
ios: true
image_path: 'assets/icons/staging-icon.png'
remove_alpha_ios: true
# flutter_launcher_icons-production.yaml
flutter_icons:
android: true
ios: true
image_path: 'assets/icons/production-icon.png'
remove_alpha_ios: true
Generate the icons with $ flutter pub run flutter_launcher_icons:main -f flutter_launcher_icons*
.
At this point, Android is all good to go as all the files were generated properly. For iOS, we see that our three icons were generated in the Assets.xcassets
folder.
Let’s go into the Target’s Build Settings and search for primary app icon
. Here we set the correct app icon for each schema.
Wrap Up
I’ve run all three flavors on Android and iOS, and all three apps have the correct name, app icon, and app or bundle identifier.
Every app’s AppBar
displays the flavor string we passed into each main file.
And that’s it! You just learned how to quickly set up flavors in your Flutter projects and generate a unique app icon for each flavor.