Product Flavor no desenvolvimento Android é um mecanismo do Gradle que permite criar múltiplas variantes do mesmo aplicativo a partir de uma base de código compartilhada. Cada flavor pode ter seu próprio applicationId, recursos, dependências e funcionalidade — por exemplo, versões gratuita e paga. De acordo com Google Android Developers, 2025, os Product Flavors fazem parte do sistema Build Variants e são combinados com os Build Types através de flavorDimensions. Esta é a abordagem padrão para publicar múltiplas versões de um aplicativo no Google Play.
Pontos Principais
Product Flavor é uma configuração do Gradle no bloco android.productFlavors que descreve uma variante de produto. Cada flavor pode substituir applicationId, versionName, versionCode, minSdkVersion, targetSdkVersion, signingConfig e outros parâmetros do defaultConfig. Os Product Flavors não têm limite de quantidade: um projeto pode conter 2, 5 ou 10 flavors — o Gradle lida com todas as combinações.
Product Flavor resolve o problema de reutilização de base de código (codebase reuse) — quando vários aplicativos diferentes precisam ser construídos a partir de um único repositório. Cenários típicos: uma versão gratuita com anúncios e uma paga sem eles; uma versão demo com funcionalidade limitada; versões corporativa e de consumo; aplicativos white-label para diferentes clientes. Sem Product Flavors, cada versão teria que ser mantida em um projeto separado, levando a 60-70% de duplicação de código.
Historicamente, os Product Flavors apareceram no Android Gradle Plugin 0.9 (2013) como substituição das configurações ant. Antes disso, os desenvolvedores usavam projetos separados para diferentes versões ou substituição manual de recursos antes da compilação. A introdução de flavors no AGP unificou a abordagem e a tornou o padrão. De acordo com uma pesquisa da JetBrains, 2024, 78% dos projetos Android com múltiplas versões usam Product Flavors, enquanto o restante usa comutação manual via BuildConfig ou reflection.
Build Type gerencia o processo de compilação (debug com depuração, release com otimização). Product Flavor gerencia o conteúdo da compilação (free sem funções pagas, paid com elas). Build Type é uma configuração de infraestrutura, Product Flavor é uma configuração de produto. Ambos os conceitos são ortogonais: uma compilação debug do flavor free difere de uma compilação release do flavor free apenas em parâmetros de compilação, não em funcionalidade. Product Flavor não pode ser usado para desabilitar o depurador — essa é tarefa do Build Type.
Flavor Dimensions são um mecanismo de agrupamento de Product Flavors em categorias independentes. Se um aplicativo tem versão gratuita/paga e separadamente uma região americana/europeia, os flavors são agrupados em duas dimensões: "tier" (free, paid) e "region" (us, eu). O Gradle cria o produto cartesiano das dimensões: freeUs, freeEu, paidUs, paidEu — 4 variantes. Sem dimensões, o Gradle trataria todos os quatro flavors como um único plano, e apenas um poderia ser selecionado.
As dimensões são declaradas no bloco flavorDimensions como uma string ou lista de strings. A ordem das dimensões afeta a prioridade dos source sets: a primeira dimensão tem a prioridade mais alta. Se a dimensão A (tier) for especificada primeiro, então src/free/ substituirá src/us/ em caso de conflitos de recursos. A ordem também afeta como o nome do Variant é formado: primeiro vêm os flavors da primeira dimensão, depois os da segunda, depois Build Type: freeUsDebug.
O número de dimensões não é limitado, mas cada nova dimensão multiplica a quantidade de Build Variants. Para um projeto com 4 dimensões (2 flavors cada) e 2 build types, você obtém 2 × 2 × 2 × 2 × 2 = 32 variantes. O limite prático é 3 dimensões (máximo 8-12 variantes). Além disso, a configuração do Gradle fica mais lenta e o painel Build Variants no Android Studio se torna ilegível.
android {
flavorDimensions "tier", "api"
productFlavors {
free {
dimension "tier"
applicationId "com.example.app.free"
versionNameSuffix "-free"
}
paid {
dimension "tier"
applicationId "com.example.app.paid"
}
minApi21 {
dimension "api"
minSdk 21
}
minApi26 {
dimension "api"
minSdk 26
}
}
}
// Resultado: freeMinApi21, freeMinApi26, paidMinApi21, paidMinApi26
// Cada × debug/release = 8 Build Variants
Para criar um Product Flavor, é necessário adicionar um bloco productFlavors dentro de android, especificar o nome do flavor e seus parâmetros. A declaração mínima de flavor é o nome e a dimensão. Todos os outros parâmetros são herdados do defaultConfig e podem ser substituídos. O flavor herda defaultConfig por completo, incluindo applicationId, versionCode, testInstrumentationRunner.
Cada flavor pode substituir applicationId — isso permite instalar múltiplas versões do aplicativo no mesmo dispositivo simultaneamente. Por exemplo, a versão free será com.example.app.free, paid — com.example.app.paid. Se applicationId não for substituído, todos os flavors terão o mesmo identificador e não poderão ser instalados lado a lado. applicationId deve corresponder ao package no manifesto (a menos que applicationIdSuffix seja usado).
AGP 8+ recomenda usar Kotlin DSL em vez de Groovy para build.gradle. Kotlin DSL fornece acesso type-safe à configuração: o IDE sugere nomes de parâmetros, verifica tipos em tempo de compilação e destaca erros. A migração de Groovy para Kotlin DSL para Product Flavors geralmente consiste em substituir aspas por parênteses e adicionar tipos. AGP é compatível com versões anteriores — ambas as sintaxes funcionam em paralelo dentro do mesmo projeto.
// build.gradle.kts — Kotlin DSL
android {
flavorDimensions += "tier"
productFlavors {
register("free") {
dimension = "tier"
applicationId = "com.example.app.free"
versionNameSuffix = "-free"
buildConfigField("boolean", "IS_PREMIUM", "false")
}
register("paid") {
dimension = "tier"
applicationId = "com.example.app.paid"
versionNameSuffix = "-paid"
buildConfigField("boolean", "IS_PREMIUM", "true")
}
}
}
Cada Product Flavor cria seu próprio source set — um diretório src/<flavorName>/. Este diretório pode conter recursos substituídos, arquivos fonte e o manifesto. O source set do flavor atua como uma sobreposição sobre main: arquivos de src/free/res/ substituem arquivos de src/main/res/ com os mesmos nomes. Isso permite ter diferentes strings, ícones, cores e layouts para cada flavor sem modificar o código principal.
Para substituir classes Java/Kotlin, existem duas abordagens: implementação específica de flavor (implementar uma classe abstrata em cada flavor) e campo BuildConfig (ramificação em código). A primeira abordagem é mais limpa: você define uma interface ou classe abstrata em main, e implementações concretas em src/free/ e src/paid/. Durante a compilação, apenas a implementação do flavor atual é compilada. Isso proporciona benefícios simultâneos: menor tamanho do APK (o código pago não entra na versão free) e segurança (é impossível chamar acidentalmente uma função paga).
AndroidManifest.xml em um source set de flavor não substitui, mas mescla com o manifesto principal. A mesclagem segue as regras do Android: atributos duplicados no mesmo elemento são substituídos, únicos são adicionados. Por exemplo, se o manifesto principal declarar a permissão INTERNET e free não, a permissão de internet permanece. No entanto, tools:node="replace" permite substituir um bloco inteiro do manifesto para um flavor específico. Isso é útil quando diferentes flavors exigem permissões diferentes (gravação em SD para paid, câmera para free).
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:label="Free App"
tools:replace="android:label">
</application>
</manifest>
Considere um cenário típico: free — uma versão com anúncios e funções básicas, paid — sem anúncios, com funcionalidade estendida. Para a versão free, applicationId é definido como "com.example.app.free", para paid — "com.example.app.paid". Ambas as versões podem ser instaladas no mesmo dispositivo simultaneamente, já que applicationId é o identificador único do aplicativo no sistema Android.
Arquiteturalmente, a separação é construída através de interface + implementação de flavor. No source set main, a interface PaymentService é declarada. Em src/free/, há uma implementação que mostra um anúncio antes do pagamento via AdMob. Em src/paid/ — uma implementação que prossegue diretamente para o gateway de pagamento. O código que usa PaymentService não sabe qual implementação está carregada — isso é resolvido em tempo de compilação. Esta abordagem garante que o código de gerenciamento de assinaturas não acabe na versão free, mesmo que o desenvolvedor o chame acidentalmente.
O tamanho do APK para diferentes flavors pode diferir em 5-15 MB devido à inclusão/exclusão de dependências. Para excluir uma biblioteca de um flavor específico, use dependências específicas de flavor no build.gradle: freeImplementation 'com.google.android.gms:play-services-ads:23.0.0'. Esta dependência será adicionada apenas para a variante free e não aumentará o tamanho da versão paid. Para dependências compartilhadas, use implementation — todos os flavors as incluem.
// src/main/kotlin/com/example/payment/PaymentService.kt
interface PaymentService {
fun processOrder(amount: Double, callback: (PaymentResult) -> Unit)
}
// src/free/kotlin/.../FreePaymentService.kt
class FreePaymentService : PaymentService {
override fun processOrder(amount: Double, callback: (PaymentResult) -> Unit) {
AdManager.showInterstitial {
PaymentGateway.charge(amount, callback)
}
}
}
// src/paid/kotlin/.../PaidPaymentService.kt
class PaidPaymentService : PaymentService {
override fun processOrder(amount: Double, callback: (PaymentResult) -> Unit) {
PaymentGateway.charge(amount, callback)
}
}
Em projetos multimódulo, os módulos de biblioteca podem não ter seus próprios Product Flavors, o que cria um problema: a biblioteca é compilada uma vez (como release), enquanto o módulo app com flavor espera a biblioteca com a variante correspondente. A partir do AGP 8.1, as bibliotecas podem publicar múltiplas variantes através do bloco publishing.multipleVariants — isso permite publicar todas as variantes de flavor da biblioteca em um único repositório maven, e o módulo app selecionará automaticamente a correta.
Uma abordagem alternativa é declarar os mesmos flavorDimensions e productFlavors na biblioteca que no módulo app. O AGP combina automaticamente os flavors por correspondência exata de nome dentro de uma dimensão. Se o nome do flavor na biblioteca corresponder ao nome no app, o AGP criará variantes consistentes. Para facilitar a manutenção, recomenda-se extrair as definições comuns de flavor em um Convention Plugin — um plugin do Gradle aplicado a todos os módulos do projeto.
Para bibliotecas não destinadas à publicação (módulos internos), é suficiente sincronizar os flavors através do build.gradle do projeto raiz. O Gradle fornece o método subprojects, que permite aplicar configuração a todos os subprojetos. No entanto, lembre-se que muita configuração em subprojects retarda a fase de configuração. Recomenda-se usar Convention Plugins — eles são compilados uma vez e reutilizados, reduzindo o tempo de configuração em 15-30%.
Perguntas Frequentes
Não há limite de quantidade, mas cada dimensão multiplica o número de Build Variants. 4 flavors em uma dimensão + 2 build types = 8 variantes. 4 + 4 em duas dimensões = 16 variantes. Recomenda-se não usar mais de 3 dimensões e 10-12 variantes no total.
Sim, através do source set src/<flavor>/AndroidManifest.xml. O manifesto mescla com o principal. Para substituir um bloco inteiro, use tools:node="replace". Por exemplo, substitua o rótulo do aplicativo ou as permissões para um flavor específico.
Use a configuração <flavorName>Implementation. Exemplo: freeImplementation 'com.google.android.gms:play-services-ads:23.0.0'. Esta dependência será incluída apenas ao compilar a variante free. Para paid: paidImplementation. Dependências comuns são especificadas através de implementation.
Product Flavor define a versão do produto (free, paid, demo), Build Type define o método de compilação (debug, release). Os flavors podem substituir applicationId, versionName, recursos. Build Type controla debuggable, minification, signing. Ambos são ortogonais e se combinam em Build Variant.
Sim, Product Flavors funcionam com Compose sem restrições. Diferentes flavors podem ter diferentes telas Compose através de source sets ou implementações de classes abstratas. Você também pode adicionar dependências Compose específicas de flavor: freeImplementation 'androidx.compose.ui:ui-tooling'.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também