Published July 2026 by Batteries Included
You need a second consumer brand: its own icon, Play listing, and marketing story. Under the hood, accounts, messaging, safety, and the path from download to paid user are often the same product.
The expensive move is treating that as a greenfield rewrite: two repos, two backlogs, and months before brand two matches brand one.
On native Android, you usually do not need a second app. You need product flavors, flavor dimensions, and build variants: different versions of the same project that can live side by side on a device and in Google Play, sharing common code and resources while changing what must differ per brand.
Typical path: stabilize one listing first, then add flavors to that master codebase and launch the next brand without rebuilding Android from scratch.
Scope: This guide is for native Android apps built with Gradle (Kotlin, Jetpack Compose, or XML). Flavors are an Android packaging feature, not Kotlin Multiplatform sharing, and not an iOS mechanism. If you use KMP for shared logic, you still configure flavors on the Android app module; iOS branding stays in Xcode.
The decision in one paragraph
Use Android product flavors when you need separate store listings and brand identity, but the same core product behavior. Define one flavor dimension (for example brand) with one flavor per listing. Keep shared logic, navigation, account lifecycle, and release discipline in main/. Put per-brand packaging: application ID, icons, themes, copy, API endpoints, signing, in flavor source sets and Gradle config. Do not fork into two repositories unless the products are genuinely diverging.
Is this Kotlin Multiplatform? No. Flavors ship different Android listings from one Android project. KMP shares Kotlin logic across platforms. Different tools; often used together.
What the three terms actually mean
Think of it this way: the flavor dimension is the question (“which brand?”). Product flavors are the answers (brandA, brandB). A build variant is what you ship: one flavor plus one build type (brandA + release → brandARelease).
Google's API docs describe product flavors as versions of a project you expect to coexist on a device, in Google Play, or in a repository; each with its own features, resources, and application IDs while sharing everything else. You configure them in the productFlavors block of the app module's build file. Every flavor must belong to a named dimension.
For a second consumer brand, one brand dimension with two or more flavors is the usual setup. That gives variants like brandADebug and brandBRelease.
When one dimension is enough
Use a single dimension when the only split that matters is which listing you ship: separate application IDs, icons, and store presence.
When you need multiple dimensions
Add a second dimension only when the app varies on two independent axes and Gradle must combine one flavor from each. Gradle never merges two flavors from the same dimension. Google's build variants documentation uses mode (demo / full) × api (minApi21 / minApi24), different questions, so you get combinations like demoMinApi24Release. Same pattern for brand × tier (free/paid per brand) or brand × market.
Quick test: do you need every brand as free and paid (or US and EU)? Two dimensions. Only Brand A and Brand B as separate listings? One brand dimension.
Do not invent a dimension for debug vs release (that is build types), or for staging vs production unless that split must multiply with brand.
What changes per brand, and what must stay shared
Isolate per brand
- Application ID. Play treats each application ID as a separate app. Use a distinct
applicationIdper flavor, orapplicationIdSuffixon a shared base. The build variants guide explainsapplicationIdvs the manifestpackageattribute: keep the manifest package forRclass resolution; the application ID is what Play and the device use for distribution. - Branding resources. Icons, colors, strings, and splash assets in flavor source sets (for example
src/brandB/res/). Flavor resources overridemain/. - Manifest placeholders and app name. Launcher label, deep-link hosts, and analytics keys that must differ per listing, via
manifestPlaceholdersor flavor-specific manifests. - Environment config.
buildConfigField,resValue, and flavor-specific dependencies for API URLs, flags, and SDK keys, not scatteredif (brand == …)in shared UI. - Signing. Separate signing configs when brands sit under different legal entities.
Keep in main/:
- Core domain logic, repositories, and networking models
- Account creation, login, and safety flows that behave the same
- Navigation structure and most screens
- Crash reporting, release automation, and CI entry points
- Bug fixes and feature work that should land for both brands unless product explicitly scopes a change to one listing
Google's source sets documentation is clear: main/ is the default shared set. Add flavor or build-type source sets only where a file truly differs.
Where the money actually is
Flavors do not make engineering free. They remove the cost of maintaining two independent apps when the product is largely the same.
In practice that looks like:
- One backlog for shared product work. A checkout or onboarding fix ships once, then gets QA'd on both variants.
- One regression surface for shared code. Two binaries to test, not two diverging repos for the same security patch months later.
- Faster second-brand launch. Brand two inherits edge-case handling from brand one instead of rediscovering it in a rewrite.
- Smaller team footprint. You do not need parallel Android squads for parity that is mostly packaging and positioning.
Android's modularization guide frames modules as building blocks that multiple apps or flavors can share. You do not have to modularize before your first flavor, but teams that already separate feature: modules often find flavor boundaries easier to reason about.
When flavors are the wrong tool
Product flavors are not a substitute for product strategy. Skip or delay them when:
- The second “brand” is a different product. Different journeys, data models, or compliance regimes eventually fight a shared codebase. Forking, or a deliberate new app, can beat endless
when (flavor)branches. - The first app is not stable enough to clone. A second variant on a fragile master doubles QA and release risk. Stabilize one listing first.
- Brand differences infect every screen. If “only the logo changes” was the pitch but every sprint adds exceptions, you are maintaining two apps with extra Gradle complexity.
- You only need internal QA vs production. That is usually a build type or config flag, not a consumer-facing brand flavor.
How to add flavors to a live app
A practical sequence when a live consumer app needs a second brand, not a greenfield white-label template:
- Pick one flavor dimension (
brand) and name flavors after business entities, not environments. - Assign distinct application IDs and confirm Play Console accounts, signing keys, and listing ownership.
- Extract the smallest brand surface first: name, icon, theme, splash; before feature code.
- Move hard-coded brand strings and assets out of
main/into flavorres/orbuildConfigField. - Wire CI to build and test both release variants on every mainline merge.
- Treat store submission as a checklist per flavor: content rating, data safety, screenshots, and deep links are per listing even when the pipeline is shared.
Pitfalls that erase the savings
- Flavor checks in shared Kotlin. A few
BuildConfig.FLAVORbranches become dozens. Prefer tiny flavor-specific implementations or resource overrides. - Variant explosion. Each dimension multiplies variants. Use
variantFilterto drop combinations you never ship. - Library flavor mismatches. Library modules with their own flavors need
missingDimensionStrategyormatchingFallbacks, documented in the ProductFlavor API reference. Misconfig shows up late as variant resolve errors. - Testing only the original brand. The second listing is the one that breaks silently in production.
Google Play: separate listings vs multi-APK
Separate consumer brands need distinct application IDs: one listing per app. Legacy multiple APK support (same package name, unique version codes under one listing) is for device-configuration splits (API level, ABI, and similar), not dual brands. See also Google's build variants documentation. With modern App Bundle publishing, treat each Play listing as one application ID.
Android vitals and discoverability apply per listing. Ratings do not transfer. Plan store presence work for each public app.
iOS and KMP: do not assume Android savings carry over
Native iOS: No Gradle product flavors. Multiple brands usually mean separate Xcode targets or schemes, xcconfig files, and bundle IDs.
Kotlin Multiplatform: KMP can share business logic; packaging and store listings stay per platform. Flavors on Android plus parallel targets on iOS is the common pattern. See our KMP and Compose Multiplatform guide if you are weighing shared logic vs fully native apps.
Scoping a second brand on Android alone is its own effort. Adding iOS is a separate line item.
Need a second brand from one Android codebase?
We help teams with live Android apps add product flavors, tighten release discipline, and launch additional Play listings without a rewrite. If portfolio scope needs buy-in first, start with a Discovery Roadmap.
Related