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?

Dwi Randy Herdinanto
8 min readJan 11, 2023

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 copyinto 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
App with prod flavor
App with dev flavor

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

--

--

Dwi Randy Herdinanto

A software developer that enthusiastic about mobile applications