Imagine yourself as an ice-cream vendor. You have a foundational ice-cream recipe, but to captivate your customers even more, you infuse various flavors into that base ice cream, resulting in an enticing range of newly flavored products. This concept, akin to enhancing ice cream with flavors, correlates with the notion of “Product Flavors” in the realm of Android app development. These flavors allow developers to create distinct versions of an app, each tailored to different scenarios, audiences, or requirements. Let’s delve into the intricacies of Product Flavors and their applications in this article.

The Significance of Product Flavors

In the multifaceted landscape of app development, there are scenarios where customization and versatility are paramount. Product Flavors emerge as a pivotal tool to address such scenarios. Consider these instances:

  • White Labeling: You’re delivering a product to multiple clients, each necessitating their own branding elements like logos, colors, and styles. Product Flavors empower you to cater to individual client preferences through a single codebase.
  • Distinct Endpoints: Your app interfaces with various backend services through different API endpoints. With Product Flavors, you can seamlessly switch between endpoints, catering to the requirements of different clients or environments.
  • Free and Paid Versions: Your app has both free and paid versions, each offering specific features. Product Flavors enable you to manage the variations between these versions efficiently, ensuring a smooth user experience.

Unveiling Product Flavors

As per the official definition from developer.android.com, Product Flavors encompass different versions of a project that are designed to coexist on a single device, the Google Play store, or a repository. They facilitate the creation of app variants that share common source code and resources, while allowing differentiation in terms of features, resources, and configurations.

Configuring Product Flavors

Let’s examine a scenario where you’re building an app with both ‘free’ and ‘paid’ versions. The build.gradle file plays a crucial role in configuring these variants. By employing the productFlavors block, you can define unique properties for each flavor:

android {
    namespace = "com.example.flavors"
    compileSdk = 33

    defaultConfig {
        applicationId = "com.example.flavors"
        minSdk = 27
        targetSdk = 33
        versionCode = 1
        versionName = "1.0"
    }
}

As per above config, your build-variants (build types) looks like this:

Configuring Product Flavors

So, product flavors allow you to output different versions of your project by simply changing only the components and settings that are different between them.

This configuration allows you to maintain a single codebase while tailoring the app for different use cases. The ‘free’ and ‘paid’ flavors can possess distinct application IDs, version codes, version names, and more.

Customizing Product Flavors

Beyond the basic configurations, Product Flavors offer the flexibility to fine-tune each variant.

Now based on the above ‘free’ and ‘paid’ specifications, you will create another version of the same app. So your build.gradle file will look like this:

android {
    namespace = "com.example.flavors"
    compileSdk = 33

    defaultConfig {
        applicationId = "com.example.flavors"
        minSdk = 27
        targetSdk = 33
        versionCode = 1
        versionName = "1.0"
    }

    productFlavors {
        create("free") {
            applicationId = "com.example.flavors.free"
            // or applicationIdSuffix = ".free"
        }
        create("paid") {
            applicationId = "com.example.flavors.paid"
            // or applicationIdSuffix = ".paid"
        }
    }
}

As a result, your build-variants will look like this:

You can define more properties that enable you to cater to specific client needs while maintaining code consistency such as:

create("free") {
    applicationId = "com.example.flavors.free"
    versionCode = 2
    versionName = "1.1"
    minSdk = 28
    targetSdk = 33
    buildConfigField("String", "HOST_URL", "\"www.flavors.com/free\"")
    manifestPlaceholders["enable_crashlytics"] = true
}

Diverse Build Variants

In Android app development, build variants play a pivotal role. By default, ‘debug’ and ‘release’ are the primary build types. Debug is the build type that is used when we run the application from the IDE directly onto the device. Release is the build type that is used when we create a signed APK/AAB for publishing on play-store.

However, there are scenarios where you need additional build variants, like for QA testing or client previews. These build variants, combined with Product Flavors, provide an arsenal of options to cater to diverse requirements. Whether it’s creating personalized versions for clients, managing different API endpoints, or offering distinct app versions, Product Flavors streamline the development process. This can be achieved through the buildTypes block:

buildTypes {
    // default
    getByName("release") {
        isMinifyEnabled = true
        proguardFiles(
            getDefaultProguardFile("proguard-android-optimize.txt"),
            "proguard-rules.pro"
        )
    }

    // default
    getByName("debug") {
        isMinifyEnabled = false
        isDebuggable = true
        applicationIdSuffix = ".debug"
        android.buildFeatures.buildConfig = true
        buildConfigField("String", "HOST_URL", "\"www.dev-flavors.com\"")
    }

    // created staging build-type
    create("staging") {      
        initWith(getByName("debug"))
        applicationIdSuffix = ".debugStaging"
        buildConfigField("String", "HOST_URL", "\"www.staging-flavors.com\"")
    }


    // created qa build-type
    create("qa") {
        applicationIdSuffix = ".qa"
        buildConfigField("String", "HOST_URL", "\"www.qa-flavors.com\"")
    }
}

Now your build-variants look like this:

This is how you can create different versions of your app for different purposes, using a single code-base and different configuration.