Flavors with Flutter
Building apps that support multiple environments
The necessity to run our application on multiple environments is a usual problem. Build a dev environment to not pollute the production database or a free version to attract users' attention.
What is a Flavor?
Quick answer: It is a method to configure environment settings! =)
Like a .env
file, but more powerful. Here is the official definition:
Flavors (known as build configurations in iOS), allow you (the developer) to create separate environments for your app using the same code base — docs.flutter.dev
Flavor is an Android concept that Flutter inherits. This same idea of environment settings is known in iOS as build configuration.
We can do more than replace the .env file with its environment variables. Flavors are more flexible, you can manage any build settings for a specific environment.
How to create a Flavor?
The bad news is that this configuration is made natively, although the good news is that they are pretty simple.
Let’s create an app with two build versions: demo and full.
Configuration
For learning’s sake, we’ll show only the build for Android. Before you know HOW to use it, focus on WHY.
Add the following code to the android/app/build.gradle
file.
android {
// ...
flavorDimensions "deafult"
productFlavors {
demo {
dimension "deafult"
}
full {
dimension "deafult"
}
}
}
Thats it!
Properties
You should’ve noticed the dimension property, but also thinking about other properties that can be used to make our lives easier.
Dimensions
Our first Flavor version has just one variable: dimension.
Using the same dimension on both will force us to choose between one of our flavors to build.
Flavors within the same dimension are exclusive, meaning you can’t use both settings in the same build.
android {
// ...
flavorDimensions "version", "env"
productFlavors {
demo {
dimension "version"
}
full {
dimension "version"
}
dev {
dimension "env"
}
production {
dimension "env"
}
}
}
With this configuration when you build the following build variants:
- [demo|full]-[dev|production]-[debug|release].aab
The debug and release are the default BuildTypes. As a result, there are a total of 8 possibilities to generate a build:
- demo-dev-debug.aab
- demo-dev-release.aab
- demo-production-debug.aab
- demo-production-release.aab
- full-dev-debug.aab
- full-dev-release.aab
- full-production-debug.aab
- full-production-release.aab
ResValue
To set different values for each Flavor we can do this with resValue parameter.
In the below example, we set distinct app names for each flavor.
android {
// ...
flavorDimensions "default"
productFlavors {
dev {
dimension "default"
resValue "string", "app_name", "App Name Dev"
}
production {
dimension "default"
resValue "string", "app_name", "App Name"
}
}
}
And query it on the Manifest file.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.appname">
<application
android:label="@string/app_name"
</application>
</manifest>
For multiple variables, we can use the resValue more than once.
android {
// ...
flavorDimensions "default"
productFlavors {
dev {
dimension "default"
resValue "string", "app_name", "App Name Dev"
resValue "string", "main_color", "#30FF21"
}
production {
dimension "default"
resValue "string", "app_name", "App Name"
resValue "string", "main_color", "#FF3CA6"
}
}
}
ApplicationId and ApplicationIdSuffix
Sometimes we want to have installed all flavors simultaneously. For Android and iOS differ the app by flavor we have to change the applicationId of the app.
We can set it with the applicationId property.
android {
// ...
flavorDimensions "default"
productFlavors {
dev {
dimension "default"
applicationId "com.example.company.dev"
}
production {
dimension "default"
applicationId "com.example.company"
}
}
}
or just customize changing the suffix term with ApplicationIdSuffix.
android {
// ...
flavorDimensions "default"
productFlavors {
dev {
dimension "default"
applicationIdSuffix ".dev"
}
production {
dimension "default"
}
}
}
Note: when changing the ApplicationId by flavors, the external services that use it as a reference, like firebase, will have to be duplicated for each flavor.
VersionNameSuffix
Also, the version name can be customized.
android {
// ...
flavorDimensions "default"
productFlavors {
dev {
dimension "default"
versionNameSuffix "-dev"
}
production {
dimension "default"
}
}
}
In this case, if you set as your app version the value “1.2.0” in the pubspec file, the result of the dev build will be “1.2.0-dev”.
Running the project
Since we set our flavor as it needed to be, we have to choose a specific flavor when running our code, we can do this by the following command:
flutter run --flavor <flavorName>
A great thing about flavor is that it does not add complexity to our development flow since we can automate the flavors in our favorite IDE (not really, just VsCode and AndroidStudio).
VS Code
In the root of the Flutter project create a file .vscode/launch.json
, this file allows you to config some parameters to run your app through the IDE.
{
"version": "1.0.0",
"configurations": [
{
"name": "development",
"request": "launch",
"type": "dart",
"program": "lib/main/main_dev.dart",
"args": ["--flavor", "dev"]
},
{
"name": "production",
"request": "launch",
"type": "dart",
"program": "lib/main/main_prd.dart",
"args": ["--flavor", "prd"]
}
],
"compounds": []
}
You will see the name of the option you created in the “Run & Debug” tab
Android Studio
To set our Flavor on Android Studio, follow these steps:
- Click on the dropdown main.dart button (on left side of play button)
- Select the Edit Configurations option
- Select the “+” symbol to add a new configuration.
- Select Flutter to create a new Flutter Run Configuration
- Name it and add the name of your flavor at “Build Flavor”
You’ve done it!
Conclusion
This Knowledge can help us to prepare our build to run smoothly on multiple environments.
Also, It is very convenient how we can easily configure our Flavors on the IDEs we use.
All these configurations are accepted by the iOS building tools, look here for more information.
Thanks for reading! If you want to read more of my thoughts and programming tips, then check out my articles and follow or subscribe for upcoming content.