Composable Function è un'unità fondamentale dell'interfaccia utente in Jetpack Compose che definisce come una parte dello schermo dovrebbe apparire e comportarsi. Ogni funzione è contrassegnata dall'annotazione @Composable ed eseguita in un contesto speciale che consente a Compose di tracciare le dipendenze e ricostruire automaticamente l'UI quando i dati cambiano. Secondo Google Android Developers, 2026, la corretta costruzione delle funzioni Composable influisce direttamente sulle prestazioni dell'applicazione e sull'efficienza della ricomposizione.
Punti chiave
Una Composable Function è una funzione nel linguaggio Kotlin, contrassegnata dall'annotazione @Composable, che descrive una parte dell'interfaccia utente in modo dichiarativo. Invece di creare e configurare oggetti View tramite codice Java o markup XML, lo sviluppatore scrive semplicemente come dovrebbe apparire l'UI per ogni stato dei dati.
La differenza principale tra una funzione Composable e il sistema View tradizionale di Android risiede nel modello di aggiornamento. Nell'approccio classico, lo sviluppatore chiamava manualmente findViewById, modificava il testo tramite setText e gestiva la visibilità tramite setVisibility. Composable Function ti libera da questa routine: quando i dati cambiano, il sistema stesso determina quali funzioni devono essere ricomposte ed esegue solo quelle.
Il compilatore Kotlin, elaborando l'annotazione @Composable, genera codice aggiuntivo che integra la funzione nel meccanismo di composizione. Questo codice include la lettura e scrittura in slot — celle di memoria speciali che memorizzano lo stato e i parametri di ciascuna funzione Composable nell'albero UI corrente. Grazie a questa integrazione, Compose sa quali funzioni dipendono da quali dati.
La sintassi di una funzione Composable è estremamente concisa: basta aggiungere @Composable prima della parola chiave fun. La funzione può accettare qualsiasi parametro, includere altre chiamate Composable nel suo corpo e utilizzare costrutti Kotlin — condizioni, cicli, espressioni when — per il rendering condizionale dell'UI.
@Composable
fun ProductItem(
product: Product,
modifier: Modifier = Modifier,
onAddToCart: () -> Unit
) {
Card(modifier = modifier.padding(8.dp)) {
Row(modifier = Modifier.fillMaxWidth().padding(12.dp),
verticalAlignment = Alignment.CenterVertically) {
Column(modifier = Modifier.weight(1f)) {
Text(text = product.name, style = MaterialTheme.typography.titleMedium)
Text(text = "${product.price}", color = MaterialTheme.colorScheme.primary)
}
Button(onClick = onAddToCart) {
Text("Aggiungi al carrello")
}
}
}
}
In questo esempio, la funzione Composable ProductItem accetta un oggetto Product, un modificatore e un callback. Tutti e tre i parametri sono immutabili, garantendo un comportamento prevedibile durante la ricomposizione. Il modificatore viene passato come parametro con un valore predefinito — questa è una pratica standard che consente al chiamante di personalizzare padding e dimensioni.
All'interno di una funzione Composable, vengono utilizzati componenti Material Design integrati (Text, Button, Card, TextField) o primitive fondamentali (Canvas, Layout). Ogni componente accetta parametri per configurare aspetto e comportamento, oltre a uno o più modificatori attraverso il parametro modifier.
I modificatori sono una catena di funzioni che modificano le dimensioni, la posizione, la gestione degli eventi e l'aspetto di un componente. L'ordine dei modificatori nella catena conta: clickable.semantics funziona diversamente da semantics.clickable, e padding.background colora sia l'area che lo sfondo includendo il padding, il che è fondamentale durante la progettazione.
All'interno di una funzione Composable, puoi utilizzare condizioni if e when per il rendering condizionale di parti dell'UI, nonché cicli for per liste dinamiche. Tutti questi costrutti funzionano naturalmente poiché Kotlin è un linguaggio di programmazione completo. Tuttavia, è importante ricordare: se una condizione o un ciclo contiene chiamate a funzioni Composable, anch'esse partecipano alla ricomposizione.
@Composable
fun ProductList(
products: List<Product>,
modifier: Modifier = Modifier
) {
LazyColumn(modifier = modifier) {
items(products, key = { it.id }) { product ->
ProductItem(
product = product,
onAddToCart = { /* add to cart */ }
)
}
}
}
Consideriamo un esempio di schermata di ricerca prodotti utilizzando diverse funzioni Composable. Qui vengono mostrati schemi tipici: un campo di input con stato, filtraggio dell'elenco, gestione dei risultati vuoti e caricamento.
data class Product(
val id: String,
val name: String,
val price: Double,
val category: String
)
@Composable
fun SearchScreen() {
var query by remember { mutableStateOf("") }
val products = remember(query) { getFilteredProducts(query) }
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
OutlinedTextField(
value = query,
onValueChange = { query = it },
label = { Text("Cerca prodotti") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
when (products) {
is Loading -> CircularProgressIndicator()
is Empty -> Text("Nessun risultato trovato")
is Result -> LazyColumn {
items(products.items, key = { it.id }) { product ->
ProductItem(product = product, onAddToCart = {})
}
}
}
}
}
Questo esempio dimostra diversi idiomi contemporaneamente: remember per preservare lo stato della query di ricerca, remember(query) per filtrare con una chiave, when per tre stati dell'UI e LazyColumn per il rendering efficiente delle liste. Ciascuno di questi idiomi è il risultato dell'esperienza pratica nello sviluppo di applicazioni Compose.
Le funzioni Composable accettano parametri proprio come le normali funzioni Kotlin, ma con una differenza importante: un parametro può essere un'altra funzione Composable passata tramite una lambda con annotazione @Composable. Questo meccanismo si chiama Slot API ed è il modello principale per creare contenitori riutilizzabili.
La Slot API risolve il problema che nel sistema View tradizionale veniva risolto attraverso ViewGroup e l'aggiunta programmatica di viste figlie. Invece dei metodi addView, Compose utilizza lambda content — l'ultimo parametro con tipo @Composable () -> Unit. Il chiamante passa qualsiasi UI in questa lambda e il contenitore definisce solo la sua disposizione.
I parametri delle funzioni Composable possono avere valori predefiniti, semplificandone l'uso in diversi contesti. Si raccomanda di rendere obbligatori solo i parametri senza i quali la funzione non può svolgere il suo compito e fornire valori predefiniti ragionevoli per gli altri.
| Parametro | Tipo | Esempio |
|---|---|---|
| Obbligatorio | Qualsiasi tipo | name: String |
| Opzionale | Con valore predefinito | modifier: Modifier = Modifier |
| Content | @Composable () -> Unit | content: @Composable () -> Unit |
| Callback | Lambda senza @Composable | onClick: () -> Unit |
Nella community di Compose sono emersi diversi idiomi consolidati che rendono le funzioni Composable più leggibili e prevedibili. Il primo è State Hoisting: lo stato viene sollevato a un livello superiore e la funzione Composable lo riceve tramite parametri. Questo rende la funzione pura e riutilizzabile in diversi contesti.
Il secondo idioma sono i parametri Event-driven. Invece di passare una ViewModel o useCase a una funzione Composable, vengono passati solo callback specifici: onSave, onDelete, onNavigateToDetail. Questo riduce l'accoppiamento e semplifica i test — un test per ProductItem non necessita di una ViewModel, solo di uno stub lambda.
Il terzo idioma è CompositionLocal per passare dati condivisi attraverso l'albero di composizione. Il tema, la densità dello schermo, il percorso corrente — tutto questo viene passato tramite CompositionLocal, evitando catene di parametri attraverso decine di funzioni Composable. Tuttavia, non bisogna abusare di CompositionLocal: i parametri espliciti sono sempre preferibili alle dipendenze implicite.
// State Hoisting: stato sollevato alla funzione genitore
@Composable
fun CounterDisplay(
count: Int,
onIncrement: () -> Unit
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Contatore: $count", style = MaterialTheme.typography.headlineLarge)
Button(onClick = onIncrement) {
Text("+1")
}
}
}
// Utilizzo con State Hoisting
@Composable
fun CounterScreen() {
var count by remember { mutableStateOf(0) }
CounterDisplay(
count = count,
onIncrement = { count++ }
)
}
Domande frequenti
Sì, return è consentito, ma con cautela. Compose ottimizza la ricomposizione a livello di singole funzioni e un return anticipato può compromettere questa ottimizzazione. È meglio utilizzare operatori condizionali if o when all'interno del corpo della funzione.
In Kotlin, Unit è un oggetto singleton, non un tipo vuoto. Le funzioni Composable restituiscono Unit, il che tecnicamente significa che restituiscono l'oggetto Unit stesso. Tuttavia, in pratica questo non ha importanza — il valore restituito viene ignorato dal sistema di composizione.
Passare collezioni mutabili è possibile, ma è una cattiva pratica. Se la collezione cambia, Compose non lo saprà perché il riferimento all'oggetto rimane lo stesso. Utilizza liste immutabili o mutableStateListOf per modifiche tracciate.
Per il debug, utilizza Android Studio con Layout Inspector, che mostra l'albero corrente delle funzioni Composable, i valori dei parametri e le ragioni della ricomposizione. Anche il debugger normale di Kotlin funziona — i punti di interruzione all'interno delle funzioni Composable si attivano correttamente a ogni ricomposizione.
Una funzione Composable restituisce sempre Unit, quindi il tipo di ritorno non viene specificato. Tentare di restituire un altro tipo causerà un errore di compilazione perché l'annotazione @Composable è incompatibile con tipi di ritorno non Unit.
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