Jetpack Compose è un toolkit dichiarativo moderno per costruire interfacce Android in Kotlin. Lo sviluppatore descrive l'UI tramite funzioni composable e il toolkit ridisegna automaticamente solo le parti modificate. Secondo Android Developers (2026), Jetpack Compose funziona su Android 5.0 (API 21) e superiori, supporta Material Design 3 e raggiunge 120 FPS su dispositivi di fascia media grazie al proprio sistema di Recomposition — un algoritmo diff intelligente che aggiorna solo i widget modificati.
Punti chiave
Jetpack Compose è un framework dichiarativo di Google per costruire interfacce utente Android, annunciato nel 2019 e arrivato alla versione stabile nel 2021. A differenza del vecchio View System (layout XML + Activity/Fragment), Compose utilizza funzioni Kotlin annotate — @Composable. L'interfaccia è descritta interamente in Kotlin: non c'è separazione tra XML e codice. Questo ha eliminato la classe di errori legati a ID non corrispondenti in XML e Kotlin (type-safe synthetic non aiutava nel refactoring).
Compose è costruito sul proprio sistema di rendering — Canvas, non legato alla gerarchia View. Ogni Composable si disegna direttamente su Canvas, bypassando onMeasure/onDraw del View System. Questo offre un aumento di performance su schermi complessi: nei test di Google (2023), uno schermo Compose con 200 elementi è stato renderizzato il 40% più velocemente di uno simile su RecyclerView + ViewHolder.
Compose richiede minSdk 21 (Android 5.0) e Kotlin 1.9+. Il BOM (Bill of Materials) di Compose sincronizza le versioni di tutte le librerie Compose. Il framework è compatibile con codice View System esistente: Compose si integra tramite ComposeView nei layout XML, e le View vecchie tramite AndroidView nella gerarchia Compose. Secondo Google Play Console (2025), Android 5.0+ copre il 97% dei dispositivi attivi, quindi la compatibilità non è una limitazione per la maggior parte dei progetti.
@Composable è un'annotazione che trasforma una normale funzione Kotlin in un blocco di costruzione dell'UI. Una funzione Composable descrive come dovrebbe apparire un frammento dell'interfaccia — testo, pulsante, elenco. Invece di restituire un valore, la funzione emette componenti UI nella composizione. È simile a un generatore: ogni funzione aggiunge elementi allo schermo quando viene chiamata.
@Composable
fun ProfileCard(name: String, avatarUrl: String) {
Card(
modifier = Modifier.fillMaxWidth().padding(16.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface
)
) {
Row(verticalAlignment = Alignment.CenterVertically) {
AsyncImage(
model = avatarUrl,
contentDescription = "Avatar",
modifier = Modifier.size(48.dp).clip(CircleShape)
)
Spacer(Modifier.width(12.dp))
Text(
text = name,
style = MaterialTheme.typography.titleMedium
)
}
}
}
La funzione ProfileCard accetta parametri (name, avatarUrl) ed emette Card → Row → AsyncImage + Text. Composizione è l'albero dei componenti emessi in un unico passaggio. Se i parametri non sono cambiati, Compose salta la chiamata della funzione (recomposition skip). Se solo name è cambiato, verrà chiamato solo Text, gli altri elementi non verranno ridisegnati. Questa ricomposizione intelligente è il vantaggio principale di performance di Compose rispetto all'ottimizzazione manuale del View System.
Le funzioni Composable utilizzano attivamente gli slot — trailing lambda, content: @Composable (() -> Unit). Questo permette di creare contenitori: Card, Column, Row accettano una lambda content, e il contenuto viene inserito nello slot. La Slot API ha sostituito attributi XML come android:layout_gravity — ora il posizionamento degli elementi figli è impostato dal codice Kotlin all'interno del blocco di contenuto.
State in Compose è qualsiasi valore che può cambiare nel tempo. Quando lo stato cambia, Compose pianifica la ricomposizione per tutti i componenti che leggono questo stato. Il meccanismo ricorda gli hook di React: mutableStateOf restituisce MutableState<T>, la lettura di .value sottoscrive automaticamente la composizione corrente ai cambiamenti.
@Composable
fun CounterExample() {
var count by remember { mutableStateOf(0) }
Column(modifier = Modifier.padding(16.dp)) {
Text("Cliccato: $count")
Button(onClick = { count++ }) {
Text("Incrementa")
}
}
}
@Composable
fun UserScreen(viewModel: UserViewModel) {
val userName by viewModel.userName.collectAsState()
Text("Utente: $userName")
}
remember preserva il valore tra le ricomposizioni — altrimenti mutableStateOf verrebbe ricreato a ogni aggiornamento dell'UI. collectAsState() converte StateFlow dal ViewModel in uno stato compatibile con Compose. Raccomandazione — utilizzare ViewModel con StateFlow per lo stato a livello di schermo e mutableStateOf per lo stato locale (ad esempio, carta espansa). Questa separazione segue il principio dei componenti intelligenti/muti.
State Hoisting è un pattern di sollevamento dello stato da un componente figlio al genitore. Il genitore passa il valore e un callback tramite parametri, il figlio chiama il callback al cambiamento. Il genitore mantiene mutableStateOf, il figlio solo i parametri. Questo rende il componente riutilizzabile e testabile: lo stesso TextField può essere usato con qualsiasi fonte di dati.
Modifier è un oggetto che descrive le trasformazioni del Composable: dimensione, padding, sfondo, gestione dei click, animazione, scorrimento. I modificatori vengono applicati tramite una catena di chiamate: Modifier.fillMaxWidth().padding(16.dp).background(Color.Blue).clickable { }. Ogni chiamata restituisce un nuovo Modifier con la proprietà aggiunta — senza mutazione dell'oggetto originale.
L'ordine dei modificatori è importante. Modifier.padding(16.dp).background(Color.Blue) colora l'area con padding. Modifier.background(Color.Blue).padding(16.dp) colora il rettangolo interno e il padding rimane trasparente. La meccanica assomiglia al modello box CSS: padding prima → background funziona come margin + background; background prima → padding funziona come background + padding interno. Lo sviluppatore deve solo ricordare: padding prima = margine esterno, padding dopo = padding interno.
Se i modificatori integrati non sono sufficienti, se ne crea uno personalizzato tramite Modifier.composed { ... } o Modifier.then(). All'interno di un modificatore personalizzato, si possono usare misurazioni di layout (Modifier.layout { measurable, constraints -> ... }), disegno (Modifier.drawWithContent { ... }), gesti (Modifier.pointerInput { ... }). Esempio: un modificatore per animazione pulsante al click — misura la dimensione, al click avvia un'animazione di scala tramite animateFloatAsState.
Per le animazioni, Compose fornisce animate*AsState (animateFloatAsState, animateColorAsState, animateDpAsState) — i valori animano tra stato vecchio e nuovo al cambiamento. Per animazioni di entrata/uscita — AnimatedVisibility e AnimatedContent con transizioni integrate (fade, slide, expand). Tutte le animazioni funzionano sul layer grafico senza innescare composizione non necessaria.
Le funzioni Composable non dovrebbero eseguire effetti collaterali direttamente (richieste di rete, timer, sottoscrizioni) — vengono chiamate a ogni ricomposizione, il che porterebbe a richieste duplicate. Per gli effetti collaterali, Compose fornisce una famiglia di funzioni Effect: LaunchedEffect avvia una coroutine all'entrata nella composizione e la cancella all'uscita, DisposableEffect — per risorse che richiedono pulizia esplicita (sensori, BroadcastReceiver).
@Composable
fun SensorReader() {
val context = LocalContext.current
var sensorValue by remember { mutableStateOf(0f) }
DisposableEffect(Unit) {
val sensor = registerSensorListener(context) { value ->
sensorValue = value
}
onDispose {
unregisterSensorListener(sensor)
}
}
Text("Valore: $sensorValue")
}
@Composable
fun UserGreeting(userId: String) {
LaunchedEffect(userId) {
val profile = api.fetchProfile(userId)
// aggiornamento stato
}
}
LaunchedEffect(userId) si riavvia se userId cambia — la coroutine precedente viene cancellata e una nuova viene avviata con il nuovo userId. Questo elimina la gestione manuale della cancellazione delle richieste. DisposableEffect(Unit) — un effetto con chiave fissa Unit, si attiva all'entrata nella composizione e chiama onDispose all'uscita. SensorReader registra un listener e si disiscrive quando si lascia lo schermo — senza rischio di perdite.
Se è necessario avviare una coroutine non all'entrata nella composizione ma su un evento (click su pulsante), si usa rememberCoroutineScope(). Restituisce un CoroutineScope legato al ciclo di vita del Composable, senza bisogno di DisposableEffect. Esempio: avviare una richiesta di rete al click su un pulsante — scope.launch { viewModel.loadData() }.
Scegliere tra Compose e View System è la principale questione architetturale per gli sviluppatori Android nel 2026. Entrambe le tecnologie sono supportate da Google, ma Compose è la direzione principale in cui Google investe risorse. View System riceve solo correzioni critiche e non si evolve. La differenza si manifesta nella sintassi, nella gestione dello stato, nelle performance e nei tempi di sviluppo.
| Aspetto | Jetpack Compose | View System |
|---|---|---|
| Descrizione UI | Funzioni Kotlin @Composable | Layout XML + Activity/Fragment |
| Stato | mutableStateOf, StateFlow, ridisegno automatico | findViewById, manuale: setText, notifyDataSetChanged |
| Performance | Ricomposizione intelligente, rendering Canvas | Gerarchia View, measure/layout/draw |
| Animazioni | animate*AsState, AnimatedVisibility, integrate | ValueAnimator, ObjectAnimator, Transition |
| Compatibilità | minSdk 21, ponti ComposeView/AndroidView | Tutte le versioni, qualsiasi |
| Dimensione APK | +3–5 MB per Compose | Senza overhead |
Per i nuovi progetti, Google raccomanda Jetpack Compose come standard per lo sviluppo UI. View System rimane per la manutenzione del codice scritto prima del 2021 e per i casi in cui la dimensione minima dell'APK è critica (ad esempio, per mercati emergenti con dispositivi di fascia bassa). Compose riduce il volume di codice UI del 30–50% rispetto a View System grazie alla sua sintassi dichiarativa e alle animazioni integrate.
Domande frequenti
Sì, tramite ComposeView nel layout XML. Aggiungi la dipendenza Compose e avvolgi lo schermo o parte di esso in ComposeView { MyComposable() }. La migrazione è schermo per schermo.
Il motivo è che lo stato è sollevato troppo in alto o vengono utilizzati oggetti mutabili. Correzione: derivedStateOf per dati derivati e remember per riferimenti stabili.
Usa LazyColumn (analogo a RecyclerView). Gli elementi vengono creati e riutilizzati man mano che si scorre. Per liste complesse con diversi tipi di celle — LazyColumn { items(items, key = { it.id }) { ... } }.
No, puoi iniziare direttamente con Compose. La conoscenza di View System aiuta nella manutenzione del codice legacy, ma Compose è un ecosistema indipendente con la propria documentazione e i propri pattern.
Sì, Material 3 è il tema standard di Compose dal 2023. Si aggiunge tramite implementation("androidx.compose.material3:material3"). Material 2 è considerato obsoleto.
Riepilogo
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.
Leggi anche