Create Build Flavor in Flutter Application (iOS & Android)
Have you ever wondered how to set up different environments in your Flutter app? such as different api url, api key & icon for development and production?
From this article we will learn step by step how to different environment application (iOS & Android) using the same source code.
Outline
- What is Build Flavor
- Difference between Build Flavor & Build Mode
- Define Environment
- Setup Fluter App
- Setup Build Flavor in iOS
- Setup Build Flavor in Android
- Run App Based on Build Flavor
What is Build Flavor
When developing a Flutter app, you may want to share it with internal testers in a development environment, using a version of the app with a backend configured for development rather than production. This version of the app is called the dev variant.
When you’re ready to release the app publicly and have the production backend set up, you’ll need another version of the app, called the prod flavor, which is connected to the production backend.
Flavors (known as build configurations in iOS), allow you (the developer) to create separate environments for your app using the same code base. You can use flavors to set up both app versions without writing two separate apps.
Difference between Build Flavor, Build Mode & Build Variant
Build Flavor
Build Flavor is about how to create separate environments for your app using the same code base.
Build Mode
Build Mode is a compilation mode that compiles the source code in different modes
- Debug mode during development, when you want to use hot reload.
- Profile mode when you want to analyze performance.
- Release mode when you are ready to release your app.
Build variant
Build variant is a combination of both build mode and build flavor, which allows us to customize the version of the application we will build in a single project. As such, build variant can represent the differences in version of the application we will build in a single project. We can illustrate it in the form of the following table.
Define Environment
Before we jump into the implementation part in flutter, it would be better if we first define the environment we want to create. we will create two different environment which are dev and prod.
In the table below is an example of the environment that we will prepare where between dev and prod will have different environments according to needs.
Setup Flutter App
Step 1. Create AppConfig File
Create a class AppConfig
this singleton class is useful for storing flavor based configurations
import 'package:flutter/material.dart';
enum Flavor { prod, dev }
class AppConfig {
String appName = "";
String baseUrl = "";
MaterialColor primaryColor = Colors.blue;
Flavor flavor = Flavor.dev;
static AppConfig shared = AppConfig.create();
factory AppConfig.create({
String appName = "",
String baseUrl = "",
MaterialColor primaryColor = Colors.blue,
Flavor flavor = Flavor.dev,
}) {
return shared = AppConfig(appName, baseUrl, primaryColor, flavor);
}
AppConfig(this.appName, this.baseUrl, this.primaryColor, this.flavor);
}
Step 2. Create Entry Point Each Flavor
Create a file lib/main_prod.dart to run app in prod flavor. This is where you assign the flavor type and specific name for App based on that flavor.
Entry point for Prod
Make or edit a file named lib/main.dart
to execute the app in production mode. In this file, you will define the flavor type and give the app a specific name, base url, primary color etc for production environment.
import 'package:flutter/material.dart';
import 'package:medium_build_flavor/app_config.dart';
import 'package:medium_build_flavor/home_page.dart';
void main() async {
AppConfig.create(
appName: "Prod Flavor Example",
baseUrl: "https://dwirandyh.com",
primaryColor: Colors.yellow,
flavor: Flavor.prod,
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo Production',
theme: ThemeData(
primarySwatch: AppConfig.shared.primaryColor,
),
home: const HomePage(),
);
}
}
Entry Point for Dev
Make a new file named lib/main_prod.dart
to execute the app in development mode. In this file, you will define the flavor type and give the app a specific name, base url, primary color etc for production environment.
import 'package:flutter/material.dart';
import 'package:medium_build_flavor/app_config.dart';
import 'package:medium_build_flavor/home_page.dart';
void main() async {
AppConfig.create(
appName: "Dev Flavor Example",
baseUrl: "https://dev.dwirandyh.com",
primaryColor: Colors.blue,
flavor: Flavor.prod,
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo Production',
theme: ThemeData(
primarySwatch: AppConfig.shared.primaryColor,
),
home: const HomePage(),
);
}
}
Step 3. Home Page
This home page view is used to show the configuration based on app config that we have defined from entry point file
import 'package:flutter/material.dart';
import 'package:medium_build_flavor/app_config.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(AppConfig.shared.appName),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("App Name: ${AppConfig.shared.appName}"),
Text("Base URL: ${AppConfig.shared.baseUrl}"),
Text("Flavor: ${AppConfig.shared.flavor}"),
],
),
),
);
}
}
Setup Build Flavor for iOS
Step 1. Duplicate Target
Open ios/Runner.xcworkspace
duplicate Runner
target. That will create 2 different targets, the target we just created dev
will be used as a development target while the Runner (default target) we rename it into prod
it will be a production target.
Step 2 Rename Scheme
We need to rename schema that generated when we duplicate our target. Rename Runner copy
into dev
this schema is used as identifier when we want run flutter command with flavor dev
Don’t for get to rename schema Runner
into prod
as well so we will have 2 scheme prod
and also dev
We have to make sure that scheme name and target name is same, for example if our scheme name is prod so the target must be prod as well
At this point, if we run flutter run -t lib/main_dev.dart --flavor dev
in CLI, we will see information to complete the custom scheme. Finish the custom scheme set up as directed.
The Xcode project defines build configurations: Debug, Release, Profile
Flutter expects a build configuration named Debug-dev or similar.
Open Xcode to fix the problem:
open ios/Runner.xcworkspace
1. Click on "Runner" in the project navigator.
2. Ensure the Runner PROJECT is selected, not the Runner TARGET.
3. Click the Editor->Add Configuration->Duplicate "Debug" Configuration.
If this option is disabled, it is likely you have the target selected instead
of the project; see:
https://stackoverflow.com/questions/19842746/adding-a-build-configuration-in-
xcode
If you have created a completely custom set of build configurations,
you can set the FLUTTER_BUILD_MODE=debug
in the .xcconfig file for that configuration and run from Xcode.
4. If you are not using completely custom build configurations, name the newly
created configuration debug.
Could not build the application for the simulator.
Error launching application on iPhone 14 Pro.
Step 3. Add Build Mode Configuration for Development
We need to define build configuration for every build mode, So that we can duplicata each configuration (Debug, Release & Profile) by click + symbol and don’t forget to add suffix -dev
We also need to rename default configuration name and add suffix -prod
Step 4. Change App Id for Development Environment
App Id is a unique identifier used to identify an iOS app & android app. It is a string that is assigned by the app developer, and is used by the operating system to identify the app when it is installed on a device.
We can add suffix .dev
as unique identifier for development and keep as is for production
Step 5. Change App Icon for Development
We are also able to change our app icon every environment, for this case we will change app icon for development environment so we will have different app icon for production & development application.
To change app icon in development environment we can duplicate AppIcon
inside Assets.xcassets
and name is as AppIconDev
After that we need change RunnerDev Build Settings
-> Primary App Icon Set Name
into AppIconDev
To see the changes in our application, we can run the flutter run lib/main_dev.dart --flavor dev
command again and you can see that the app icon for development has changed.
Setup Build Flavor in Android
Step 1. Add Flavor Configuration
The first step to create build flavor in android is we need to add some configuration in app/build.gradle
android {
defaultConfig {
...
}
...
flavorDimensions "default"
productFlavors {
prod {
dimension "default"
resValue "string", "app_name", "Flutter Demo Prod"
}
dev {
dimension "default"
applicationIdSuffix ".dev"
resValue "string", "app_name", "Flutter Demo Dev"
versionNameSuffix ".dev"
}
}
}
Step 2. Change Icon for Development
To change app icon for development application, we need to create a directory inside app/src/dev
and put all app icon resources into that folder. so that when we run development flavor it will use ic_launcher
from dev/res
directory
Run App Based On Flavor
To run our app now we must use the following commands:
Running each flavor on DEBUG mode:
flutter run -t lib/main.dart --flavor prod
flutter run -t lib/main_dev.dart --flavor dev
Running each flavor on PROFILE mode:
flutter run --profile -t lib/main.dart --flavor prod
flutter run --profile -t lib/main_dev.dart --flavor dev
Running each flavor on RELEASE mode:
flutter run --release -t lib/main.dart --flavor prod
flutter run --release -t lib/main_dev.dart --flavor dev
Sometimes between changing flavors is necessary a flutter clean
to clean our app build files.
If you want to see full project, you can download full project in my repository below