Flutter Flavors: A Complete Guide to Multiple Environments on Android and iOS
FlutterPulseThis article was translated specially for the channel FlutterPulseYou'll find lots of interesting things related to Flutter on this channel. Don't hesitate to subscribe!🚀

Set up Flutter flavors for dev, staging, and prod with custom icons, names, and configs — all from a single codebase.
Flavors are native configurations that allow you to generate multiple versions of the same app using a single codebase — each with its own icons, names, configurations, and behaviors.
The main goal is to isolate different environments — development (dev), staging (stg), and production (prod) — making testing, validation, and deployment more independent and organized.
With flavors, you can install and test multiple variations of the app on the same device, distribute them via app stores or testing platforms like Firebase Distribution, and have them coexist without overwriting each other.
In this article, I'll walk you through the step-by-step setup of dev, stg, and prod flavors in a Flutter app for both Android and iOS.
Table of Contents
Flavors on AndroidFlavors on iOSDisplaying a Flavor Banner in the AppConfiguring VSCode
Flavors on Android
1. Configure build.gradle
In your Flutter project, open the file android/app/build.gradle and inside the android block, add the flavor configuration:
android {
...
flavorDimensions "env"
productFlavors {
stg {
dimension "env"
applicationIdSuffix ".stg"
versionNameSuffix "-stg"
resValue "string", "app_name", "App STG"
}
dev {
dimension "env"
applicationIdSuffix ".dev"
versionNameSuffix "-dev"
resValue "string", "app_name", "App DEV"
}
prod {
dimension "env"
resValue "string", "app_name", "App"
}
}
}applicationIdSuffix: A suffix added to the app's identifier. An app with the IDcom.example.appcompiled with thedevflavor becomescom.example.app.dev.resValue: Assigns a distinct app name for each flavor.
Replace App, App DEV, and App STG with the appropriate name for each flavor of your app.
2. Edit AndroidManifest
Open the file android/app/src/main/AndroidManifest.xml and update the android:label property:
...
<application
android:label="@string/app_name"
Done! Your app now has flavors configured and you can test it by running:
flutter run --flavor dev
3. Create App Icons
Let's now generate different icons for each flavor.
In Android Studio, open the android directory.
Switch the view mode to Project.

Inside app/src, create the following directories: dev/res and stg/res.

Right-click the res folder you just created and go to New > Image Asset.

Import the icon, adjust its size, and save.

Repeat this process for each flavor. Keep in mind that icons for the prod flavor remain under the default main/res/ folder.
Now, when running the app on an Android device, each flavor will display its own icon.

Flavors on iOS
- Create Schemes
Open the file ios/Runner.xcworkspace using Xcode.
In the top menu, navigate to: Product -> Scheme -> Manage Schemes.

Click the + button and create the schemes dev and stg:

Rename the existing Runner scheme to prod.

Now, in the left panel, click on Runner, and under PROJECT, select Runner again. Go to the Info tab and click the + button to add a new build configuration.

You'll need to duplicate the existing Debug, Release, and Profile configurations, creating one set for each flavor.

Rename them according to each flavor, for example: Debug-prod, Release-dev, Profile-stg. In the end the result will be like this:

Next, go back to Product -> Scheme -> Manage Schemes, select a scheme and click Edit.

For each flavor scheme, select the appropriate build configurations in the left sidebar.
- For
prod, select configs ending in-prod - For
dev, use those ending in-dev - For
stg, the ones ending in-stg

Repeat this for all schemes.
2. Edit bundle.id
Click on Runner under TARGETS, then go to Build Settings. Search for "bundle" to find the Packaging section.
Add the corresponding suffix to the Bundle Identifier for each flavor:
.devfor development.stgfor staging- Leave it unchanged for production

After this, each flavor will have a unique identifier, like this:

3. Change App Display Name
Still inside TARGETS → Runner, go to the Info tab. Change the Bundle display name value to: $(APP_DISPLAY_NAME)

Now go to Build Settings, click the + and select Add User-Defined Setting.

Define one value for each flavor. The final setup should look something like this:

You can now run the app with: flutter run --flavor dev.
4. Create App Icons
On iOS, app icons require multiple sizes. Generating them manually is time-consuming, so you can use the AppIcon platform to simplify the process.
- Upload your icon
- Choose iPhone (and iPad, if needed)
- Download the generated zip
Inside the downloaded folder, copy AppIcon.appiconset from AppIcons/Assets.xcassets.
Paste it into your project at: ios/Runner/Assets.xcassets/

Rename the folder according to each flavor:
AppIcon-dev.appiconsetAppIcon-stg.appiconset- Keep the default name for
prod

Then go back to Xcode. Under TARGETS → Runner → Build Settings, search for "primary". You'll find the Primary App Icon Set Name setting.
Set the name for each flavor (e.g., AppIcon-dev). Keep the default for prod.

The final result will be like this:

Now, when you run the app on an iOS simulator or device, each flavor will show its own icon.

Displaying a Flavor Banner in the App
We'll now show a banner inside the app indicating whether it's running under the dev or stg flavor.
At this point, you have two options:
- Use a single
main.dartfile, or - Use separate entry points (
main_dev.dart,main_stg.dart, etc.)
We'll go through both approaches.
- Single
main.dartFile
In this approach, we pass the flavor to the app using the --dart-define parameter, like this:
flutter run --flavor dev --dart-define=FLAVOR=dev
Here's how main.dart will look:
void main() {
const flavor = String.fromEnvironment('FLAVOR', defaultValue: 'prod');
runApp(
const App(flavor: flavor),
);
}Now we can display the banner based on the current flavor:
class App extends StatelessWidget {
const App({
super.key,
required this.flavor,
});
final String flavor;
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
debugShowCheckedModeBanner: false,
home: flavor != 'prod'
? Banner(
message: flavor.toUpperCase(),
location: BannerLocation.topStart,
color: Color(0xFF2D808D),
child: const HomePage(),
)
: const HomePage(),
);
}
}Here, we apply the logic: if the flavor is not prod, the banner will be shown.
2. Multiple main.dart Files
In this approach, we keep main.dart as the entry point for prod and create separate files for the other flavors, like main_dev.dart and main_stg.dart.
Each file hardcodes the corresponding flavor:
void main() {
runApp(
const App(flavor: 'dev'),
);
}To run the app, use:
flutter run --flavor dev -t lib/main_dev.dart.

Configuring VSCode
To debug your app using VSCode with different flavors, you'll need to configure the launch.json file properly.
The setup depends on whether you're using a single main.dart file or separate entry points for each flavor.
In your project, open the file: .vscode/launch.json.
If you're using a single main.dart:
{
"version": "0.2.0",
"configurations": [
{
"name": "App",
"request": "launch",
"type": "dart",
"args": [
"--flavor",
"prod",
]
},
{
"name": "App Dev",
"request": "launch",
"type": "dart",
"args": [
"--flavor",
"dev",
"--dart-define",
"FLAVOR=dev"
]
},
{
"name": "App Stg",
"request": "launch",
"type": "dart",
"args": [
"--flavor",
"stg",
"--dart-define"
"FLAVOR=stg"
]
}
]
}Or if you're using multiple main.dart files:
{
"version": "0.2.0",
"configurations": [
{
"name": "App",
"request": "launch",
"type": "dart",
"args": [
"--flavor",
"prod",
]
},
{
"name": "App Dev",
"request": "launch",
"type": "dart",
"args": [
"--flavor",
"dev",
"-t",
"lib/main_dev.dart",
]
},
{
"name": "App Stg",
"request": "launch",
"type": "dart",
"args": [
"--flavor",
"stg",
"-t",
"lib/main_stg.dart",
]
}
]
}We've now completed the full setup for Flutter flavors — a powerful feature that allows you to isolate different environments, making testing, validation, and deployment more organized and efficient.
Although it's a native platform feature, flavors work seamlessly with Flutter when properly configured, enabling hybrid apps to take full advantage of this capability with custom icons, names, behaviors, and environment separation.