Crea app adattive con Jetpack Compose

1. Introduzione

In questo codelab imparerai a creare app adattive per smartphone, tablet e pieghevoli e in che modo migliorano la connettività con Jetpack Compose. Scoprirai anche le best practice per l'utilizzo dei componenti e dei temi di Material 3.

Prima di entrare nel dettaglio dell'argomento, è importante capire cosa intendiamo per adattabilità.

Adattabilità

L'interfaccia utente dell'app deve essere adattabile per tenere conto di dimensioni, orientamenti e fattori di forma diversi delle finestre. Un layout adattivo cambia in base allo spazio sullo schermo a disposizione. Queste modifiche spaziano da semplici modifiche al layout per riempire lo spazio, scegliendo i rispettivi stili di navigazione, fino alla modifica completa dei layout per sfruttare spazio aggiuntivo.

Per scoprire di più, dai un'occhiata a Progettazione adattiva.

In questo codelab, esplorerai come usare e come pensare all'adattabilità quando usi Jetpack Compose. Realizzi un'applicazione, chiamata Reply, che mostra come implementare l'adattabilità per tutti i tipi di schermi e come l'adattabilità e la raggiungibilità agiscono congiuntamente per offrire agli utenti un'esperienza ottimale.

Obiettivi didattici

  • Come progettare un'app in modo che abbia come target tutte le dimensioni delle finestre con Jetpack Compose.
  • Come scegliere come target della tua app diversi pieghevoli.
  • Come utilizzare diversi tipi di navigazione per una maggiore raggiungibilità e accessibilità.
  • Come utilizzare i componenti di Material 3 per offrire la migliore esperienza possibile su ogni dimensione di 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 di modificare le dimensioni della finestra.

Emulatore ridimensionabile con opzioni per smartphone, dispositivo aperto, tablet e computer.

Se non hai dimestichezza con Compose, valuta la possibilità di seguire il codelab 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.

La dimostrazione del supporto di più dispositivi che realizzerai 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 passo secondo i tuoi tempi.

Apri il progetto in Android Studio

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

Esplora il codice di avvio

Il codice del ramo main contiene il pacchetto ui. In questo pacchetto lavorerai con i seguenti file:

  • MainActivity.kt: attività dell'entry point in cui avvii l'app.
  • ReplyApp.kt: contiene gli elementi componibili dell'interfaccia utente della schermata principale.
  • ReplyHomeViewModel.kt: fornisce i dati e lo stato dell'interfaccia utente per i contenuti dell'app.
  • ReplyListContent.kt: contiene composabili per fornire elenchi e schermate dei dettagli.

Se esegui questa app su un emulatore ridimensionabile e provi diversi tipi di dispositivi, come uno smartphone o un tablet, l'interfaccia utente si espande semplicemente nello spazio a disposizione anziché sfruttare lo spazio sullo schermo o offrire ergonomia di raggiungibilità.

Schermata iniziale sullo smartphone

Visualizzazione iniziale allungata sul tablet

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

3. Rendere le app adattabili

Questa sezione illustra cosa significa rendere le app adattabili e quali componenti di Material 3 sono disponibili per semplificare questa operazione. Inoltre, copre i tipi di schermi e stati che sceglierai come target, tra cui smartphone, tablet, tablet di grandi dimensioni e pieghevoli.

Inizierai illustrando le nozioni di base sulle dimensioni delle finestre, sulla postura dei piegamenti e sui diversi tipi di opzioni di navigazione. Puoi quindi utilizzare queste API nella tua app per renderla più adattabile.

Dimensioni delle finestre

I dispositivi Android sono disponibili in tutte le forme e dimensioni, dagli smartphone ai dispositivi pieghevoli, ai tablet e ai dispositivi ChromeOS. Per supportare il maggior numero possibile di dimensioni delle finestre, l'interfaccia utente deve essere adattabile e reattiva. Per aiutarti a trovare la soglia giusta per modificare l'interfaccia utente della tua app, abbiamo definito valori di breakpoint che consentono di classificare i dispositivi in classi di dimensioni predefinite (compatta, media ed espansa), chiamate classi di dimensioni della finestra. Si tratta di un insieme di breakpoint del viewport basati su opinioni che ti aiutano a progettare, sviluppare e testare layout di applicazioni adattabili e responsive.

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

WindowwidthSizeClass per una larghezza compatta, media ed estesa.

WindowHeightSizeClass per altezza compatta, media ed espansa.

Sia la larghezza che l'altezza sono 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 per 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 a causa delle dimensioni variabili e della presenza di cerniere. I cardini possono coprire parte del display, rendendo l'area non adatta alla visualizzazione dei contenuti. Potrebbero anche essere separati, il che significa che ci sono due display fisici separati quando il dispositivo è aperto.

Posture pieghevoli, piatte e semiaperte

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

Scopri di più su posture e cerniere per il ripiegamento.

Tutti questi aspetti devono essere presi in considerazione quando si implementano layout adattabili che supportano i dispositivi pieghevoli.

Ricevere informazioni adattive

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

  1. Aggiungi le voci per questo elemento 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 compilazione del modulo dell'app, aggiungi la dipendenza della nuova libreria ed esegui una sincronizzazione Gradle:

app/build.gradle.kts

dependencies {

    implementation(libs.androidx.material3.adaptive)
}

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

Puoi provare subito questa funzionalità in MainActivity.

  1. In onCreate() all'interno del blocco ReplyTheme, ottieni le informazioni di adattamento della finestra e mostra le classi di dimensioni in un composable 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 delle dimensioni della finestra verranno stampate sui contenuti dell'app. Non esitare a esplorare cosa altro viene fornito nelle informazioni di adattamento della finestra. Successivamente, 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 in base alle dimensioni e allo stato del dispositivo per semplificarne l'utilizzo.

Quando gli utenti tengono in mano uno smartphone, in genere i loro polpastrelli si trovano nella parte inferiore dello schermo. Quando gli utenti tengono un dispositivo pieghevole aperto o un tablet, le dita sono in genere vicine ai lati. Gli utenti devono essere in grado di navigare o avviare un'interazione con un'app senza dover assumere posizioni estreme delle mani o cambiare il loro posizionamento.

Mentre progetti la tua app e decidi dove posizionare gli elementi di interfaccia utente interattivi nel layout, considera le implicazioni ergonomiche delle diverse aree dello schermo.

  • Quali aree puoi raggiungere comodamente quando tieni il dispositivo?
  • Quali aree possono essere raggiunte solo allungando le dita, il che potrebbe essere sconveniente?
  • Quali aree sono difficili da raggiungere o sono lontane da dove l'utente tiene il dispositivo?

La navigazione è la prima cosa con cui gli utenti interagiscono e contiene azioni di importanza elevata relative ai percorsi critici degli utenti, pertanto dovrebbe essere collocata nelle aree più facili da raggiungere. La libreria Material Adaptive fornisce diversi componenti che ti aiutano a implementare la navigazione, a seconda della classe delle dimensioni della finestra del dispositivo.

Menu di navigazione in basso

Il menu di navigazione in basso è perfetto per i dispositivi compatti, perché li teniamo in modo naturale in modo che il pollice possa raggiungere facilmente tutti i punti di contatto del menu di navigazione in basso. Utilizzalo ogni volta che hai un dispositivo di dimensioni compatte o un dispositivo pieghevole in uno stato compatto piegato.

Barra di navigazione inferiore con elementi

Per una finestra di larghezza media, la barra di navigazione è ideale per la raggiungibilità, in quanto il pollice si appoggia naturalmente sul lato del dispositivo. Puoi anche combinare una barra di navigazione con un riquadro di navigazione per mostrare più informazioni.

Barra di navigazione con elementi

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

Riquadro di navigazione a scomparsa modale

Puoi utilizzare un riquadro a scomparsa di navigazione modale per telefoni e tablet di dimensioni medie e compatte, che 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 permanente

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

Riquadro di navigazione a scomparsa permanente con elementi

Implementare la navigazione dinamica

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

Al momento, l'app mostra sempre un NavigationBar sotto i contenuti dello schermo, indipendentemente dallo stato del dispositivo. In alternativa, puoi utilizzare il componente Materiale NavigationSuiteScaffold per passare automaticamente da un componente di navigazione all'altro in base a informazioni come l'attuale classe delle dimensioni della finestra.

  1. Aggiungi la dipendenza Gradle per ottenere questo componente aggiornando il catalogo delle versioni e lo script di compilazione 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 composable ReplyNavigationWrapper() in ReplyApp.kt e sostituisci Column e i relativi contenuti con 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 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: la barra di navigazione si trasformerà in una barra di navigazione e viceversa.

In finestre molto ampie, ad esempio su un tablet in orizzontale, ti consigliamo di mostrare il riquadro di navigazione permanente. NavigationSuiteScaffold supporta la visualizzazione di un riquadro permanente, anche se non è mostrato in nessuno dei valori WindowWidthSizeClass attuali. Tuttavia, puoi farlo 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 prima 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, viene utilizzato NavigationSuiteType.NavigationDrawer. In caso contrario, viene utilizzato il calcolo predefinito.

Quando esegui di nuovo l'app sull'emulatore ridimensionabile e provi tipi diversi, tieni presente 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 dell&#39;adattabilità per dimensioni diverse dei dispositivi.

Congratulazioni. Ora conosci i diversi tipi di navigazione per supportare differenti dimensioni e stati delle finestre.

Nella sezione successiva scoprirai come sfruttare l'area dello schermo rimanente anziché estendere lo stesso elemento dell'elenco da un lato all'altro.

5. Utilizzo dello spazio sullo schermo

Indipendentemente dal fatto che l'app venga eseguita su un tablet piccolo, un dispositivo non piegato o un tablet grande, lo schermo viene allungato per riempire lo spazio rimanente. Devi assicurarti di poter sfruttare lo spazio sullo schermo per mostrare più informazioni, ad esempio, in questa app, mostrare email e thread agli utenti nella stessa pagina.

Material 3 definisce tre layout canonici, ciascuno con configurazioni per classi di dimensioni delle finestre compatte, medie ed espanse. Il layout canonico Dettagli elenco è perfetto per questo caso d'uso ed è disponibile in Componi 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, dovrai anche aggiungere 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 prima un navigatore utilizzando rememberListDetailPaneNavigator(). Il navigatore offre un certo controllo sul riquadro visualizzato e sui contenuti da rappresentare 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: la direttiva di struttura e il valore della struttura. Per ottenere il comportamento predefinito, questo codice utilizza la direttiva di struttura e il valore di struttura fornito dal navigatore.

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

Esegui l'app e imposta la visualizzazione dell'emulatore su pieghevole o tablet (potrebbe anche essere necessario modificare l'orientamento) per vedere il layout a due riquadri. Sembra già molto meglio di prima.

Ora esaminiamo alcuni dei comportamenti desiderati per 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 il tocco di un elemento non produce alcun effetto. Il posto migliore per conservare queste informazioni è con il resto dello stato dell'interfaccia utente 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() che viene chiamata quando l'utente tocca una voce dell'elenco. Modifica questa funzione per copiare lo stato dell'interfaccia utente 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 dovrebbe essere visualizzato nel riquadro dei dettagli? Esistono diversi modi per gestire questa situazione, ad esempio mostrando per impostazione predefinita il primo elemento dell'elenco.

  1. Nello stesso file, modifica la funzione observeEmails(). Quando l'elenco di email è stato caricato, se per lo stato precedente dell'interfaccia utente non era stata selezionata un'email, impostala 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 e imposta l'emulatore sulle dimensioni del tablet. Tocca un elemento dell'elenco per verificare che i contenuti del riquadro dei dettagli vengano aggiornati.

Questo funziona perfettamente quando entrambi i riquadri sono visibili, ma quando la finestra ha spazio per mostrare un solo riquadro, sembra che non accada nulla quando tocchi un elemento. Prova a passare dalla visualizzazione dell'emulatore a uno smartphone o a un dispositivo pieghevole in verticale e nota che è visibile solo il riquadro elenco anche dopo aver toccato un elemento. Questo accade perché, anche se l'email selezionata viene aggiornata, il ListDetailPaneScaffold mantiene lo stato attivo nel 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)
            }
        )
    },
    // ...
)

Questo lambda utilizza il navigatore creato in precedenza per aggiungere un comportamento aggiuntivo quando viene fatto clic su un elemento. Chiamerà la funzione lambda originale passata a questa funzione e poi chiamerà anche navigator.navigateTo() specificando quale riquadro deve essere mostrato. A ogni riquadro dello schema è associato un ruolo e, per il riquadro dei dettagli, è ListDetailPaneScaffoldRole.Detail. Nelle finestre più piccole, sembrerà che l'app abbia eseguito la navigazione in avanti.

L'app deve anche gestire ciò che accade quando l'utente preme il pulsante Indietro dal riquadro dei dettagli e questo comportamento sarà diverso a seconda che siano visibili uno o due riquadri.

  1. Supporta la navigazione a ritroso 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 del 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 delle chiamate lambda 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 tieni presente 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 schermate diverse, mostrando entrambi i riquadri affiancati o passando da uno all'altro in modo fluido.

Mostra le modifiche dell&#39;adattabilità per dimensioni diverse dei dispositivi.

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 creare app adattabili 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 dell'app. Hai anche imparato in che modo l'adattabilità migliora la raggiungibilità e l'esperienza utente.

Passaggi successivi

Dai un'occhiata agli altri codelab sul percorso di scrittura.

App di esempio

  • I compositi di Sample sono una raccolta di molte app che incorporano le best practice spiegate nei codelab.

Documenti di riferimento