Product Flavor: ce este, configurare și exemple în Gradle

Autor: IT Sectr Publicat: 2026-05-30 Timp de citire: 9 min

Product Flavor în dezvoltarea Android este un mecanism Gradle care permite crearea mai multor variante ale unei aplicații dintr-o bază de cod comună. Fiecare flavor poate avea propriul applicationId, resurse, dependențe și funcționalități — de exemplu, versiunile gratuită și plătită. Potrivit Google Android Developers, 2025, Product Flavors fac parte din sistemul Build Variants și se combină cu Build Types prin flavorDimensions. Aceasta este abordarea standard pentru publicarea mai multor versiuni ale unei aplicații în Google Play.

Principalele

  • Product Flavor — variantă de produs cu applicationId, resurse și cod unic.
  • Flavor Dimensions grupează flavor-urile în axe independente pentru configurare multidimensională.
  • Source sets pentru flavor suprascriu resursele main: pictograme, șiruri, manifest.
  • Gradle generează automat Build Variant pentru fiecare combinație flavor + build type.
  • Google Play acceptă publicarea mai multor flavor-uri ca aplicații separate sau una cu configurații diferite.

Ce este Product Flavor?

Product Flavor este o configurație Gradle în blocul android.productFlavors care descrie o variantă de produs. Fiecare flavor poate suprascrie applicationId, versionName, versionCode, minSdkVersion, targetSdkVersion, signingConfig și alți parametri defaultConfig. Product Flavors nu au limită de număr: un proiect poate conține 2, 5, 10 flavor-uri — Gradle va procesa toate combinațiile.

Product Flavor rezolvă problema codebase reuse — când dintr-un singur depozit trebuie construite mai multe aplicații diferite. Scenarii tipice: versiunea gratuită cu reclame și cea plătită fără; versiunea demo cu funcționalitate limitată; versiunile corporativă și de consum; aplicații white-label pentru diferiți clienți. Fără Product Flavors, fiecare versiune ar trebui menținută într-un proiect separat, ceea ce duce la 60-70% duplicare de cod.

Istoric, Product Flavors au apărut în Android Gradle Plugin 0.9 (2013) ca înlocuitor pentru configurațiile ant. Înainte, dezvoltatorii foloseau proiecte separate pentru versiuni diferite sau înlocuirea manuală a resurselor înainte de compilare. Introducerea flavor-urilor în AGP a unificat abordarea și a făcut-o standard. Potrivit sondajului JetBrains, 2024, 78% dintre proiectele Android cu mai multe versiuni folosesc Product Flavors, restul — comutare manuală prin BuildConfig sau reflection.

Product Flavor vs Build Type

Build Type gestionează procesul de compilare (debug cu depanare, release cu optimizare). Product Flavor gestionează conținutul compilării (free fără funcții plătite, paid cu ele). Build Type este o configurare infrastructurală, Product Flavor — una de produs. Ambele concepte sunt ortogonale: compilarea debug a free-flavor diferă de compilarea release a free-flavor doar prin parametrii de compilare, nu prin funcționalitate. Product Flavor nu poate fi folosit pentru a dezactiva depanarea — aceasta este sarcina Build Type.

Flavor Dimensions: organizarea dimensiunilor

Ordinea dimensiunilor și prioritatea

Flavor Dimensions (dimensiuni) — un mecanism de grupare a Product Flavors în categorii independente. Dacă aplicația are versiune gratuită/plătită și separat regiunea americană/europeană, flavour-urile se grupează în două dimensiuni: „tier” (free, paid) și „region” (us, eu). Gradle creează produsul cartezian al dimensiunilor: freeUs, freeEu, paidUs, paidEu — 4 variante. Fără dimensiuni, Gradle ar percepe toate patru flavour-uri ca un singur plan și doar unul ar putea fi selectat.

Dimensiunile se declară în blocul flavorDimensions ca șir sau listă de șiruri. Ordinea dimensiunilor afectează prioritatea source sets: prima dimensiune are cea mai mare prioritate. Dacă dimensiunea A (tier) este prima, atunci src/free/ va suprascrie src/us/ în caz de conflict de resurse. De asemenea, ordinea influențează denumirea Variant: mai întâi flavour-ul primei dimensiuni, apoi al doilea, apoi Build Type: freeUsDebug.

Numărul de dimensiuni nu este limitat, dar fiecare nouă dimensiune multiplică numărul de Build Variants. Pentru un proiect cu 4 dimensiuni (câte 2 flavour) și 2 build types: 2 × 2 × 2 × 2 × 2 = 32 de variante. Limita practică — 3 dimensiuni (maximum 8-12 variante). Mai mult — iar configurarea Gradle încetinește, iar în Android Studio panoul Build Variants devine ilizibil.

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

// Rezultat: freeMinApi21, freeMinApi26, paidMinApi21, paidMinApi26
// Fiecare × debug/release = 8 Build Variants

Crearea Product Flavors în build.gradle

Kotlin DSL pentru Product Flavors

Pentru a crea un Product Flavor, trebuie adăugat blocul productFlavors în interiorul android, specificând numele flavor și parametrii săi. Declarația minimă a unui flavor este numele și dimension. Toți ceilalți parametri sunt moșteniți din defaultConfig și pot fi suprascriși. Flavor moștenește defaultConfig complet, inclusiv applicationId, versionCode și testInstrumentationRunner.

Fiecare flavor poate suprascrie applicationId — aceasta permite instalarea mai multor versiuni ale aplicației pe același dispozitiv simultan. De exemplu, versiunea free va fi com.example.app.free, iar paid — com.example.app.paid. Dacă applicationId nu este suprascris, toate flavour-urile vor avea același identificator și nu vor putea fi instalate în paralel. applicationId trebuie să coincidă cu package-ul din manifest (dacă nu se folosește applicationIdSuffix).

AGP 8+ recomandă utilizarea Kotlin DSL în loc de Groovy pentru build.gradle. Kotlin DSL oferă acces tip-sigur la configurare: IDE sugerează nume de parametri, verifică tipurile la compilare și evidențioază erorile. Migrarea de la Groovy la Kotlin DSL pentru Product Flavors constă de obicei în înlocuirea ghilimelelor cu paranteze și adăugarea tipurilor. AGP este compatibil retroactiv — ambele sintaxe funcționează în paralel în același proiect.

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

Resurse și cod pentru flavor-uri diferite

Fiecare Product Flavor își creează propriul source set — directorul src/<flavorName>/. În acest director pot fi plasate resurse suprascrise, surse și manifest. Source set-ul flavor funcționează ca un strat peste main: fișierele din src/free/res/ suprascriu fișierele din src/main/res/ cu aceleași nume. Aceasta permite șiruri, pictograme, culori și layout-uri diferite pentru fiecare flavor fără a modifica codul principal.

Pentru suprascrierea claselor Java/Kotlin există două abordări: flavor-specific implementation (implementarea unei clase abstracte în fiecare flavor) și BuildConfig field (ramificare în cod). Prima abordare este mai curată: definiți o interfață sau o clasă abstractă în main, iar implementările concrete în src/free/ și src/paid/. La compilare, se compilează doar implementarea flavor-ului curent. Aceasta oferă avantaje simultane: dimensiune APK mai mică (codul plătit nu ajunge în versiunea free) și securitate (nu se poate apela accidental o funcție plătită).

AndroidManifest.xml în source set-ul flavor nu înlocuiește, ci se îmbină cu manifestul main. Îmbinarea are loc conform regulilor Android: aceleași atribute într-un element sunt suprascrise, cele unice sunt adăugate. De exemplu, dacă în manifestul main este declarată permisiunea INTERNET, iar în free nu, internetul va rămâne. Dar tools:node="replace" permite înlocuirea unui întreg bloc de manifest pentru un flavor specific. Acest lucru este util când flavour-uri diferite necesită permisiuni diferite (scriere pe SD pentru paid, cameră pentru 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>

Exemplu: versiunile gratuită și plătită ale aplicației

Să analizăm un scenariu tipic: free — versiune cu reclame și funcții de bază, paid — fără reclame, cu funcționalitate extinsă. Pentru versiunea free se setează applicationId „com.example.app.free”, iar pentru paid — „com.example.app.paid”. Ambele versiuni pot fi instalate pe același dispozitiv simultan, deoarece applicationId este identificatorul unic al aplicației în sistemul Android.

Arhitectural, separarea se bazează pe interface + flavor implementation. În source set-ul main se declară interfața PaymentService. În src/free/ se află implementarea care afișează reclame înainte de plată prin AdMob. În src/paid/ — implementarea care trece direct la gateway-ul de plată. Codul care folosește PaymentService nu știe care implementare este încărcată — acest lucru se decide la compilare. O astfel de abordare garantează că în versiunea free nu va ajunge codul de gestionare a abonamentelor, chiar dacă dezvoltatorul îl apelează accidental.

Dimensiunea APK pentru flavor-uri diferite poate diferi cu 5-15 MB din cauza includerii/excluderii dependențelor. Pentru a exclude o bibliotecă dintr-un anumit flavor, se folosesc dependențe specifice flavor-ului în build.gradle: freeImplementation 'com.google.android.gms:play-services-ads:23.0.0'. Această dependență va fi adăugată doar pentru varianta free și nu va crește dimensiunea versiunii paid. Pentru dependențe comune se folosește implementation — acestea sunt incluse în toate flavor-urile.

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 în proiecte multi-modul

În proiectele multi-modul, modulele bibliotecă pot să nu aibă propriile Product Flavors, ceea ce creează o problemă: biblioteca se compilează o dată (ca release), iar modulul app cu flavor așteaptă biblioteca cu varianta corespunzătoare. Începând cu AGP 8.1, bibliotecile pot publica multiple variants prin blocul publishing.multipleVariants — aceasta permite publicarea tuturor variantelor de flavor ale bibliotecii într-un singur depozit maven, iar modulul app va selecta automat varianta necesară.

O abordare alternativă este declararea acelorași flavorDimensions și productFlavors în bibliotecă ca în modulul app. AGP potrivește automat flavour-urile după potrivirea completă a numelui într-o dimensiune. Dacă numele flavor-ului din bibliotecă coincide cu numele din app, AGP va crea variante consistente. Pentru ușurința întreținerii, se recomandă plasarea definițiilor comune de flavour într-un Convention Plugin — un plugin Gradle care se aplică tuturor modulelor proiectului.

Pentru bibliotecile care nu sunt destinate publicării (module interne), este suficient să sincronizați flavour-urile prin build.gradle al proiectului rădăcină. Gradle oferă metoda subprojects care permite aplicarea configurației la toate subproiectele. Totuși, rețineți că o configurare prea mare în subprojects încetinește faza de configurare. Se recomandă utilizarea Convention Plugins — acestea se compilează o dată și se reutilizează, reducând timpul de configurare cu 15-30%.

Întrebări frecvente

Câte Product Flavors pot fi create?

Nu există limită de număr, dar fiecare dimensiune multiplică numărul de Build Variants. 4 flavour într-o dimensiune + 2 build types = 8 variante. 4 + 4 în două dimensiuni = 16 variante. Se recomandă cel mult 3 dimensiuni și 10-12 variante totale.

Se poate suprascrie manifestul pentru un flavour?

Da, prin source set src/<flavor>/AndroidManifest.xml. Manifestul se îmbină cu cel principal. Pentru înlocuirea unui întreg bloc, folosiți tools:node="replace". De exemplu, pentru a schimba eticheta aplicației sau permisiunile pentru un flavor specific.

Cum adaug dependențe specifice flavour-ului?

Folosiți configurația <flavorName>Implementation. Exemplu: freeImplementation 'com.google.android.gms:play-services-ads:23.0.0'. Această dependență va fi inclusă doar la compilarea variantei free. Pentru paid: paidImplementation. Dependențele comune se specifică prin implementation.

Cu ce diferă Product Flavor de Build Type?

Product Flavor definește versiunea de produs (free, paid, demo), Build Type — modul de compilare (debug, release). Flavour-urile pot suprascrie applicationId, versionName, resurse. Build Type gestionează debuggable, minification, signing. Ambele sunt ortogonale și se combină în Build Variant.

Pot folosi Product Flavor cu Jetpack Compose?

Da, Product Flavors funcționează cu Compose fără restricții. Flavor-uri diferite pot avea ecrane Compose diferite prin source sets sau implementarea claselor abstracte. Se pot adăuga și dependențe Compose specifice flavor-ului: freeImplementation 'androidx.compose.ui:ui-tooling'.

Rezumat

  • Product Flavor — mecanism Gradle pentru crearea mai multor versiuni ale aplicației dintr-un singur cod.
  • Flavor Dimensions grupează flavor-urile în dimensiuni, permițând combinarea diferitelor aspecte ale aplicației.
  • Source sets pentru flavor suprascriu resursele, codul și manifestul fără a modifica directorul principal.
  • Interface + flavor implementation — abordare arhitecturală curată pentru separarea funcționalității.
  • Dependențe specifice flavor-ului previn introducerea bibliotecilor inutile în versiunile nepotrivite.
  • Proiectele multi-modul necesită sincronizarea flavour-urilor prin Convention Plugins sau multiple variants publishing.
  • Recomandare: cel mult 3 dimensiuni de flavour și cel mult 10 Build Variants totale în proiect.

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și