Introduction
Product flavors in Android allow you to create different versions of an application from a single project, such as free and paid versions, or region-specific versions with unique resources, dependencies, and settings. This saves development time and simplifies codebase maintenance, as shared code remains in the main source set while differences are isolated in folders specific to each flavor.
Prerequisites
Before you begin, ensure you have:
- The latest version of Android Studio (Arctic Fox or newer recommended).
- A basic understanding of Gradle build files in Android.
- Access to the module-level
build.gradlefile (typicallyapp/build.gradle).
Step-by-Step Guide
In this section, we will configure two product flavors: free and paid. Each will have a unique package name suffix and version name.
Step 1: Open the module's build.gradle file
In Android Studio, locate the build.gradle file (Module: app) in your project. It is usually found in the app/ folder or your module's directory. Open it for editing. Ensure you are editing the module-level file, not the project-level root file.
Step 2: Add the productFlavors block to the android configuration
Inside the android { ... } block, add the following lines if they are not already present:
android {
// ... existing settings (compileSdk, defaultConfig, etc.)
// Specify the dimension for flavors. This is mandatory if you use multiple dimensions.
flavorDimensions "version"
productFlavors {
// Flavor definitions will go here
}
}
If you already have a flavorDimensions declaration, use the existing one or add a new one. Dimensions allow you to group flavors if you have several (e.g., "tier" and "region").
Step 3: Define each flavor with unique parameters
Inside productFlavors { ... }, add each flavor. Example for free and paid:
productFlavors {
free {
dimension "version"
applicationIdSuffix ".free"
versionNameSuffix "-free"
// Optional: resources or dependencies specific to free
}
paid {
dimension "version"
applicationIdSuffix ".paid"
versionNameSuffix "-paid"
}
}
dimension— specifies the dimension to which the flavor belongs. Required ifflavorDimensionsis defined.applicationIdSuffix— appends a suffix to the package name so flavors have unique IDs (e.g.,com.example.app.free). Without this, flavors with the sameapplicationIdcannot be installed simultaneously.versionNameSuffix— appends a suffix toversionNamefor distinction in Google Play (e.g.,1.0-free).
You can also set other parameters inside each flavor:
minSdkVersion,targetSdkVersion— override SDK versions.resValue "string", "app_name", "MyApp Free"— define a string resource accessible viaR.string.app_name.buildConfigField "String", "API_URL", '"https://api.free.example.com"'— create a constant in theBuildConfigclass for use in code.sourceSets— specify custom paths for source code and resources (although by default Gradle looks insrc/free/,src/paid/).
Step 4: Sync the project with Gradle
After making changes, click the Sync Now button that appears at the top of the file editor, or select File → Sync Project with Gradle Files. This applies the new settings. If there are syntax errors, they will appear in the Build panel. For diagnostics, you can run in the terminal:
./gradlew --refresh-dependencies
Step 5: Build an APK for the selected flavor
In Android Studio, open the Build Variants panel (usually on the left, under Tool Windows). You will see combinations of flavors and build types. For example, freeDebug, freeRelease, paidDebug, paidRelease.
To build an APK:
- Select the desired build variant (e.g.,
freeReleasefor a release build of the free version). - Go to Build → Build Bundle(s) / APK(s) → Build APK(s).
- The resulting APK will be created in the
app/build/outputs/apk/free/release/(orpaid/release/) folder.
Alternatively, use the command line (from the project root):
# Build debug version of free flavor
./gradlew assembleFreeDebug
# Build release version of paid flavor
./gradlew assemblePaidRelease
Verifying the Result
Ensure the flavors work correctly:
- In the Build Variants panel, switch between
freeDebugandpaidDebug. Resources should change if you added flavor-specific ones (e.g., different icons insrc/free/res/andsrc/paid/res/). - Install both APKs on a device or emulator. Check the package name: for the
freeflavor it should be something likecom.example.app.free, and forpaid—com.example.app.paid. They must not conflict. - Run the application for each flavor and check for unique resources or behavior defined via
resValueorbuildConfigField. For example, in code you can useBuildConfig.API_URL.
Possible Issues
- Error: "More than one file was found with OS independent path" — occurs when resources are duplicated (e.g., files with the same name) between flavors or with main resources. Ensure resources for each flavor are placed only in their respective folders (
src/free/res/,src/paid/res/) and there are no conflicts. - applicationId conflict — if you do not use
applicationIdSuffixor assign the sameapplicationIdto all flavors, they will have identical package names, which prevents simultaneous installation. Always add a unique suffix or changeapplicationIdentirely. - Missing dependencies — if a flavor requires specific libraries (e.g., ads only in the free version), add them inside the flavor's
dependenciesblock:
Use configurations likefreeImplementation 'com.google.android.gms:play-services-ads:22.0.0'implementation,api, etc., as in the maindependencies. - Gradle sync issues — check syntax in
build.gradle, especially brackets and indentation. If the Gradle cache is corrupted, run./gradlew cleanor use File → Invalidate Caches and Restart in Android Studio. - Flavor-specific code not picked up — ensure Java/Kotlin files are placed in the correct source sets:
src/free/java/andsrc/paid/java/. Gradle automatically includes them when building the corresponding flavor. Also check thatsourceSetsare not manually overridden without considering flavors. - Flavor changes not applied — after editing
build.gradle, always sync the project. If the problem persists, check if parameters (e.g.,applicationId) are overridden indefaultConfigor other blocks — flavor-specific settings take precedence.