Some Android apps serve multiple brands or customers from a single codebase — white-label apps, enterprise apps with per-client branding, or multi-org platforms. This is multi-tenancy at the app level.
Flavors: The Simplest Multi-Tenant Approach
For a fixed set of known tenants, Gradle product flavors let you ship separate APKs from one codebase:
// build.gradle.kts
android {
flavorDimensions += "brand"
productFlavors {
create("brandA") {
dimension = "brand"
applicationId = "com.brandA.app"
resValue("string", "app_name", "Brand A")
}
create("brandB") {
dimension = "brand"
applicationId = "com.brandB.app"
resValue("string", "app_name", "Brand B")
}
}
}
Directory structure:
src/
main/ ← shared code
brandA/
res/
values/colors.xml ← Brand A colors
drawable/logo.xml ← Brand A logo
brandB/
res/
values/colors.xml ← Brand B colors
drawable/logo.xml ← Brand B logo
Dynamic Theming: Runtime Branding
For apps where the brand is determined at runtime (login-time, configuration download), apply theming programmatically:
data class BrandConfig(
val primaryColor: Int,
val accentColor: Int,
val logoUrl: String,
val fontFamily: String?,
val companyName: String
)
class ThemeManager(private val dataStore: DataStore<Preferences>) {
val brandConfig: Flow<BrandConfig?> = dataStore.data.map { prefs ->
val json = prefs[BRAND_CONFIG_KEY] ?: return@map null
Json.decodeFromString<BrandConfig>(json)
}
}
Apply theme in Activity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// Apply brand theme before super.onCreate() / setContentView
val brandTheme = themeManager.getCurrentTheme()
setTheme(brandTheme.themeResId)
super.onCreate(savedInstanceState)
setContentView(binding.root)
}
}
Material You: Dynamic Color
Android 12+ supports dynamic color from the user's wallpaper. Opt in to let the system brand your app automatically:
// In Application.onCreate or before setContentView
DynamicColors.applyToActivitiesIfAvailable(this)
For multi-tenant apps, you can override dynamic colors with brand colors:
// Programmatic token override
val brandedTheme = DynamicColorsOptions.Builder()
.setContentBasedSource(R.color.brand_primary)
.build()
DynamicColors.applyToActivity(this, brandedTheme)
Resource Overlays via Remote Config
For apps that receive brand configs from a server:
class RemoteBrandingManager(
private val config: BrandConfig,
private val context: Context
) {
fun applyToView(view: View) {
view.backgroundTintList = ColorStateList.valueOf(config.primaryColor)
}
fun applyToButton(button: Button) {
button.backgroundTintList = ColorStateList.valueOf(config.accentColor)
button.setTextColor(config.textOnPrimary)
}
fun loadLogo(imageView: ImageView) {
imageView.load(config.logoUrl)
}
}
Compose Multi-Tenant Theming
In Compose, the entire theme is a composable — replacing it is natural:
@Composable
fun BrandedTheme(
brandConfig: BrandConfig,
content: @Composable () -> Unit
) {
val colorScheme = lightColorScheme(
primary = Color(brandConfig.primaryColor),
secondary = Color(brandConfig.accentColor),
surface = Color(brandConfig.surfaceColor)
)
MaterialTheme(
colorScheme = colorScheme,
typography = brandConfig.typography ?: MaterialTheme.typography,
content = content
)
}
// Usage
@Composable
fun App(brandConfig: BrandConfig) {
BrandedTheme(brandConfig) {
MainNavHost()
}
}
Architecture for Multi-Tenant Data Isolation
// Tenant-scoped repositories
class TenantAwareRepository(
private val tenantId: String,
private val api: Api
) {
suspend fun getItems(): List<Item> = api.getItems(tenantId = tenantId)
}
// Scope tenant context via DI
@Module
@InstallIn(ActivityRetainedComponent::class)
object TenantModule {
@Provides
fun provideTenantId(sessionManager: SessionManager): String =
sessionManager.currentTenantId ?: throw IllegalStateException("No tenant")
@Provides
fun provideTenantRepository(tenantId: String, api: Api) =
TenantAwareRepository(tenantId, api)
}
Key Takeaways
| Approach | Best for |
|---|---|
| Product flavors | Fixed set of known brands at build time |
| Runtime theme switching | Dynamic brands loaded from server |
| Material You | Consumer apps that want system-integrated theming |
Compose MaterialTheme | Clean runtime rebrandable Compose apps |
| Tenant-scoped DI | Data isolation between tenants in the same binary |