Contenitori di stato e stato dell'interfaccia utente

La guida al livello UI illustra il flusso di dati unidirezionali (UDF) come mezzo per produrre e gestire lo stato dell'interfaccia utente per il livello.

I dati passano in modo unidirezionale dal livello dati all'interfaccia utente.
Figura 1: flusso di dati unidirezionale

evidenzia inoltre i vantaggi della delega della gestione delle funzioni definite dall'utente a una classe speciale denominata proprietario statale. Puoi implementare un proprietario di stato tramite una classe ViewModel o una classe normale. Questo documento esamina in modo più approfondito i titolari degli stati e il ruolo che svolgono nel livello UI.

Alla fine di questo documento, dovresti sapere come gestire lo stato dell'applicazione nel livello UI, ovvero la pipeline di produzione dello stato dell'interfaccia utente. Dovresti essere in grado di comprendere e conoscere quanto segue:

  • Comprendi i tipi di stato dell'interfaccia utente presenti nel livello dell'interfaccia utente.
  • Comprendi i tipi di logica che operano su questi stati dell'interfaccia utente nel livello UI.
  • Sapere come scegliere l'implementazione appropriata di un titolare di uno stato, ad esempio un ViewModel o una classe semplice.

Elementi della pipeline di produzione dello stato dell'interfaccia utente

Lo stato dell'interfaccia utente e la logica che lo produce definiscono il livello dell'interfaccia utente.

Stato UI

Lo stato UI è la proprietà che descrive l'interfaccia utente. Esistono due tipi di stato dell'interfaccia utente:

  • Lo stato UI schermo indica cosa devi mostrare sullo schermo. Ad esempio, una classe NewsUiState può contenere gli articoli e altre informazioni necessarie per il rendering dell'interfaccia utente. Questo stato è solitamente connesso ad altri livelli gerarchici perché contiene dati dell'app.
  • Lo stato degli elementi UI si riferisce a proprietà intrinseche degli elementi dell'interfaccia utente che influiscono sul modo in cui vengono visualizzati. Un elemento UI può essere mostrato o nascosto e può avere un carattere, una dimensione o un colore del carattere specifici. Nelle viste Android, la vista gestisce questo stato da solo in quanto è intrinsecamente stateful, esponendo metodi per modificarlo o eseguire query sullo stato. Un esempio di ciò sono i metodi get e set della classe TextView per il relativo testo. In Jetpack Compose, lo stato è esterno all'oggetto componibile e puoi persino sollevarlo dalle immediate vicinanze del componibile nella funzione componibile chiamante o in un proprietario di stato. Un esempio è ScaffoldState per l'elemento componibile Scaffold.

Logica

Lo stato della UI non è una proprietà statica, poiché i dati dell'applicazione e gli eventi utente causano la variazione dello stato della UI nel tempo. La logica determina le specifiche della modifica, incluse quali parti dello stato dell'interfaccia utente sono state modificate, perché e quando dovrebbe cambiare.

La logica genera lo stato dell'UI
Figura 2: logica come producer dello stato UI

La logica in un'applicazione può essere una logica di business o di interfaccia utente:

  • La logica di business è l'implementazione dei requisiti di prodotto per i dati delle app. Ad esempio, l'aggiunta ai preferiti di un articolo in un'app di lettori di notizie quando l'utente tocca il pulsante. Questa logica per salvare un preferito in un file o in un database viene di solito inserita nel dominio o nei livelli dati. Il proprietario dello stato di solito delega questa logica a questi livelli richiamando i metodi che mostrano.
  • La logica della UI dipende dalla modalità di visualizzazione dello stato dell'UI sullo schermo. Ad esempio, ottenere il suggerimento corretto per la barra di ricerca quando l'utente ha selezionato una categoria, scorrere fino a un determinato elemento di un elenco o visualizzare la logica di navigazione in una determinata schermata quando l'utente fa clic su un pulsante.

Ciclo di vita di Android e tipi di stato e logica dell'interfaccia utente

Il livello UI è costituito da due parti: una dipendente e l'altra indipendente dal ciclo di vita dell'interfaccia utente. Questa separazione determina le origini dati disponibili per ogni parte e, di conseguenza, richiede tipi diversi di stato e logica dell'interfaccia utente.

  • Indipendente dal ciclo di vita dell'UI: questa parte del livello dell'interfaccia utente si occupa dei livelli di produzione dei dati dell'app (livelli di dati o di dominio) ed è definita dalla logica di business. Il ciclo di vita, le modifiche alla configurazione e la ricreazione di Activity nella UI possono influire sul fatto che la pipeline di produzione dello stato dell'interfaccia utente sia attiva, ma non influiscono sulla validità dei dati prodotti.
  • Dipendente del ciclo di vita dell'interfaccia utente: questa parte del livello dell'interfaccia utente si occupa della logica dell'interfaccia utente ed è direttamente influenzata dalle modifiche al ciclo di vita o alla configurazione. Queste modifiche influiscono direttamente sulla validità delle origini dei dati letti al suo interno e, di conseguenza, il suo stato può cambiare solo quando il suo ciclo di vita è attivo. Alcuni esempi includono le autorizzazioni di runtime e il recupero di risorse dipendenti dalla configurazione, come stringhe localizzate.

Quanto riportato sopra può essere riassunto nella tabella seguente:

Indipendente dal ciclo di vita dell'UI Dipendente dal ciclo di vita dell'UI
Logica di business Logica UI
Stato UI schermata

La pipeline di produzione dello stato della UI

La pipeline di produzione dello stato dell'interfaccia utente si riferisce ai passaggi eseguiti per produrre lo stato dell'interfaccia utente. Questi passaggi comprendono l'applicazione dei tipi di logica definiti in precedenza e dipendono completamente dalle esigenze della tua UI. Alcune UI potrebbero trarre vantaggio sia dalle parti indipendenti dal ciclo di vita dell'interfaccia utente sia da quelle dipendenti dal ciclo di vita dell'interfaccia utente della pipeline, o nessuna delle due.

In altre parole, sono valide le seguenti permutazioni della pipeline del livello UI:

  • Stato della UI prodotto e gestito dalla stessa. Ad esempio, un contatore di base semplice e riutilizzabile:

    @Composable
    fun Counter() {
        // The UI state is managed by the UI itself
        var count by remember { mutableStateOf(0) }
        Row {
            Button(onClick = { ++count }) {
                Text(text = "Increment")
            }
            Button(onClick = { --count }) {
                Text(text = "Decrement")
            }
        }
    }
    
  • Logica UI → UI. Ad esempio, mostrare o nascondere un pulsante che consente a un utente di passare in cima a un elenco.

    @Composable
    fun ContactsList(contacts: List<Contact>) {
        val listState = rememberLazyListState()
        val isAtTopOfList by remember {
            derivedStateOf {
                listState.firstVisibleItemIndex < 3
            }
        }
    
        // Create the LazyColumn with the lazyListState
        ...
    
        // Show or hide the button (UI logic) based on the list scroll position
        AnimatedVisibility(visible = !isAtTopOfList) {
            ScrollToTopButton()
        }
    }
    
  • Logica di business → UI. Un elemento UI che mostra la foto dell'utente corrente sullo schermo.

    @Composable
    fun UserProfileScreen(viewModel: UserProfileViewModel = hiltViewModel()) {
        // Read screen UI state from the business logic state holder
        val uiState by viewModel.uiState.collectAsStateWithLifecycle()
    
        // Call on the UserAvatar Composable to display the photo
        UserAvatar(picture = uiState.profilePicture)
    }
    
  • Logica di business → logica dell'interfaccia utente → interfaccia utente. Un elemento UI che scorre per visualizzare le informazioni corrette sullo schermo per un determinato stato dell'interfaccia utente.

    @Composable
    fun ContactsList(viewModel: ContactsViewModel = hiltViewModel()) {
        // Read screen UI state from the business logic state holder
        val uiState by viewModel.uiState.collectAsStateWithLifecycle()
        val contacts = uiState.contacts
        val deepLinkedContact = uiState.deepLinkedContact
    
        val listState = rememberLazyListState()
    
        // Create the LazyColumn with the lazyListState
        ...
    
        // Perform UI logic that depends on information from business logic
        if (deepLinkedContact != null && contacts.isNotEmpty()) {
            LaunchedEffect(listState, deepLinkedContact, contacts) {
                val deepLinkedContactIndex = contacts.indexOf(deepLinkedContact)
                if (deepLinkedContactIndex >= 0) {
                  // Scroll to deep linked item
                  listState.animateScrollToItem(deepLinkedContactIndex)
                }
            }
        }
    }
    

Nel caso in cui entrambi i tipi di logica siano applicati alla pipeline di produzione dello stato dell'interfaccia utente, la logica di business deve sempre essere applicata prima della logica dell'interfaccia utente. Provare ad applicare la logica di business dopo la logica dell'interfaccia utente implicherebbe che la logica di business dipenda dalla logica dell'interfaccia utente. Le seguenti sezioni descrivono i motivi per cui si verificano problemi esaminando in dettaglio i diversi tipi di logiche e i rispettivi titolari di stato.

I dati passano dal livello di produzione dei dati all&#39;UI
Figura 3: applicazione della logica nel livello UI

Titolari statali e relative responsabilità

La responsabilità di un proprietario di stato è archiviare lo stato in modo che l'app possa leggerlo. Nei casi in cui sia necessaria la logica, agisce da intermediario e fornisce l'accesso alle origini dati che ospitano la logica richiesta. In questo modo, il proprietario dello stato delega la logica all'origine dati appropriata.

Ciò produce i seguenti vantaggi:

  • UI semplici: l'interfaccia utente associa solo il proprio stato.
  • Mantenabilità: la logica definita nel proprietario dello stato può essere iterata senza modificare l'interfaccia utente stessa.
  • Testabilità: l'interfaccia utente e la logica di produzione dello stato possono essere testate in modo indipendente.
  • Leggibilità: i lettori del codice possono vedere chiaramente le differenze tra il codice di presentazione dell'interfaccia utente e il codice di produzione dello stato dell'interfaccia utente.

Indipendentemente dalle dimensioni o dall'ambito, ogni elemento UI ha una relazione 1:1 con il rispettivo proprietario di stato. Inoltre, un proprietario di stato deve essere in grado di accettare ed elaborare qualsiasi azione dell'utente che potrebbe determinare una modifica dello stato dell'interfaccia utente e deve produrre la successiva modifica dello stato.

Tipi di detentori di stato

Analogamente ai tipi di stato e logica dell'interfaccia utente, esistono due tipi di titolari di stato nel livello UI, definiti in base alla loro relazione con il ciclo di vita dell'interfaccia utente:

  • Il proprietario dello stato della logica di business.
  • Il titolare dello stato della logica dell'interfaccia utente.

Le seguenti sezioni analizzano più attentamente i tipi di detentori di stato, a partire da quelli della logica di business.

Logica di business e proprietario dello stato

I titolari dello stato della logica di business elaborano gli eventi utente e trasformano i dati dai livelli dati o dominio in modo da visualizzare lo stato dell'interfaccia utente. Per offrire un'esperienza utente ottimale quando si considerano le modifiche al ciclo di vita di Android e alla configurazione delle app, i titolari di stato che utilizzano la logica di business devono avere le seguenti proprietà:

Proprietà Dettagli
Genera stato UI I titolari dello stato della logica di business sono responsabili della produzione dello stato dell'interfaccia utente per le loro UI. Questo stato dell'interfaccia utente è spesso il risultato dell'elaborazione degli eventi utente e della lettura dei dati dei livelli dati e di dominio.
Conservazione tramite attività ricreative I titolari dello stato della logica di business mantengono le proprie pipeline di elaborazione dello stato e dello stato nelle attività ricreative di Activity, contribuendo a fornire un'esperienza utente fluida. Nei casi in cui il titolare dello stato non può essere mantenuto e viene ricreato (di solito dopo il decesso del processo), deve essere in grado di ricreare facilmente il suo ultimo stato per garantire un'esperienza utente coerente.
Possedere uno stato di lunga durata I titolari dello stato della logica di business vengono spesso utilizzati per gestire lo stato delle destinazioni di navigazione. Di conseguenza, spesso mantengono il proprio stato durante le modifiche alla navigazione fino a quando non vengono rimossi dal grafico di navigazione.
È univoco per la relativa interfaccia utente e non è riutilizzabile I titolari dello stato della logica di business in genere producono lo stato per una determinata funzione dell'app, ad esempio TaskEditViewModel o TaskListViewModel, e quindi sempre e sempre applicabili solo a questa funzione dell'app. Lo stesso proprietario di stato può supportare queste funzioni dell'app in diversi fattori di forma. Ad esempio, le versioni dell'app per dispositivi mobili, TV e tablet possono riutilizzare lo stesso proprietario dello stato della logica di business.

Ad esempio, considera la destinazione della navigazione nell'autore nell'app "Ora in Android":

L&#39;app Now in Android dimostra come una destinazione di navigazione che rappresenta una funzione principale dell&#39;app debba avere un proprietario unico dello stato della logica di business.
Figura 4: app Now in Android

In qualità di proprietario dello stato della logica di business, AuthorViewModel genera lo stato dell'interfaccia utente in questo caso:

@HiltViewModel
class AuthorViewModel @Inject constructor(
    savedStateHandle: SavedStateHandle,
    private val authorsRepository: AuthorsRepository,
    newsRepository: NewsRepository
) : ViewModel() {

    val uiState: StateFlow<AuthorScreenUiState> = …

    // Business logic
    fun followAuthor(followed: Boolean) {
      …
    }
}

Tieni presente che AuthorViewModel ha gli attributi descritti in precedenza:

Proprietà Dettagli
Produce AuthorScreenUiState AuthorViewModel legge i dati da AuthorsRepository e NewsRepository e li utilizza per produrre AuthorScreenUiState. Applica inoltre la logica di business quando l'utente vuole seguire o non seguire più un Author delega a AuthorsRepository.
Ha accesso al livello dati Un'istanza di AuthorsRepository e NewsRepository viene passata al suo costruttore, il che consente di implementare la logica di business che prevede l'esecuzione di un Author.
Sopravvive a Activity attività ricreative Poiché è implementato con ViewModel, verrà conservato durante le attività di svago di Activity. In caso di decesso del processo, è possibile leggere l'oggetto SavedStateHandle per fornire la quantità minima di informazioni richieste per ripristinare lo stato della UI dal livello dati.
Possedere uno stato di lunga durata ViewModel ha come ambito il grafico di navigazione, pertanto, a meno che la destinazione dell'autore non venga rimossa dal grafico di navigazione, lo stato dell'interfaccia utente in uiState StateFlow rimane in memoria. L'uso dell'elemento StateFlow aggiunge anche il vantaggio di rendere pigra l'applicazione della logica di business che produce lo stato, poiché lo stato viene prodotto solo se esiste un raccoglitore dello stato dell'interfaccia utente.
È univoco per la sua UI Il AuthorViewModel è applicabile solo alla destinazione di navigazione dell'autore e non può essere riutilizzato altrove. Se esiste una logica di business riutilizzata in più destinazioni di navigazione, questa deve essere incapsulata in un componente basato sul livello dei dati o del dominio.

ViewModel come titolare dello stato della logica di business

I vantaggi di ViewModels nello sviluppo Android li rendono adatti a fornire l'accesso alla logica di business e a preparare i dati dell'applicazione per la presentazione sullo schermo. Questi vantaggi includono:

  • Le operazioni attivate da ViewModel sopravvivono alle modifiche alla configurazione.
  • Integrazione con Navigatore:
    • La navigazione memorizza ViewModels nella cache quando lo schermo si trova nello stack posteriore. Questo è importante per rendere immediatamente disponibili i dati caricati in precedenza quando torni a destinazione. Si tratta di un aspetto più difficile da fare con un titolare di stato che segue il ciclo di vita della schermata componibile.
    • Il ViewModel viene cancellato anche quando la destinazione viene separata dallo stack posteriore, garantendo che il tuo stato venga automaticamente pulito. Ciò è diverso dall'ascolto dello smaltimento componibile, che può verificarsi per diversi motivi, come l'apertura di una nuova schermata, a causa di una modifica alla configurazione o per altri motivi.
  • Integrazione con altre librerie Jetpack come Hilt.

Logica dell'interfaccia utente e proprietario dello stato

La logica dell'interfaccia utente è logica che opera sui dati forniti dall'interfaccia utente stessa. Questo può riguardare lo stato degli elementi UI o su origini dati UI come l'API delle autorizzazioni o Resources. I titolari di stato che utilizzano la logica dell'interfaccia utente in genere hanno le seguenti proprietà:

  • Mostra lo stato della UI e gestisce lo stato degli elementi UI.
  • Non sopravvive alla ricreazione Activity: i titolari di stato ospitati nella logica dell'interfaccia utente dipendono spesso dalle origini dati della UI stessa e il tentativo di conservare queste informazioni nelle modifiche alla configurazione spesso causa una perdita di memoria. Se i proprietari di stato hanno bisogno che i dati rimangano persistenti anche dopo le modifiche alla configurazione, devono delegare a un altro componente più adatto alla possibilità di sopravvivere alla ricreazione della Activity. In Jetpack Compose, ad esempio, gli stati degli elementi componibili dell'interfaccia utente creati con funzioni remembered delegano spesso a rememberSaveable per preservare lo stato durante le attività di svago di Activity. Esempi di queste funzioni includono rememberScaffoldState() e rememberLazyListState().
  • Contiene riferimenti a origini dati con ambito UI: è possibile fare riferimento alle origini dati, come API e risorse del ciclo di vita, in modo sicuro poiché il titolare dello stato della logica dell'interfaccia utente ha lo stesso ciclo di vita dell'UI.
  • Riutilizzabile in più UI: diverse istanze dello stesso titolare dello stato logico della UI possono essere riutilizzate in diverse parti dell'app. Ad esempio, un proprietario di stato per la gestione degli eventi di input utente per un gruppo di chip può essere utilizzato in una pagina di ricerca per i chip di filtro e anche per il campo "A" per i destinatari di un'email.

Il titolare dello stato della logica dell'interfaccia utente è in genere implementato con una classe normale. Questo perché l'interfaccia utente stessa è responsabile della creazione del titolare dello stato della logica dell'interfaccia utente e il titolare dello stato della logica dell'interfaccia utente ha lo stesso ciclo di vita della UI stessa. In Jetpack Compose, ad esempio, il proprietario dello stato fa parte della composizione e segue il relativo ciclo di vita.

Ciò può essere illustrato nel seguente esempio nel Esempio di Now in Android:

Ora in Android utilizza un proprietario di stato di una classe normale per gestire la logica dell&#39;interfaccia utente
Figura 5: app di esempio Now in Android

L'esempio di Now in Android mostra una barra dell'app in basso o una barra di navigazione per la navigazione, a seconda delle dimensioni dello schermo del dispositivo. Per gli schermi più piccoli si utilizza la barra in basso dell'app, mentre per gli schermi più grandi viene usata la barra di navigazione.

Poiché la logica per decidere l'elemento UI di navigazione appropriato utilizzato nella funzione componibile NiaApp non dipende dalla logica di business, può essere gestita da un titolare di stato di classe normale chiamato NiaAppState:

@Stable
class NiaAppState(
    val navController: NavHostController,
    val windowSizeClass: WindowSizeClass
) {

    // UI logic
    val shouldShowBottomBar: Boolean
        get() = windowSizeClass.widthSizeClass == WindowWidthSizeClass.Compact ||
            windowSizeClass.heightSizeClass == WindowHeightSizeClass.Compact

    // UI logic
    val shouldShowNavRail: Boolean
        get() = !shouldShowBottomBar

   // UI State
    val currentDestination: NavDestination?
        @Composable get() = navController
            .currentBackStackEntryAsState().value?.destination

    // UI logic
    fun navigate(destination: NiaNavigationDestination, route: String? = null) { /* ... */ }

     /* ... */
}

Nell'esempio precedente, i seguenti dettagli relativi a NiaAppState sono rilevabili:

  • Non sopravvive alla Activity ricreazione: NiaAppState è remembered nella composizione creandolo con una funzione Componibile rememberNiaAppState seguendo le convenzioni di denominazione di Compose. Dopo la ricreazione di Activity, l'istanza precedente va persa e ne viene creata una nuova con tutte le dipendenze trasmesse, in modo da riflettere la nuova configurazione dell'istanza Activity ricreata. Queste dipendenze possono essere nuove o ripristinate dalla configurazione precedente. Ad esempio, rememberNavController() viene utilizzato nel costruttore NiaAppState e delega a rememberSaveable per conservare lo stato nelle attività ricreative Activity.
  • Contiene riferimenti a origini dati con ambito UI: i riferimenti a navigationController, Resources e altri tipi simili con ambito di ciclo di vita possono essere conservati in sicurezza in NiaAppState poiché condividono lo stesso ambito del ciclo di vita.

Scegliere tra un ViewModel e una classe plain per un proprietario di stato

Nelle sezioni precedenti, la scelta tra ViewModel e un proprietario di classe normale dipende dalla logica applicata allo stato dell'interfaccia utente e dalle origini dei dati su cui opera la logica.

In breve, il diagramma seguente mostra la posizione dei detentori di stato nella pipeline di produzione dello stato dell'interfaccia utente:

I dati passano dal livello di produzione dei dati al livello UI
Figura 6: titolari di stato nella pipeline di produzione dello stato dell'interfaccia utente. Le frecce indicano il flusso di dati.

Infine, devi generare lo stato dell'interfaccia utente utilizzando i detentori di stato più vicini al luogo di consumo. In modo meno formale, devi mantenere lo stato il più possibile, mantenendo un'adeguata proprietà. Se hai bisogno dell'accesso alla logica di business e hai bisogno che lo stato dell'interfaccia utente rimanga invariato finché è possibile accedere a una schermata, anche durante le attività di svago di Activity, ViewModel è un'ottima scelta per l'implementazione del titolare dello stato della logica di business. Per lo stato della UI e la logica dell'interfaccia utente, dovrebbe essere sufficiente una classe semplice il cui ciclo di vita dipende esclusivamente dalla UI.

I titolari di stato sono compoundable

I titolari di uno stato possono dipendere da altri proprietari purché le dipendenze abbiano una durata uguale o inferiore. Ecco alcuni esempi:

  • un titolare dello stato della logica dell'interfaccia utente può dipendere da un altro proprietario dello stato della logica dell'interfaccia utente.
  • un titolare dello stato a livello di schermata può dipendere da un titolare dello stato della logica dell'interfaccia utente.

Lo snippet di codice riportato di seguito mostra come DrawerState di Compose dipenda da un altro proprietario di stato interno, SwipeableState, e come il titolare dello stato della logica dell'interfaccia utente di un'app potrebbe dipendere da DrawerState:

@Stable
class DrawerState(/* ... */) {
  internal val swipeableState = SwipeableState(/* ... */)
  // ...
}

@Stable
class MyAppState(
  private val drawerState: DrawerState,
  private val navController: NavHostController
) { /* ... */ }

@Composable
fun rememberMyAppState(
  drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed),
  navController: NavHostController = rememberNavController()
): MyAppState = remember(drawerState, navController) {
  MyAppState(drawerState, navController)
}

Un esempio di dipendenza che dura più di un proprietario di stato è un titolare di stato logico dell'interfaccia utente a seconda di un titolare di stato a livello di schermata. Ciò ridurrebbe la riusabilità dello stato di minore durata e gli consentirebbe di accedere a più logica e stato di quanto ne abbia effettivamente bisogno.

Se un titolare di stato con durata minore ha bisogno di determinate informazioni da un titolare di stato con ambito più elevato, passa solo le informazioni di cui ha bisogno come parametro invece di passare l'istanza del proprietario dello stato. Ad esempio, nello snippet di codice riportato di seguito, la classe titolare dello stato logico dell'interfaccia utente riceve da ViewModel solo ciò di cui ha bisogno come parametri, invece di passare l'intera istanza ViewModel come dipendenza.

class MyScreenViewModel(/* ... */) {
  val uiState: StateFlow<MyScreenUiState> = /* ... */
  fun doSomething() { /* ... */ }
  fun doAnotherThing() { /* ... */ }
  // ...
}

@Stable
class MyScreenState(
  // DO NOT pass a ViewModel instance to a plain state holder class
  // private val viewModel: MyScreenViewModel,

  // Instead, pass only what it needs as a dependency
  private val someState: StateFlow<SomeState>,
  private val doSomething: () -> Unit,

  // Other UI-scoped types
  private val scaffoldState: ScaffoldState
) {
  /* ... */
}

@Composable
fun rememberMyScreenState(
  someState: StateFlow<SomeState>,
  doSomething: () -> Unit,
  scaffoldState: ScaffoldState = rememberScaffoldState()
): MyScreenState = remember(someState, doSomething, scaffoldState) {
  MyScreenState(someState, doSomething, scaffoldState)
}

@Composable
fun MyScreen(
  modifier: Modifier = Modifier,
  viewModel: MyScreenViewModel = viewModel(),
  state: MyScreenState = rememberMyScreenState(
    someState = viewModel.uiState.map { it.toSomeState() },
    doSomething = viewModel::doSomething
  ),
  // ...
) {
  /* ... */
}

Il seguente diagramma rappresenta le dipendenze tra l'interfaccia utente e i diversi proprietari di stato dello snippet di codice precedente:

UI che dipende dal supporto dello stato della logica dell&#39;interfaccia utente e dello stato a livello di schermo
Figura 7: interfaccia utente a seconda dei diversi stati. Le frecce indicano le dipendenze.

Samples

I seguenti esempi di Google mostrano l'utilizzo dei titolari di stato nel livello UI. Esplorale per vedere concretamente queste indicazioni: