Product Flavor: cos'è, configurazione ed esempi in Gradle

Autore: IT Sectr Pubblicato: 2026-05-30 Tempo di lettura: 9 min

Product Flavor nello sviluppo Android è un meccanismo Gradle che consente di creare più varianti della stessa applicazione da una base di codice condivisa. Ogni flavor può avere il proprio applicationId, risorse, dipendenze e funzionalità — ad esempio, versioni gratuita e a pagamento. Secondo Google Android Developers, 2025, i Product Flavors fanno parte del sistema Build Variants e vengono combinati con i Build Types tramite flavorDimensions. Questo è l'approccio standard per pubblicare più versioni di un'app su Google Play.

Punti Chiave

  • Product Flavor — una variante di prodotto con applicationId, risorse e codice unici.
  • Flavor Dimensions raggruppano i flavor in assi indipendenti per la configurazione multidimensionale.
  • Source sets per un flavor sovrascrivono le risorse principali: icone, stringhe, manifest.
  • Gradle genera automaticamente un Build Variant per ogni combinazione di flavor + build type.
  • Google Play supporta la pubblicazione di più flavor come app separate o una singola app con diverse configurazioni.

Cos'è Product Flavor?

Product Flavor è una configurazione Gradle nel blocco android.productFlavors che descrive una variante di prodotto. Ogni flavor può sovrascrivere applicationId, versionName, versionCode, minSdkVersion, targetSdkVersion, signingConfig e altri parametri di defaultConfig. I Product Flavors non hanno limiti di quantità: un progetto può contenere 2, 5 o 10 flavor — Gradle gestisce tutte le combinazioni.

Product Flavor risolve il problema del riutilizzo della base di codice (codebase reuse) — quando è necessario creare più applicazioni diverse da un unico repository. Scenari tipici: una versione gratuita con pubblicità e una a pagamento senza; una versione demo con funzionalità limitate; versioni aziendali e consumer; app white-label per diversi clienti. Senza Product Flavors, ogni versione dovrebbe essere mantenuta in un progetto separato, portando a una duplicazione del codice del 60-70%.

Storicamente, i Product Flavors sono apparsi in Android Gradle Plugin 0.9 (2013) come sostituzione delle configurazioni ant. Prima di allora, gli sviluppatori utilizzavano progetti separati per diverse versioni o la sostituzione manuale delle risorse prima della build. L'introduzione dei flavor in AGP ha unificato l'approccio e lo ha reso lo standard. Secondo un sondaggio di JetBrains, 2024, il 78% dei progetti Android con più versioni utilizza Product Flavors, mentre il resto utilizza la commutazione manuale tramite BuildConfig o reflection.

Product Flavor vs Build Type

Build Type gestisce il processo di build (debug con debugging, release con ottimizzazione). Product Flavor gestisce il contenuto della build (free senza funzionalità a pagamento, paid con esse). Build Type è un'impostazione di infrastruttura, Product Flavor è un'impostazione di prodotto. Entrambi i concetti sono ortogonali: una build debug del flavor free differisce da una build release del flavor free solo nei parametri di compilazione, non nella funzionalità. Product Flavor non può essere utilizzato per disabilitare il debugger — questo è compito di Build Type.

Flavor Dimensions: organizzazione delle dimensioni

Ordine delle dimensioni e priorità

Flavor Dimensions sono un meccanismo di raggruppamento dei Product Flavors in categorie indipendenti. Se un'app ha una versione gratuita/a pagamento e separatamente una regione americana/europea, i flavor vengono raggruppati in due dimensioni: "tier" (free, paid) e "region" (us, eu). Gradle crea il prodotto cartesiano delle dimensioni: freeUs, freeEu, paidUs, paidEu — 4 varianti. Senza dimensioni, Gradle tratterebbe tutti e quattro i flavor come un unico piano, e solo uno potrebbe essere selezionato.

Le dimensioni vengono dichiarate nel blocco flavorDimensions come stringa o elenco di stringhe. L'ordine delle dimensioni influisce sulla priorità dei source set: la prima dimensione ha la priorità più alta. Se la dimensione A (tier) viene specificata per prima, allora src/free/ sovrascriverà src/us/ in caso di conflitti di risorse. L'ordine influisce anche su come viene formato il nome del Variant: prima vengono i flavor della prima dimensione, poi quelli della seconda, poi Build Type: freeUsDebug.

Il numero di dimensioni non è limitato, ma ogni nuova dimensione moltiplica il numero di Build Variants. Per un progetto con 4 dimensioni (2 flavor ciascuna) e 2 build types, si ottengono 2 × 2 × 2 × 2 × 2 = 32 varianti. Il limite pratico è 3 dimensioni (massimo 8-12 varianti). Oltre questo, la configurazione Gradle rallenta e il pannello Build Variants in Android Studio diventa illeggibile.

groovy
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
        }
    }
}

// Risultato: freeMinApi21, freeMinApi26, paidMinApi21, paidMinApi26
// Ogni × debug/release = 8 Build Variants

Creazione di Product Flavors in build.gradle

Kotlin DSL per Product Flavors

Per creare un Product Flavor, è necessario aggiungere un blocco productFlavors all'interno di android, specificare il nome del flavor e i suoi parametri. La dichiarazione minima del flavor è il nome e la dimensione. Tutti gli altri parametri vengono ereditati da defaultConfig e possono essere sovrascritti. Il flavor eredita completamente defaultConfig, inclusi applicationId, versionCode, testInstrumentationRunner.

Ogni flavor può sovrascrivere applicationId — questo consente di installare più versioni dell'app sullo stesso dispositivo contemporaneamente. Ad esempio, la versione gratuita sarà com.example.app.free, quella a pagamento — com.example.app.paid. Se applicationId non viene sovrascritto, tutti i flavor avranno lo stesso identificatore e non potranno essere installati fianco a fianco. applicationId deve corrispondere al package nel manifest (a meno che non venga utilizzato applicationIdSuffix).

AGP 8+ raccomanda di utilizzare Kotlin DSL invece di Groovy per build.gradle. Kotlin DSL fornisce un accesso type-safe alla configurazione: l'IDE suggerisce i nomi dei parametri, controlla i tipi in fase di compilazione e evidenzia gli errori. La migrazione da Groovy a Kotlin DSL per Product Flavors consiste generalmente nel sostituire le virgolette con parentesi e aggiungere tipi. AGP è retrocompatibile — entrambe le sintassi funzionano in parallelo all'interno dello stesso progetto.

kotlin
// 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")
        }
    }
}

Risorse e codice per diversi flavor

Ogni Product Flavor crea il proprio source set — una directory src/<flavorName>/. Questa directory può contenere risorse sovrascritte, file sorgente e il manifest. Il source set del flavor agisce come un overlay su main: i file da src/free/res/ sovrascrivono i file da src/main/res/ con gli stessi nomi. Ciò consente di avere stringhe, icone, colori e layout diversi per ogni flavor senza modificare il codice principale.

Per sovrascrivere classi Java/Kotlin, esistono due approcci: implementazione specifica del flavor (implementare una classe astratta in ogni flavor) e campo BuildConfig (ramificazione nel codice). Il primo approccio è più pulito: si definisce un'interfaccia o classe astratta in main e implementazioni concrete in src/free/ e src/paid/. Durante la build, viene compilata solo l'implementazione del flavor corrente. Ciò offre vantaggi simultanei: dimensione APK ridotta (il codice a pagamento non entra nella versione gratuita) e sicurezza (è impossibile chiamare accidentalmente una funzione a pagamento).

AndroidManifest.xml in un source set di flavor non sostituisce ma si fonde con il manifest principale. La fusione segue le regole di Android: gli attributi duplicati nello stesso elemento vengono sovrascritti, quelli unici vengono aggiunti. Ad esempio, se il manifest principale dichiara il permesso INTERNET e free no, il permesso Internet rimane. Tuttavia, tools:node="replace" consente di sostituire un intero blocco del manifest per un flavor specifico. Ciò è utile quando flavor diversi richiedono permessi diversi (scrittura su SD per paid, fotocamera per free).

xml

<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>

Esempio: versioni gratuita e a pagamento dell'app

Consideriamo uno scenario tipico: free — una versione con pubblicità e funzionalità di base, paid — senza pubblicità, con funzionalità estese. Per la versione gratuita, applicationId è impostato su "com.example.app.free", per quella a pagamento — "com.example.app.paid". Entrambe le versioni possono essere installate sullo stesso dispositivo contemporaneamente, poiché applicationId è l'identificatore unico dell'applicazione nel sistema Android.

Architetturalmente, la separazione è costruita tramite interfaccia + implementazione del flavor. Nel source set principale, viene dichiarata l'interfaccia PaymentService. In src/free/, c'è un'implementazione che mostra un annuncio prima del pagamento tramite AdMob. In src/paid/ — un'implementazione che procede direttamente al gateway di pagamento. Il codice che utilizza PaymentService non sa quale implementazione sia caricata — questo viene risolto in fase di compilazione. Questo approccio garantisce che il codice di gestione degli abbonamenti non finisca nella versione gratuita, anche se lo sviluppatore lo chiama accidentalmente.

La dimensione dell'APK per diversi flavor può differire di 5-15 MB a causa dell'inclusione/esclusione di dipendenze. Per escludere una libreria da un flavor specifico, utilizzare dipendenze specifiche del flavor in build.gradle: freeImplementation 'com.google.android.gms:play-services-ads:23.0.0'. Questa dipendenza verrà aggiunta solo per la variante gratuita e non aumenterà la dimensione della versione a pagamento. Per le dipendenze condivise, utilizzare implementation — tutti i flavor le includono.

kotlin
// 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)
    }
}

Product Flavor in progetti multimodulo

Nei progetti multimodulo, i moduli libreria potrebbero non avere i propri Product Flavors, creando un problema: la libreria viene costruita una volta (come release), mentre il modulo app con flavor si aspetta la libreria con la variante corrispondente. A partire da AGP 8.1, le librerie possono pubblicare varianti multiple tramite il blocco publishing.multipleVariants — ciò consente di pubblicare tutte le varianti di flavor della libreria in un unico repository maven, e il modulo app selezionerà automaticamente quella corretta.

Un approccio alternativo è dichiarare gli stessi flavorDimensions e productFlavors nella libreria come nel modulo app. AGP abbina automaticamente i flavor per corrispondenza esatta del nome all'interno di una dimensione. Se il nome del flavor nella libreria corrisponde al nome nell'app, AGP creerà varianti coerenti. Per facilitare la manutenzione, si consiglia di estrarre le definizioni comuni di flavor in un Convention Plugin — un plugin Gradle applicato a tutti i moduli del progetto.

Per le librerie non destinate alla pubblicazione (moduli interni), è sufficiente sincronizzare i flavor tramite il build.gradle del progetto root. Gradle fornisce il metodo subprojects, che consente di applicare la configurazione a tutti i sottoprogetti. Tuttavia, tieni presente che troppa configurazione in subprojects rallenta la fase di configurazione. Si consiglia di utilizzare Convention Plugins — vengono compilati una volta e riutilizzati, riducendo il tempo di configurazione del 15-30%.

Domande Frequenti

Quanti Product Flavors si possono creare?

Non c'è limite alla quantità, ma ogni dimensione moltiplica il numero di Build Variants. 4 flavor in una dimensione + 2 build types = 8 varianti. 4 + 4 in due dimensioni = 16 varianti. Si consiglia di non utilizzare più di 3 dimensioni e 10-12 varianti in totale.

Si può sovrascrivere il manifest per un flavor?

Sì, tramite il source set src/<flavor>/AndroidManifest.xml. Il manifest si fonde con quello principale. Per sostituire un intero blocco, utilizzare tools:node="replace". Ad esempio, sostituire l'etichetta dell'app o i permessi per un flavor specifico.

Come aggiungere dipendenze specifiche del flavor?

Utilizzare la configurazione <flavorName>Implementation. Esempio: freeImplementation 'com.google.android.gms:play-services-ads:23.0.0'. Questa dipendenza verrà inclusa solo quando si costruisce la variante gratuita. Per quella a pagamento: paidImplementation. Le dipendenze comuni vengono specificate tramite implementation.

Qual è la differenza tra Product Flavor e Build Type?

Product Flavor definisce la versione del prodotto (free, paid, demo), Build Type definisce il metodo di build (debug, release). I flavor possono sovrascrivere applicationId, versionName, risorse. Build Type controlla debuggable, minification, signing. Entrambi sono ortogonali e si combinano in Build Variant.

Si può usare Product Flavor con Jetpack Compose?

Sì, i Product Flavors funzionano con Compose senza restrizioni. Flavor diversi possono avere schermate Compose diverse tramite source set o implementazioni di classi astratte. Si possono anche aggiungere dipendenze Compose specifiche del flavor: freeImplementation 'androidx.compose.ui:ui-tooling'.

Riepilogo

  • Product Flavor — un meccanismo Gradle per creare più versioni di un'app da un'unica base di codice.
  • Flavor Dimensions raggruppano i flavor in dimensioni, consentendo di combinare diversi aspetti dell'app.
  • Source sets per un flavor sovrascrivono risorse, codice e manifest senza modificare la directory principale.
  • Interfaccia + implementazione del flavor è un approccio architetturale pulito per separare le funzionalità.
  • Dipendenze specifiche del flavor impediscono a librerie non necessarie di finire in versioni inappropriate.
  • Progetti multimodulo richiedono la sincronizzazione dei flavor tramite Convention Plugins o la pubblicazione di varianti multiple.
  • Raccomandazione: non più di 3 dimensioni di flavor e non più di 10 Build Variants totali in un progetto.

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche