How I Created Multiple Applications with a Single Codebase Using Flutter Flavors ( White-label Applications)

How I Created Multiple Applications with a Single Codebase Using Flutter Flavors ( White-label Applications)

White-label applications are an efficient way to develop multiple branded versions of an app from a single codebase. Recently, I worked on creating a white-label application using Flutter and its powerful Flavors feature. This allowed me to manage different versions of the app for various markets, each with its own branding, assets, and configurations. Here, I’ll walk you through the process and the steps I followed to achieve this.

Understanding Flutter Flavors

Flutter flavors allow you to build different versions of your app from a single codebase. Each flavor can have its own unique configuration, such as different application IDs, resources, and build settings, making it ideal for white-label applications.

Steps to Create a White-Label Application Using Flutter Flavors

1. Setting Up the Project Structure

The first step was to organize the project structure to accommodate the different flavors. I created specific directories for each flavor’s resources (e.g., logos, icons) in the android/app/src directory:

android/app/src/brandA/
android/app/src/brandB/
android/app/src/brandC/        

Each directory contains its own res/ folder with flavor-specific resources.

2. Modifying the build.gradle File

I updated the build.gradle file to include the necessary plugins and configurations:

plugins {
    id "com.android.application"
    id "kotlin-android"
    id "dev.flutter.flutter-gradle-plugin"
}

android {
    namespace "brand.app.id.android"
    compileSdk flutter.compileSdkVersion
    ndkVersion flutter.ndkVersion

    defaultConfig {
        applicationId "brand.app.id.android"
        minSdkVersion flutter.minSdkVersion
        targetSdkVersion flutter.targetSdkVersion
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
        multiDexEnabled true
    }

    signingConfigs {
        release {
            // Signing config for release builds
        }
        brandA {
            // Signing config for Brand A version
        }
        brandB {
            // Signing config for Brand B version
        }
        brandC {
            // Signing config for Brand C version
        }
    }

    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }

    flavorDimensions "default"

    productFlavors {
        brandA {
            dimension "default"
            applicationId "brand.app.id.android"
            resValue "string", "app_name", "Brand A"
            signingConfig signingConfigs.brandA
        }
        brandB {
            dimension "default"
            applicationIdSuffix ".brandB"
            resValue "string", "app_name", "Brand B"
            signingConfig signingConfigs.brandB
        }
        brandC {
            dimension "default"
            applicationIdSuffix ".brandC"
            resValue "string", "app_name", "Brand C"
            signingConfig signingConfigs.brandC
        }
    }
}
        

This setup allowed me to define separate product flavors for Brand A, Brand B, and Brand C, each with its unique application ID and signing configuration.

3. Creating a Base Model for Flavors

I created a BaseBrandModel to handle flavor-specific configurations:

abstract class BaseBrandModel {
  String get appName;
  String get imageAssetPath;
  String get logo;
  String get primaryColor;
  Color get color;
  String get languageCode;
  String get fontFamily;
  AppStrings get appStrings;
  TextDirection get textDirection;
  Set<String> get subscriptionsIds;
  FirebaseOptions get firebaseOptions;
  String get appPackage;

  static BaseBrandModel? _instance;

  static BaseBrandModel get instance {
    if (_instance == null) {
      throw Exception("BrandModel not initialized. Call initialize() first.");
    }
    return _instance!;
  }

  static void initialize(BaseBrandModel model) {
    _instance = model;
  }
}
        

This model provides an interface for flavor-specific details, such as app name, primary color, language code, and Firebase options.

4. Initializing the App Based on Flavor

I initialized the app using the appropriate flavor model:

const String? appFlavor = String.fromEnvironment('FLUTTER_APP_FLAVOR');

switch (appFlavor) {
  case 'brandA':
    BaseBrandModel.initialize(BrandAModel());
    break;
  case 'brandB':
    BaseBrandModel.initialize(BrandBModel());
    break;
  case 'brandC':
    BaseBrandModel.initialize(BrandCModel());
    break;
  default:
    BaseBrandModel.initialize(BrandAModel());
}

try {
  await Firebase.initializeApp(
    options: BaseBrandModel.instance.firebaseOptions,
  );
  print('Firebase initialized successfully: ${BaseBrandModel.instance.appName}');
} catch (e) {
  print('Failed to initialize Firebase: $e');
}        

This code selects the correct brand model based on the flavor and initializes Firebase accordingly.


To use the flavor-specific configurations, such as the logo, in your Flutter screens, you can leverage the BaseBrandModelclass that you have set up. Here’s how you can implement it:

Assuming you’ve defined the logo property in your BaseBrandModel, you can access it anywhere in your Flutter app using BaseBrandModel.instance.logo. Here’s an example of how you might use it in a Flutter screen:

            Image.asset(BaseBrandModel.instance.logo)        

5. Building and Running the Flavors

Finally, I built and ran each flavor using the following commands:

flutter run --flavor brandA 
flutter run --flavor brandB 
flutter run --flavor brandC -        

These commands allowed me to generate builds for each flavor, ensuring that each version of the app was tailored to its specific market.

Conclusion

Using Flutter flavors to create a white-label application is a powerful technique that simplifies the management of multiple app versions. By centralizing the codebase and customizing the branding and configurations per flavor, I was able to efficiently produce a tailored experience for different markets.

This approach is not only time-efficient but also ensures consistency across all versions of the app. If you're working on a project that requires multiple branded versions, leveraging Flutter flavors is definitely the way to go.

Feel free to connect with me for more insights on Flutter development and white-label app creation!

To view or add a comment, sign in

More articles by Islam Ibrahim

Others also viewed

Explore content categories