A Complete Guide to Implementing Flutter Flavors and Why They're Important

Gidudu NicholasGidudu Nicholas
3 min read

Flutter Flavors Explained: How to Implement and Why You Should

So, you’ve built your Flutter app, and everything is looking great. But then you realize:

  • You need a development version to test features.

  • A staging version for your QA team.

  • And a production version for real users.

How do you manage all these versions without going crazy?

That’s where Flavours in Flutter come in. Let’s break it down.


🌿 What are Flavours, Anyway?

Think of Flavours like different “toppings” on your app.
Same base app, but slightly different depending on what you need.

For example:

  • The dev app might use a test API, say “Dev Mode” in the title, and have a different icon.

  • The prod app connects to the real API and shows the proper logo.

  • The staging app is what your testers see.

With Flavours, you can:
✅ Have different configurations (API endpoints, app names, icons)
✅ Build specific versions of your app without manual tweaks
✅ Avoid the nightmare of deploying test builds to real users!


🎯 Why Bother with Flavours?

Good question!
Imagine you’re working on a new feature, but you don’t want to break the live app. Or you want your testers to check something before releasing it.

Flavours let you:

  • Build safe environments for testing

  • Separate production from development

  • Ship custom versions for different clients

And you don’t have to keep swapping files or commenting code in and out.


🛠️ How to Set Up Flavours (Step by Step)

Let’s get to the fun part—setting up Flavours in your Flutter project.


1️⃣ Create Entry Points for Each Flavour

Start by creating separate Dart files for each flavour:

// lib/main_dev.dart
import 'package:your_app/main.dart' as app;

void main() {
  const flavour = "Development";
  app.main(flavour);
}

// lib/main_prod.dart
import 'package:your_app/main.dart' as app;

void main() {
  const flavour = "Production";
  app.main(flavour);
}

Then update your main.dart:

// lib/main.dart
import 'package:flutter/material.dart';

void main([String flavour = "Production"]) {
  runApp(MyApp(flavour: flavour));
}

class MyApp extends StatelessWidget {
  final String flavour;

  const MyApp({super.key, required this.flavour});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MyApp ($flavour)',
      home: Scaffold(
        appBar: AppBar(title: Text('Hello from $flavour')),
        body: Center(child: Text('Running on $flavour')),
      ),
    );
  }
}

Now, when you run lib/main_dev.dart, it shows "Development"!


2️⃣ Set Up Android Flavours

In your android/app/build.gradle file:

android {
    ...
    flavorDimensions "app"
    productFlavors {
        dev {
            dimension "app"
            applicationIdSuffix ".dev"
            versionNameSuffix "-dev"
        }
        prod {
            dimension "app"
        }
    }
}

This lets Android know there are different "flavours" of your app.


3️⃣ Set Up iOS Flavours

iOS is a bit more manual:

  • In Xcode, duplicate the schemes:

    • RunnerRunner-dev

    • RunnerRunner-prod

  • Change the Bundle Identifier in each scheme:

    • com.yourcompany.app.dev

    • com.yourcompany.app

This keeps the apps separate when installed.


4️⃣ Run Your Flavours

Use Flutter’s -t flag:

flutter run -t lib/main_dev.dart
flutter run -t lib/main_prod.dart

Or build them:

flutter build apk -t lib/main_dev.dart
flutter build ipa -t lib/main_prod.dart

5️⃣ Handle Different Configurations

Need different API URLs or secrets?
Use a config.dart file or the flutter_dotenv package.

For example:

class Config {
  static const apiUrl = String.fromEnvironment('API_URL', defaultValue: 'https://api.prod.com');
}

Or, with dotenv, load .env files per flavour.


🌟 Best Practices (From Experience)

✅ Name your apps clearly: "MyApp Dev", "MyApp Staging".
✅ Use different icons for each flavour to avoid mix-ups.
Automate builds with CI/CD.
✅ Never hardcode secrets in your code—use environment variables.


✨ Final Thoughts

Flavours in Flutter save you time, stress, and mistakes.
Once set up, you can easily switch between environments, test safely, and deploy with confidence.

Want a hands-on guide or GitHub example? Let me know, and I’ll help you get started!

Happy coding! 🚀

4
Subscribe to my newsletter

Read articles from Gidudu Nicholas directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Gidudu Nicholas
Gidudu Nicholas

Hello, I am a senior Flutter developer with vast experience in crafting mobile applications. I am a seasoned community organizer with vast experience in launching and building Google Developer communities under GDG Bugiri Uganda and Flutter Kampala.