When we are developing a large scale application inside a company with a team, the development phase goes around a well-defined process where the new feature goes through different stages, such as feature implementation by the development team (both front-end & back-end) and testing the feature by QA team to ensure the app is working flawlessly when it reaches to end-users. In such cases, there might be different back-end servers hosted with different URL’s which need to be implemented in the mobile app too to implement the new changes. If your app doesn’t contain a separate build configuration you need to manually change the server URL and build your app accordingly each time you debug or prepare for release.
In such scenarios, separating build environments with different build configurations can save you and your time providing an easy way to debug or prepare for release. So, in this article, we will learn how we can separate build environments in Flutter Apps using Flutter Flavor. It is commonly known as Flavors
in Android
and Schemes
in iOS
.
Getting started with Flutter Flavor
Before creating a Flavors for Flutter, let’s create a flutter project first. I have created a project with flavours_flutter
. I will be adding three different build configurations i.e. dev
, stage
, and prod
representing the development
, staging
and production
environment.
App Configuration
Let us create a new file under our lib
folder as flavour_config.dart
which will hold the details about which flavor we are currently running.
Inside flavour_config.dart
the file, we will create a enum
name Environment
to define the build configuration type, a set method to set the configuration and get method which will let us know which build configuration is being currently used. You can refer to the snippet
below :
enum Environment {
dev,
stage,
prod,
}
class Constants {
static late Map<String, dynamic> _config;
static void setEnvironment(Environment env) {
switch (env) {
case Environment.dev:
_config = _Config.debugConstants;
break;
case Environment.stage:
_config = _Config.stageConstants;
break;
case Environment.prod:
_config = _Config.prodConstants;
break;
}
}
static String get whereAmI {
return _config[_Config.flavour] as String;
}
}
class _Config {
static const flavour = 'flavour';
static Map<String, dynamic> debugConstants = {
flavour: 'dev',
};
static Map<String, dynamic> stageConstants = {
flavour: 'stage',
};
static Map<String, dynamic> prodConstants = {
flavour: 'prod',
};
}
Now, let’s create three different files named as main_dev.dart
, main_stage.dart
and main_prod.dart
which will contain our main
method for individual build configuration.
Inside main_dev.dart
the file we will use the set method defined under flavour_config.dart
file to set the current build configuration to begin using. Refer to the snippet below :
void main() async {
Constants.setEnvironment(Environment.dev);
await initializeApp();
}
Similarly, under main_stage.dart
and main_prod.dart
file pass the parameter to setEnvironment
the method as Environment. stage and Environment.prod
respectively.
At last, we can use Constants.whereAmI
method to find out which build configuration is our app using currently. I have used it to get the current configuration and an extension method that will return color and name based on flavor. You can find the code snippet below :
extension FlavourTypeExtension on String {
Color getFlavourColor() {
switch (this) {
case 'dev':
return Colors.yellow[800]!;
case 'stage':
return Colors.grey[600]!;
case 'prod':
return Colors.green[600]!;
default:
return Colors.blue[600]!;
}
}
String getFlavourName() {
switch (this) {
case 'dev':
return 'Development';
case 'stage':
return 'Staging';
case 'prod':
return 'Production';
default:
return 'Unknown';
}
}
}
Refer to the picture below about using it :
Generating App Icons :
Usually, we will use different app names to differentiate the variants of the app based on the build configuration, but in this article, we will also learn how to different app icons to make it more clear. There is an awesome tool that helps us to generate launcher icons for our apps easily and quickly, you can check to visit this site to access the tool. It has been developed by Roman Nurik
. I have generated three different app icons for dev
, stage
and production
. After you generate and download your desired icon you will get a folder containing the assets for iOS
and android
as shown below :
NOTE :
I have created the launcher icon formacos
desktop app
andweb
app
, you can skip this option.
Android Flavors :
Now let’s continue with configurations of environment variants (a.k.a Flavors) on the Android platform.
In your project go to android > app > src
the folder and create three folders named as dev
, stage
and prod
.
Now, simply navigate to the folder where you have kept the downloaded launcher icon asset then from the android folder simply move the res folder to the respective folder you have just created. For instance, in my case I have kept the res folder containing my asset for dev configuration under the dev folder and so on.
Then under dev
, stage
and prod
folder create another folder named as values
and a file inside that folder named as strings.xml
which will hold the app label or name
for different build configurations.
For reference you can take a look at the picture attached below :
After that add the following line of code inside the strings.xml
file :
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">{Your App Name}</string>
</resources>
Replace {Your App Name}
with your desired name for your app for individual flavor. For example, I have kept [DEV] App
, [STAGE] App
and [PROD] App
for dev
, stage
and prod
respectively.
Now, let’s navigate to AndroidManifest.xml
file, and under that let’s replace the value of android:label
with @string/app_name
. Changing this value will allow us to pick the individual app label or name that we have defiled under strings.xml
the file for individual flavors.
Lastly, let’s define the flavor dimensions under the app level build.gradle
file. For this go to android>app>build.gradle
file, add the following code below the declaration of buildTypes
:
flavorDimensions "default"
productFlavors {
dev {
applicationIdSuffix ".dev"
dimension "default"
}
stage {
applicationIdSuffix ".stage"
dimension "default"
}
prod {
dimension "default"
}
}
And thatβs all you need to do for setting up flavors in android. Now you can use the command below to build your app :
For building dev
environment :
> flutter run --flavor dev -t lib/main_dev.dart
For building stage
environment :
> flutter run --flavor stage -t lib/main_stage.dart
For building prod
environment :
> flutter run --flavor prod -t lib/main_prod.dart
Here you can see three different apps with different names and icons installed on my device.
iOS Schemes :
Creating schemes for iOS is a bit tricky and confusing task, but in this article, we will learn how we can create different schemes for dev
, stage
and prod
in an easy way.
Let’s get started, first open your project in Xcode
and select the target Runner
the file then creates three different schemes as shown below :
After that now select the project Runner
file and you can now see the configuration option under the info tab, refer to the picture below :
There are three different pre-defined configuration available as Debug
, Release
, and Profile
. Now what we need to do is click the +
button just below Profile
the option then you get a popup dialogue as :
Now, rename Debug copy
, Release copy
and Profile copy
as Debug-dev
, Release-dev
and Profile-dev
. Depending up-to how many build configurations you are setting up you need to repeat the same method. In my case, I have been setting up for dev
, stage
and prod
, the final result was like this:
As we have duplicated the configurations now, we have to make sure that our individual schemes are connected with their respective configuration. You can take reference from below :
Above I have shown how I have done the configuration for the prod environment, similarly according to your types of build configuration you can copy those steps. For example for dev
select release-dev
, debug-dev
and profile dev
.
Finally, here we have our schemes connected with their respective configuration, now let’s continue with other customization for schemes. At first, let us change the app-bundle identifier. I will be keeping com.example.flavoursFlutter.dev
, com.example.flavoursFlutter.stage
and com.example.flavoursFlutter.prod
for dev
, stage
and prod
respectively. I will be showing the process for dev
and you can copy the same steps for stage
and prod
. For the process look at the attached video below :
After that’s done we would like our app to display different names based on scheme, for that let first open the info.plist
file and search for CFBundleName
and replace the value for that key with $(APP_DISPLAY_NAME)
. I have attached a code snippet to show what has changed in my case please refer below :
// Previous data
<key>CFBundleName</key>
<string>flavours_flutter</string>
// Updated data
<key>CFBundleDisplayName</key>
<string>$(APP_DISPLAY_NAME)</string>
Now let’s jump back to Xcode
and update the value for app name for which we need to add a user-defined setting as APP_DISPLAY_NAME
. I will be keeping my app name as [DEV] App
, [STAGE] App
and [PROD] App
for dev
, stage
and prod
respectively as similar to what we have done for android.
NOTE
Repeat the same steps forprofile
andrelease
too.
Now when you run your app you will be able to see a different app for dev, stage, and prod with a different name but we have one more task pending. As I have mentioned earlier and we have already added a separate launcher icon for android now it’s time for iOS. Let’s begin with that, for that make sure you have the generated app icon, if not please go to Generating App Icons section
and follow the mentioned steps. After that you will have a folder called iOS
inside the downloaded folder while generating the app icon which will look like :
Now let’s again jump back to Xcode
adding these assets. after selecting the Assets option, right-click and select the iOS option which will create AppIcon-1
, all you need to do is rename with a name for which scheme you are trying to add the launcher icon. For example, in my case, I have added for dev, stage, and prod so I have renamed it to AppIcon-dev
, AppIcon-stage
and AppIcon-prod
respectively.
Now it’s time to drag and drop respective assets for the launcher icon as shown below :
And now we are almost done, for the final step let’s set the configuration which will pick the right icon asset for the right schemes while building an app. For this process check the attached image and gif :
Lastly, now build your app with the same command you used for android. The final result will look like this:
Bonus Tips :
For those who are using visual studio code
it for debugging just copy and paste the following code snippet in your launch.json
file.
{
"version": "0.2.0",
"configurations": [
{
"name": "dev",
"request": "launch",
"type": "dart",
"args": [
"-t",
"lib/main_dev.dart",
"--flavor",
"dev"
]
},
{
"name": "stage",
"request": "launch",
"type": "dart",
"args": [
"-t",
"lib/main_stage.dart",
"--flavor",
"stage"
]
},
{
"name": "production",
"request": "launch",
"type": "dart",
"args": [
"-t",
"lib/main_prod.dart",
"--flavor",
"prod"
]
}
]
}
You can find the GitHub repository for this project here. I will be posting another article about integrating multiple firebase projects for separate build environments, until then keep reading awesome articles at Flutter Guide.
π§π»βπ»π§π»βπ»π§π»βπ»π§π»βπ»π§π»βπ» Thank You for reading this article, I hope you find it useful. π§π»βπ»π§π»βπ»π§π»βπ»π§π»βπ»π§π»βπ»
Also Read: Flutter Version Management: Easiest Way To Manage Multiple Flutter SDK Version Through Simple CLI