In Compose l'interfaccia utente è immutabile: non è possibile aggiornarla dopo che è stata disegnata. Puoi controllare lo stato dell'interfaccia utente. Ogni volta che cambia lo stato dell'UI, Compose ricrea le parti dell'albero dell'UI che sono cambiate. I composabili possono accettare stato ed esporre eventi. Ad esempio, un TextField
accetta un valore ed espone un onValueChange
di callback che richiede all'handler di callback di modificare il valore.
var name by remember { mutableStateOf("") } OutlinedTextField( value = name, onValueChange = { name = it }, label = { Text("Name") } )
Poiché i composabili accettano lo stato ed espongono gli eventi, il pattern di flusso di dati unidirezionale si adatta bene a Jetpack Compose. Questa guida si concentra su come implementare il pattern di flusso di dati unidirezionale in Compose, su come implementare eventi e detentori dello stato e su come utilizzare i ViewModel in Compose.
Flusso di dati unidirezionale
Un flusso di dati unidirezionale (UDF) è un pattern di progettazione in cui lo stato scorre verso il basso e gli eventi verso l'alto. Seguendo il flusso di dati unidirezionale, puoi disaccoppiare i composabili che mostrano lo stato nell'interfaccia utente dalle parti dell'app che memorizzano e modificano lo stato.
Il ciclo di aggiornamento dell'interfaccia utente per un'app che utilizza il flusso di dati unidirezionale è il seguente:
- Evento: una parte dell'interfaccia utente genera un evento e lo passa verso l'alto, ad esempio un clic sul pulsante passato al ViewModel per la gestione; oppure un evento viene passato da altri livelli dell'app, ad esempio per indicare che la sessione utente è scaduta.
- Aggiorna stato: un gestore eventi potrebbe modificare lo stato.
- Mostra stato: il detentore dello stato lo trasmette e l'interfaccia utente lo visualizza.

Seguire questo pattern quando si utilizza Jetpack Compose offre diversi vantaggi:
- Testabilità: scindere lo stato dall'interfaccia utente che lo mostra semplifica il test di entrambi in isolamento.
- Incapsulamento dello stato: poiché lo stato può essere aggiornato in un'unica posizione e esiste un'unica fonte attendibile per lo stato di un composable, è meno probabile che vengano creati bug a causa di stati incoerenti.
- Coerenza dell'interfaccia utente: tutti gli aggiornamenti dello stato vengono immediatamente riportati nell'interfaccia utente tramite l'utilizzo di detentori dello stato osservabili, come
StateFlow
oLiveData
.
Flusso di dati unidirezionale in Jetpack Compose
I composabili funzionano in base a stato ed eventi. Ad esempio, un TextField
viene aggiornato solo quando viene aggiornato il relativo parametro value
e espone un callback onValueChange
, ovvero un evento che richiede la modifica del valore in uno nuovo. Compose
definisce l'oggetto State
come contenitore di valori e le modifiche al valore dello stato attivano una ricompozione. Puoi conservare lo stato in un
remember { mutableStateOf(value) }
o in un
rememberSaveable { mutableStateOf(value)
a seconda del tempo per cui devi ricordare il valore.
Il tipo del valore del composable TextField
è String
, quindi può provenire da qualsiasi luogo, da un valore hardcoded, da un ViewModel o trasmesso dal composable principale. Non è necessario conservarlo in un oggetto State
, ma devi
aggiornare il valore quando viene chiamato onValueChange
.
Definisci i parametri composibili
Quando definisci i parametri di stato di un composable, tieni presente le seguenti domande:
- Quanto è riutilizzabile o flessibile il composable?
- In che modo i parametri di stato influiscono sul rendimento di questo composable?
Per favorire il disaccoppiamento e il riutilizzo, ogni composable deve contenere il minor numero possibile di informazioni. Ad esempio, quando crei un composable per contenere l'intestazione di un articolo di notizie, preferisci passare solo le informazioni che devono essere visualizzate anziché l'intero articolo:
@Composable fun Header(title: String, subtitle: String) { // Recomposes when title or subtitle have changed. } @Composable fun Header(news: News) { // Recomposes when a new instance of News is passed in. }
A volte, l'utilizzo di singoli parametri migliora anche le prestazioni. Ad esempio, se News
contiene più informazioni di title
e subtitle
, ogni volta che una nuova istanza di News
viene passata a Header(news)
, il composable si ricomporrà, anche se title
e subtitle
non sono cambiati.
Valuta attentamente il numero di parametri che passi. Una funzione con troppi parametri riduce l'ergonomia della funzione, quindi in questo caso è preferibile raggrupparli in una classe.
Eventi in Scrivi
Ogni input alla tua app deve essere rappresentato come evento: tocchi, modifiche al testo e persino timer o altri aggiornamenti. Poiché questi eventi modificano lo stato dell'interfaccia utente,
ViewModel
dovrebbe essere quello a gestirli e aggiornare lo stato dell'interfaccia utente.
Il livello dell'interfaccia utente non deve mai cambiare stato al di fuori di un gestore eventi perché questo può introdurre incoerenze e bug nell'applicazione.
Preferisci passare valori immutabili per gli oggetti lambda di stato e gestore eventi. Questo approccio presenta i seguenti vantaggi:
- Migliori la riutilizzabilità.
- Assicurati che l'interfaccia utente non modifichi direttamente il valore dello stato.
- Eviti i problemi di concorrenza perché ti assicuri che lo stato non venga mutato da un altro thread.
- Spesso, riduci la complessità del codice.
Ad esempio, un composable che accetta un String
e un lambda come parametri può essere chiamato da molti contesti ed è altamente riutilizzabile. Supponiamo che la barra superiore dell'app nella tua app mostri sempre del testo e abbia un pulsante Indietro. Puoi definire un composable MyAppTopAppBar
più generico che riceve il testo e l'handle del pulsante Indietro come parametri:
@Composable fun MyAppTopAppBar(topAppBarText: String, onBackPressed: () -> Unit) { TopAppBar( title = { Text( text = topAppBarText, textAlign = TextAlign.Center, modifier = Modifier .fillMaxSize() .wrapContentSize(Alignment.Center) ) }, navigationIcon = { IconButton(onClick = onBackPressed) { Icon( Icons.AutoMirrored.Filled.ArrowBack, contentDescription = localizedString ) } }, // ... ) }
Modelli di visualizzazione, stati ed eventi: un esempio
Utilizzando ViewModel
e mutableStateOf
, puoi anche introdurre un flusso di dati unidirezionale nella tua app se una delle seguenti condizioni è vera:
- Lo stato dell'interfaccia utente viene visualizzato tramite detentori di stato osservabili, come
StateFlow
oLiveData
. ViewModel
gestisce gli eventi provenienti dall'interfaccia utente o da altri livelli dell'app e aggiorna il detentore dello stato in base agli eventi.
Ad esempio, quando implementi una schermata di accesso, toccare un pulsante Accedi dovrebbe far sì che l'app mostri un indicatore di avanzamento e una chiamata di rete. Se l'accesso è andato a buon fine, l'app passa a un'altra schermata; in caso di errore, l'app mostra una barra di notifica. Ecco come modellare lo stato dello schermo e l'evento:
Lo schermo ha quattro stati:
- Non eseguito: quando l'utente non ha ancora eseguito l'accesso.
- In corso: quando la tua app sta tentando di far accedere l'utente tramite l'esecuzione di una chiamata di rete.
- Error: quando si verifica un errore durante l'accesso.
- Accesso eseguito: quando l'utente ha eseguito l'accesso.
Puoi modellare questi stati come una classe sigillata. ViewModel
espone lo stato come
State
, imposta lo stato iniziale e lo aggiorna in base alle esigenze. ViewModel
gestisce anche l'evento di accesso esponendo un metodo onSignIn()
.
class MyViewModel : ViewModel() { private val _uiState = mutableStateOf<UiState>(UiState.SignedOut) val uiState: State<UiState> get() = _uiState // ... }
Oltre all'API mutableStateOf
, Compose fornisce estensioni per LiveData
, Flow
e Observable
per registrarsi come ascoltatore e rappresentare il valore come stato.
class MyViewModel : ViewModel() { private val _uiState = MutableLiveData<UiState>(UiState.SignedOut) val uiState: LiveData<UiState> get() = _uiState // ... } @Composable fun MyComposable(viewModel: MyViewModel) { val uiState = viewModel.uiState.observeAsState() // ... }
Scopri di più
Per scoprire di più sull'architettura in Jetpack Compose, consulta le seguenti risorse:
Campioni
Consigliati per te
- Nota: il testo del link viene visualizzato quando JavaScript è disattivato
- State e Jetpack Compose
- Salvare lo stato dell'interfaccia utente in Compose
- Gestire l'input dell'utente