Crea app adattive con Jetpack Compose

1. Introduzione

In questo codelab imparerai a creare app adattive per smartphone, tablet e dispositivi pieghevoli e come migliorano l'accessibilità con Jetpack Compose. Imparerai anche le best practice per l'utilizzo dei componenti e dei temi di Material 3.

Prima di iniziare, è importante capire cosa intendiamo per adattabilità.

Adattabilità

L'interfaccia utente della tua app deve essere reattiva per tenere conto di diverse dimensioni, orientamenti e fattori di forma della finestra. Un layout adattivo cambia in base allo spazio disponibile sullo schermo. Queste modifiche vanno da semplici aggiustamenti del layout per riempire lo spazio, alla scelta di stili di navigazione rispettivi, fino alla modifica completa dei layout per utilizzare lo spazio aggiuntivo.

Per scoprire di più, consulta la sezione Design adattivo.

In questo codelab, esplorerai come utilizzare e pensare all'adattabilità quando utilizzi Jetpack Compose. Crei un'applicazione, chiamata Reply, che mostra come implementare l'adattabilità per tutti i tipi di schermi e come l'adattabilità e l'accessibilità funzionano insieme per offrire agli utenti un'esperienza ottimale.

Obiettivi didattici

  • Come progettare la tua app in modo che abbia come target tutte le dimensioni delle finestre con Jetpack Compose.
  • Come scegliere come target la tua app per diversi dispositivi pieghevoli.
  • Come utilizzare diversi tipi di navigazione per una migliore raggiungibilità e accessibilità.
  • Come utilizzare i componenti Material 3 per offrire la migliore esperienza per ogni dimensione della finestra.

Che cosa ti serve

Per questo codelab utilizzerai l'emulatore ridimensionabile, che ti consente di passare da un tipo di dispositivo all'altro e da una dimensione della finestra all'altra.

Emulatore ridimensionabile con opzioni per smartphone, piegato, tablet e computer.

Se non hai familiarità con Compose, ti consigliamo di seguire il codelab sulle nozioni di base di Jetpack Compose prima di completare questo codelab.

Cosa creerai

  • Un'app client di posta interattiva chiamata Reply, che utilizza le best practice per i design adattabili, diverse navigazioni Material e un utilizzo ottimale dello spazio sullo schermo.

Mostra il supporto di più dispositivi che otterrai in questo codelab

2. Configurazione

Per ottenere il codice di questo codelab, clona il repository GitHub dalla riga di comando:

git clone https://github.com/android/codelab-android-compose.git
cd codelab-android-compose/AdaptiveUiCodelab

In alternativa, puoi scaricare il repository come file ZIP:

Ti consigliamo di iniziare con il codice nel ramo main e di seguire il codelab passo dopo passo al tuo ritmo.

Apri il progetto in Android Studio

  1. Nella finestra Benvenuto in Android Studio, seleziona c01826594f360d94.pngApri un progetto esistente.
  2. Seleziona la cartella <Download Location>/AdaptiveUiCodelab (assicurati di selezionare la directory AdaptiveUiCodelab contenente build.gradle).
  3. Una volta importato il progetto in Android Studio, verifica di poter eseguire il ramo main.

Esplora il codice iniziale

Il codice del ramo principale contiene il pacchetto ui. Nel pacchetto lavorerai con i seguenti file:

  • MainActivity.kt: l'attività di entry point da cui avvii l'app.
  • ReplyApp.kt: contiene i composable dell'UI della schermata principale.
  • ReplyHomeViewModel.kt: fornisce i dati e lo stato della UI per i contenuti dell'app.
  • ReplyListContent.kt: contiene i composable per fornire elenchi e schermate di dettagli.

Se esegui questa app su un emulatore ridimensionabile e provi diversi tipi di dispositivi, come uno smartphone o un tablet, la UI si espande nello spazio disponibile anziché sfruttare lo spazio dello schermo o fornire un'ergonomia di accessibilità.

Schermata iniziale sullo smartphone

Visualizzazione iniziale allungata sul tablet

Lo aggiornerai per sfruttare lo spazio dello schermo, aumentare l'usabilità e migliorare l'esperienza utente complessiva.

3. Rendere adattabili le app

Questa sezione introduce il concetto di app adattabili e i componenti forniti da Material 3 per semplificare questa operazione. Vengono inoltre trattati i tipi di schermi e stati che prenderai di mira, inclusi smartphone, tablet, tablet di grandi dimensioni e pieghevoli.

Inizierai esaminando i concetti di base delle dimensioni delle finestre, delle posture di piegatura e dei diversi tipi di opzioni di navigazione. Successivamente, puoi utilizzare queste API nella tua app per renderla più adattabile.

Dimensioni delle finestre

I dispositivi Android sono disponibili in tutte le forme e dimensioni, da smartphone a pieghevoli, tablet e dispositivi ChromeOS. Per supportare il maggior numero possibile di dimensioni delle finestre, la tua UI deve essere reattiva e adattabile. Per aiutarti a trovare la soglia giusta per modificare la UI della tua app, abbiamo definito valori di breakpoint che consentono di classificare i dispositivi in classi di dimensioni predefinite (compatte, medie ed estese), chiamate classi di dimensioni della finestra. Si tratta di un insieme di punti di interruzione del viewport che ti aiutano a progettare, sviluppare e testare layout di applicazioni adattabili e reattivi.

Le categorie sono state scelte appositamente per bilanciare la semplicità del layout con la flessibilità di ottimizzare l'app per casi unici. La classe di dimensioni della finestra è sempre determinata dallo spazio dello schermo disponibile per l'app, che potrebbe non corrispondere all'intero schermo fisico per il multitasking o altre segmentazioni.

WindowWidthSizeClass per larghezza compatta, media ed espansa.

WindowHeightSizeClass per altezza compatta, media ed espansa.

Sia la larghezza che l'altezza vengono classificate separatamente, quindi in qualsiasi momento la tua app ha due classi di dimensioni della finestra: una per la larghezza e una per l'altezza. La larghezza disponibile è in genere più importante dell'altezza disponibile a causa dell'ubiquità dello scorrimento verticale, quindi in questo caso utilizzerai anche le classi di dimensioni della larghezza.

Stati di piega

I dispositivi pieghevoli presentano ancora più situazioni a cui la tua app può adattarsi grazie alle loro dimensioni variabili e alla presenza di cerniere. Le cerniere possono oscurare parte del display, rendendo l'area inadatta a mostrare contenuti. Inoltre, potrebbero essere separate, il che significa che ci sono due display fisici separati quando il dispositivo è aperto.

Posture pieghevoli, piatta e semiaperta

Inoltre, l'utente potrebbe guardare il display interno mentre la cerniera è parzialmente aperta, il che comporta diverse posture fisiche in base all'orientamento della piega: postura a tavolino (piega orizzontale, mostrata a destra nell'immagine sopra) e postura a libro (piega verticale).

Scopri di più su posture di piegatura e cerniere.

Tutti questi aspetti devono essere presi in considerazione quando implementi layout adattivi che supportano i dispositivi pieghevoli.

Ricevere informazioni adattive

La libreria Material3 adaptive fornisce un comodo accesso alle informazioni sulla finestra in cui è in esecuzione l'app.

  1. Aggiungi voci per questo artefatto e la relativa versione al file del catalogo delle versioni:

gradle/libs.versions.toml

[versions]
material3Adaptive = "1.0.0"

[libraries]
androidx-material3-adaptive = { module = "androidx.compose.material3.adaptive:adaptive", version.ref = "material3Adaptive" }
  1. Nel file di build del modulo dell'app, aggiungi la nuova dipendenza della libreria, quindi esegui una sincronizzazione Gradle:

app/build.gradle.kts

dependencies {

    implementation(libs.androidx.material3.adaptive)
}

Ora, in qualsiasi ambito componibile, puoi utilizzare currentWindowAdaptiveInfo() per ottenere un oggetto WindowAdaptiveInfo contenente informazioni come la classificazione delle dimensioni della finestra corrente e se il dispositivo è in una postura pieghevole come la postura da tavolo.

Puoi provarlo ora in MainActivity.

  1. In onCreate() all'interno del blocco ReplyTheme, recupera le informazioni adattive della finestra e mostra le classi di dimensioni in un elemento componibile Text. Puoi aggiungerlo dopo l'elemento ReplyApp():

MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContent {
        ReplyTheme {
            val uiState by viewModel.uiState.collectAsStateWithLifecycle()
            ReplyApp(
                replyHomeUIState = uiState,
                onEmailClick = viewModel::setSelectedEmail
            )

            val adaptiveInfo = currentWindowAdaptiveInfo()
            val sizeClassText =
                "${adaptiveInfo.windowSizeClass.windowWidthSizeClass}\n" +
                "${adaptiveInfo.windowSizeClass.windowHeightSizeClass}"
            Text(
                text = sizeClassText,
                color = Color.Magenta,
                modifier = Modifier.padding(
                    WindowInsets.safeDrawing.asPaddingValues()
                )
            )
        }
    }
}

Se esegui l'app ora, le classi di dimensioni della finestra verranno stampate sopra i contenuti dell'app. Esplora le altre informazioni fornite nella finestra. In seguito, puoi rimuovere questo Text, poiché copre i contenuti dell'app e non sarà necessario per i passaggi successivi.

4. Navigazione dinamica

Ora adatterai la navigazione dell'app man mano che lo stato e le dimensioni del dispositivo cambiano per rendere l'app più facile da usare.

Quando gli utenti tengono in mano uno smartphone, le dita si trovano di solito nella parte inferiore dello schermo. Quando gli utenti tengono in mano un dispositivo pieghevole aperto o un tablet, le dita si trovano di solito vicino ai lati. Gli utenti devono essere in grado di navigare o avviare un'interazione con un'app senza richiedere posizioni estreme della mano o cambiare il posizionamento della mano.

Quando progetti la tua app e decidi dove posizionare gli elementi dell'interfaccia utente interattivi nel layout, considera le implicazioni ergonomiche delle diverse regioni dello schermo.

  • Quali aree sono comode da raggiungere mentre tieni in mano il dispositivo?
  • Quali aree possono essere raggiunte solo estendendo le dita, il che potrebbe essere scomodo?
  • Quali aree sono difficili da raggiungere o lontane da dove l'utente tiene il dispositivo?

La navigazione è la prima cosa con cui gli utenti interagiscono e contiene azioni di grande importanza correlate ai percorsi utente critici, pertanto deve essere posizionata nelle aree più facili da raggiungere. La libreria adattiva Material fornisce diversi componenti che ti aiutano a implementare la navigazione, a seconda della classe di dimensioni della finestra del dispositivo.

Navigazione in basso

La navigazione in basso è perfetta per le dimensioni compatte, in quanto teniamo naturalmente il dispositivo in modo che il pollice possa raggiungere facilmente tutti i punti di contatto della navigazione in basso. Utilizzalo ogni volta che hai un dispositivo compatto o un dispositivo pieghevole in uno stato di chiusura compatto.

Barra di navigazione inferiore con elementi

Per una finestra di dimensioni medie, la barra di navigazione è ideale per l'accessibilità, poiché il pollice cade naturalmente lungo il lato del dispositivo. Puoi anche combinare una barra di navigazione con un riquadro di navigazione per mostrare più informazioni.

Modalità di navigazione laterale con elementi

Il riquadro di navigazione offre un modo semplice per visualizzare informazioni dettagliate per le schede di navigazione ed è facilmente accessibile quando utilizzi tablet o dispositivi più grandi. Sono disponibili due tipi di riquadri di navigazione: un riquadro di navigazione modale e un riquadro di navigazione permanente.

Riquadro di navigazione modale

Puoi utilizzare un riquadro di navigazione modale per smartphone e tablet di dimensioni compatte e medie, in quanto può essere espanso o nascosto come overlay sui contenuti. A volte può essere combinato con una barra di navigazione.

Riquadro di navigazione a scomparsa modale con elementi

Riquadro di navigazione a scomparsa permanente

Puoi utilizzare un riquadro di navigazione permanente per la navigazione fissa su tablet di grandi dimensioni, Chromebook e computer.

Riquadro di navigazione permanente con elementi

Implementare la navigazione dinamica

Ora passerai da un tipo di navigazione all'altro man mano che lo stato e le dimensioni del dispositivo cambiano.

Attualmente, l'app mostra sempre un NavigationBar sotto i contenuti dello schermo, indipendentemente dallo stato del dispositivo. Puoi invece utilizzare il componente Material NavigationSuiteScaffold per passare automaticamente da un componente di navigazione all'altro in base a informazioni come la classe di dimensioni della finestra corrente.

  1. Aggiungi la dipendenza Gradle per ottenere questo componente aggiornando il catalogo delle versioni e lo script di build dell'app, quindi esegui una sincronizzazione Gradle:

gradle/libs.versions.toml

[versions]
material3AdaptiveNavSuite = "1.3.0"

[libraries]
androidx-material3-adaptive-navigation-suite = { module = "androidx.compose.material3:material3-adaptive-navigation-suite", version.ref = "material3AdaptiveNavSuite" }

app/build.gradle.kts

dependencies {

    implementation(libs.androidx.material3.adaptive.navigation.suite)
}
  1. Trova la funzione componibile ReplyNavigationWrapper() in ReplyApp.kt e sostituisci Column e i relativi contenuti con un NavigationSuiteScaffold:

ReplyApp.kt

@Composable
private fun ReplyNavigationWrapperUI(
    content: @Composable () -> Unit = {}
) {
    var selectedDestination: ReplyDestination by remember {
        mutableStateOf(ReplyDestination.Inbox)
    }

    NavigationSuiteScaffold(
        navigationSuiteItems = {
            ReplyDestination.entries.forEach {
                item(
                    selected = it == selectedDestination,
                    onClick = { /*TODO update selection*/ },
                    icon = {
                        Icon(
                            imageVector = it.icon,
                            contentDescription = stringResource(it.labelRes)
                        )
                    },
                    label = {
                        Text(text = stringResource(it.labelRes))
                    },
                )
            }
        }
    ) {
        content()
    }
}

L'argomento navigationSuiteItems è un blocco che ti consente di aggiungere elementi utilizzando la funzione item(), in modo simile all'aggiunta di elementi in un LazyColumn. All'interno della lambda finale, questo codice chiama content() passato come argomento a ReplyNavigationWrapperUI().

Esegui l'app sull'emulatore e prova a cambiare le dimensioni tra smartphone, pieghevole e tablet. Vedrai la barra di navigazione trasformarsi in una barra di navigazione e viceversa.

Su finestre molto larghe, ad esempio su un tablet in modalità orizzontale, potresti voler mostrare il riquadro di navigazione permanente. NavigationSuiteScaffold supporta la visualizzazione di un riquadro permanente, anche se non viene mostrato in nessuno dei valori WindowWidthSizeClass attuali. Tuttavia, puoi fare in modo che lo faccia con una piccola modifica.

  1. Aggiungi il seguente codice appena prima della chiamata a NavigationSuiteScaffold:

ReplyApp.kt

@Composable
private fun ReplyNavigationWrapperUI(
    content: @Composable () -> Unit = {}
) {
    var selectedDestination: ReplyDestination by remember {
        mutableStateOf(ReplyDestination.Inbox)
    }

    val windowSize = with(LocalDensity.current) {
        currentWindowSize().toSize().toDpSize()
    }
    val layoutType = if (windowSize.width >= 1200.dp) {
        NavigationSuiteType.NavigationDrawer
    } else {
        NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo(
            currentWindowAdaptiveInfo()
        )
    }

    NavigationSuiteScaffold(
        layoutType = layoutType,
        ...
    ) {
        content()
    }
}

Questo codice recupera innanzitutto le dimensioni della finestra e le converte in unità DP utilizzando currentWindowSize() e LocalDensity.current, quindi confronta la larghezza della finestra per decidere il tipo di layout dell'interfaccia utente di navigazione. Se la larghezza della finestra è almeno 1200.dp, utilizza NavigationSuiteType.NavigationDrawer. In caso contrario, viene utilizzato il calcolo predefinito.

Quando esegui di nuovo l'app sull'emulatore ridimensionabile e provi diversi tipi, nota che ogni volta che la configurazione dello schermo cambia o apri un dispositivo pieghevole, la navigazione passa al tipo appropriato per le dimensioni.

Mostra le modifiche di adattabilità per dispositivi di dimensioni diverse.

Congratulazioni, hai scoperto diversi tipi di navigazione per supportare diversi tipi di dimensioni e stati delle finestre.

Nella sezione successiva, esplorerai come sfruttare lo spazio rimanente dello schermo anziché allungare lo stesso elemento dell'elenco da un bordo all'altro.

5. Utilizzo dello spazio sullo schermo

Indipendentemente dal fatto che tu stia eseguendo l'app su un piccolo tablet, un dispositivo aperto o un tablet di grandi dimensioni, lo schermo viene allungato per riempire lo spazio rimanente. Vuoi assicurarti di poter sfruttare lo spazio dello schermo per mostrare più informazioni, ad esempio, per questa app, mostrare email e thread agli utenti sulla stessa pagina.

Material 3 definisce tre layout canonici, ognuno dei quali ha configurazioni per le classi di dimensioni della finestra compatta, media ed espansa. Il layout canonico Dettagli elenco è perfetto per questo caso d'uso ed è disponibile nella composizione come ListDetailPaneScaffold.

  1. Per ottenere questo componente, aggiungi le seguenti dipendenze ed esegui una sincronizzazione Gradle:

gradle/libs.versions.toml

[libraries]
androidx-material3-adaptive-layout = { module = "androidx.compose.material3.adaptive:adaptive-layout", version.ref = "material3Adaptive" }
androidx-material3-adaptive-navigation = { module = "androidx.compose.material3.adaptive:adaptive-navigation", version.ref = "material3Adaptive" }

app/build.gradle.kts

dependencies {

    implementation(libs.androidx.material3.adaptive.layout)
    implementation(libs.androidx.material3.adaptive.navigation)
}
  1. Trova la funzione componibile ReplyAppContent() in ReplyApp.kt, che al momento mostra solo il riquadro dell'elenco chiamando ReplyListPane(). Sostituisci questa implementazione con ListDetailPaneScaffold inserendo il seguente codice. Poiché si tratta di un'API sperimentale, aggiungi anche l'annotazione @OptIn alla funzione ReplyAppContent():

ReplyApp.kt

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ReplyAppContent(
    replyHomeUIState: ReplyHomeUIState,
    onEmailClick: (Email) -> Unit,
) {
    val navigator = rememberListDetailPaneScaffoldNavigator<Long>()

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane = {
            ReplyListPane(replyHomeUIState, onEmailClick)
        },
        detailPane = {
            ReplyDetailPane(replyHomeUIState.emails.first())
        }
    )
}

Questo codice crea innanzitutto un navigatore utilizzando rememberListDetailPaneNavigator(). Il navigatore consente di controllare quale riquadro viene visualizzato e quali contenuti devono essere rappresentati in quel riquadro, come verrà dimostrato in seguito.

ListDetailPaneScaffold mostrerà due riquadri quando la classe di dimensioni della larghezza della finestra viene espansa. In caso contrario, verrà visualizzato un riquadro o l'altro in base ai valori forniti per due parametri: l'istruzione di scaffolding e il valore di scaffolding. Per ottenere il comportamento predefinito, questo codice utilizza la direttiva scaffold e il valore scaffold fornito dal navigatore.

I restanti parametri obbligatori sono espressioni lambda componibili per i riquadri. ReplyListPane() e ReplyDetailPane() (disponibili in ReplyListContent.kt) vengono utilizzati per compilare rispettivamente i ruoli dei riquadri elenco e dettagli. ReplyDetailPane() prevede un argomento email, quindi per ora questo codice utilizza la prima email dell'elenco di email in ReplyHomeUIState.

Esegui l'app e passa alla visualizzazione dell'emulatore su pieghevole o tablet (potresti anche dover cambiare l'orientamento) per visualizzare il layout a due riquadri. Sembra già molto meglio di prima.

Ora vediamo alcuni dei comportamenti desiderati di questa schermata. Quando l'utente tocca un'email nel riquadro dell'elenco, questa deve essere visualizzata nel riquadro dei dettagli insieme a tutte le risposte. Al momento, l'app non tiene traccia dell'email selezionata e toccare un elemento non ha alcun effetto. Il posto migliore per conservare queste informazioni è insieme al resto dello stato della UI in ReplyHomeUIState.

  1. Apri ReplyHomeViewModel.kt e trova la classe di dati ReplyHomeUIState. Aggiungi una proprietà per l'email selezionata, con un valore predefinito di null:

ReplyHomeViewModel.kt

data class ReplyHomeUIState(
    val emails : List<Email> = emptyList(),
    val selectedEmail: Email? = null,
    val loading: Boolean = false,
    val error: String? = null
)
  1. Nello stesso file, ReplyHomeViewModel ha una funzione setSelectedEmail() chiamata quando l'utente tocca un elemento dell'elenco. Modifica questa funzione per copiare lo stato della UI e registrare l'email selezionata:

ReplyHomeViewModel.kt

fun setSelectedEmail(email: Email) {
    _uiState.update {
        it.copy(selectedEmail = email)
    }
}

Un aspetto da considerare è cosa succede prima che l'utente tocchi un elemento e l'email selezionata sia null. Cosa deve essere visualizzato nel riquadro dei dettagli? Esistono diversi modi per gestire questo caso, ad esempio mostrando per impostazione predefinita il primo elemento dell'elenco.

  1. Nello stesso file, modifica la funzione observeEmails(). Quando viene caricato l'elenco delle email, se lo stato precedente della UI non aveva un'email selezionata, impostalo sul primo elemento:

ReplyHomeViewModel.kt

private fun observeEmails() {
    viewModelScope.launch {
        emailsRepository.getAllEmails()
            .catch { ex ->
                _uiState.value = ReplyHomeUIState(error = ex.message)
            }
            .collect { emails ->
                val currentSelection = _uiState.value.selectedEmail
                _uiState.value = ReplyHomeUIState(
                    emails = emails,
                    selectedEmail = currentSelection ?: emails.first()
                )
            }
    }
}
  1. Torna a ReplyApp.kt e utilizza l'email selezionata, se disponibile, per compilare i contenuti del riquadro dei dettagli:

ReplyApp.kt

ListDetailPaneScaffold(
    // ...
    detailPane = {
        if (replyHomeUIState.selectedEmail != null) {
            ReplyDetailPane(replyHomeUIState.selectedEmail)
        }
    }
)

Esegui di nuovo l'app, passa alla dimensione tablet nell'emulatore e verifica che toccando una voce di elenco i contenuti del riquadro dei dettagli vengano aggiornati.

Funziona alla perfezione quando entrambi i riquadri sono visibili, ma quando la finestra ha spazio per mostrare un solo riquadro, sembra che non succeda nulla quando tocchi un elemento. Prova a passare alla visualizzazione dell'emulatore su uno smartphone o un dispositivo pieghevole in modalità verticale e noterai che solo il riquadro dell'elenco è visibile anche dopo aver toccato un elemento. Questo perché, anche se l'email selezionata viene aggiornata, ListDetailPaneScaffold mantiene il focus sul riquadro dell'elenco in queste configurazioni.

  1. Per risolvere il problema, inserisci il seguente codice come lambda passato a ReplyListPane:

ReplyApp.kt

ListDetailPaneScaffold(
    // ...
    listPane = {
        ReplyListPane(
            replyHomeUIState = replyHomeUIState,
            onEmailClick = { email ->
                onEmailClick(email)
                navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, email.id)
            }
        )
    },
    // ...
)

Questa lambda utilizza il navigatore creato in precedenza per aggiungere un comportamento aggiuntivo quando viene fatto clic su un elemento. Chiama la lambda originale passata a questa funzione e poi chiama anche navigator.navigateTo() specificando il riquadro da mostrare. A ogni riquadro dello scaffold è associato un ruolo e per il riquadro dei dettagli è ListDetailPaneScaffoldRole.Detail. Nelle finestre più piccole, sembrerà che l'app sia stata spostata in avanti.

L'app deve anche gestire cosa succede quando l'utente preme il pulsante Indietro nel riquadro dei dettagli e questo comportamento varia a seconda che sia visibile un riquadro o due.

  1. Supporta la navigazione indietro aggiungendo il seguente codice.

ReplyApp.kt

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ReplyAppContent(
    replyHomeUIState: ReplyHomeUIState,
    onEmailClick: (Email) -> Unit,
) {
    val navigator = rememberListDetailPaneScaffoldNavigator<Long>()

    BackHandler(navigator.canNavigateBack()) {
        navigator.navigateBack()
    }

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane = {
            AnimatedPane {
                ReplyListPane(
                    replyHomeUIState = replyHomeUIState,
                    onEmailClick = { email ->
                        onEmailClick(email)
                        navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, email.id)
                    }
                )
            }
        },
        detailPane = {
            AnimatedPane {
                if (replyHomeUIState.selectedEmail != null) {
                    ReplyDetailPane(replyHomeUIState.selectedEmail)
                }
            }
        }
    )
}

Il navigatore conosce lo stato completo di ListDetailPaneScaffold, se è possibile tornare indietro e cosa fare in tutti questi scenari. Questo codice crea un BackHandler che viene attivato ogni volta che il navigatore può tornare indietro e all'interno della lambda chiama navigateBack(). Inoltre, per rendere la transizione tra i riquadri molto più fluida, ogni riquadro è racchiuso in un composable AnimatedPane().

Esegui di nuovo l'app su un emulatore ridimensionabile per tutti i diversi tipi di dispositivi e nota che ogni volta che la configurazione dello schermo cambia o apri un dispositivo pieghevole, la navigazione e i contenuti dello schermo cambiano dinamicamente in risposta alle modifiche dello stato del dispositivo. Prova anche a toccare le email nel riquadro dell'elenco e a vedere come si comporta il layout su schermi diversi, mostrando entrambi i riquadri affiancati o animando il passaggio da uno all'altro in modo fluido.

Mostra le modifiche di adattabilità per dispositivi di dimensioni diverse.

Congratulazioni, hai reso la tua app adattabile a tutti i tipi di stati e dimensioni dei dispositivi. Prova a eseguire l'app su dispositivi pieghevoli, tablet o altri dispositivi mobili.

6. Complimenti

Complimenti! Hai completato questo codelab e hai imparato a rendere le app adattive con Jetpack Compose.

Hai imparato a controllare le dimensioni e lo stato di chiusura di un dispositivo e ad aggiornare di conseguenza l'interfaccia utente, la navigazione e altre funzioni della tua app. Hai anche scoperto come l'adattabilità migliora la raggiungibilità e l'esperienza utente.

Passaggi successivi

Dai un'occhiata agli altri codelab nel percorso di apprendimento di Compose.

App di esempio

Documenti di riferimento