Creare una navigazione adattiva

La maggior parte delle app ha alcune destinazioni di primo livello accessibili tramite l'interfaccia utente di navigazione principale dell'app. Nelle finestre compatte, come lo schermo di uno smartphone standard, le destinazioni vengono in genere visualizzate in una barra di navigazione nella parte inferiore della finestra. In una finestra espansa, ad esempio un'app a schermo intero su un tablet, una barra di navigazione accanto all'app è in genere una scelta migliore, poiché i controlli di navigazione sono più facili da raggiungere tenendo i lati sinistro e destro del dispositivo.

NavigationSuiteScaffold semplifica il passaggio tra le UI di navigazione visualizzando il composable dell'UI di navigazione appropriato in base a WindowSizeClass. Ciò include la modifica dinamica dell'interfaccia utente durante le modifiche alle dimensioni della finestra di runtime. Il comportamento predefinito consiste nel mostrare uno dei seguenti componenti dell'interfaccia utente:

  • Barra di navigazione se la larghezza o l'altezza è compatta o se il dispositivo è in modalità tavolo
  • Modalità di navigazione laterale per tutto il resto
Figura 1. NavigationSuiteScaffold mostra una barra di navigazione nelle finestre compatte.
Figura 2. NavigationSuiteScaffold mostra una barra di navigazione nelle finestre espanse.

Aggiungi dipendenze

NavigationSuiteScaffold fa parte della libreria Material3 adaptive navigation suite. Aggiungi una dipendenza per la libreria nel file build.gradle della tua app o del tuo modulo:

Kotlin

implementation("androidx.compose.material3:material3-adaptive-navigation-suite")

Trendy

implementation 'androidx.compose.material3:material3-adaptive-navigation-suite'

Creare una struttura

Le due parti principali di NavigationSuiteScaffold sono gli elementi della suite di navigazione e i contenuti per la destinazione selezionata. Puoi definire direttamente gli elementi della suite di navigazione in un componente componibile, ma è comune definirli altrove, ad esempio in un'enumerazione:

enum class AppDestinations(
    @StringRes val label: Int,
    val icon: ImageVector,
    @StringRes val contentDescription: Int
) {
    HOME(R.string.home, Icons.Default.Home, R.string.home),
    FAVORITES(R.string.favorites, Icons.Default.Favorite, R.string.favorites),
    SHOPPING(R.string.shopping, Icons.Default.ShoppingCart, R.string.shopping),
    PROFILE(R.string.profile, Icons.Default.AccountBox, R.string.profile),
}

Per utilizzare NavigationSuiteScaffold, devi monitorare la destinazione corrente, cosa che puoi fare utilizzando rememberSaveable:

var currentDestination by rememberSaveable { mutableStateOf(AppDestinations.HOME) }

Nell'esempio seguente, il parametro navigationSuiteItems (tipo NavigationSuiteScope) utilizza la funzione item per definire l'interfaccia utente di navigazione per una singola destinazione. L'interfaccia utente di destinazione viene utilizzata in barre di navigazione, guide e riquadri. Per creare elementi di navigazione, esegui un ciclo iterativo su AppDestinations (definito nello snippet precedente):

NavigationSuiteScaffold(
    navigationSuiteItems = {
        AppDestinations.entries.forEach {
            item(
                icon = {
                    Icon(
                        it.icon,
                        contentDescription = stringResource(it.contentDescription)
                    )
                },
                label = { Text(stringResource(it.label)) },
                selected = it == currentDestination,
                onClick = { currentDestination = it }
            )
        }
    }
) {
    // TODO: Destination content.
}

All'interno della lambda dei contenuti di destinazione, utilizza il valore currentDestination per decidere quale UI visualizzare. Se utilizzi una libreria di navigazione nella tua app, usala qui per visualizzare la destinazione appropriata. Può essere sufficiente un'istruzione when:

NavigationSuiteScaffold(
    navigationSuiteItems = { /*...*/ }
) {
    // Destination content.
    when (currentDestination) {
        AppDestinations.HOME -> HomeDestination()
        AppDestinations.FAVORITES -> FavoritesDestination()
        AppDestinations.SHOPPING -> ShoppingDestination()
        AppDestinations.PROFILE -> ProfileDestination()
    }
}

Cambia colori

NavigationSuiteScaffold crea un Surface sull'intera area occupata dallo scaffold, in genere l'intera finestra. Inoltre, lo scaffold disegna la particolare UI di navigazione, ad esempio un NavigationBar. Sia la superficie che la UI di navigazione utilizzano i valori specificati nel tema della tua app, ma puoi sostituirli.

Il parametro containerColor specifica il colore della superficie. Il valore predefinito è il colore di sfondo della combinazione di colori. Il parametro contentColor specifica il colore dei contenuti su quella superficie. Il valore predefinito è il colore "on" di ciò che è specificato per containerColor. Ad esempio, se containerColor utilizza il colore background, contentColor utilizza il colore onBackground. Per maggiori dettagli sul funzionamento del sistema di colori, consulta Temi Material Design 3 in Compose. Quando esegui l'override di questi valori, utilizza i valori definiti nel tema in modo che l'app supporti le modalità di visualizzazione scura e chiara:

NavigationSuiteScaffold(
    navigationSuiteItems = { /* ... */ },
    containerColor = MaterialTheme.colorScheme.primary,
    contentColor = MaterialTheme.colorScheme.onPrimary,
) {
    // Content...
}

La UI di navigazione viene disegnata davanti alla superficie NavigationSuiteScaffold. I valori predefiniti per i colori dell'interfaccia utente sono forniti da NavigationSuiteDefaults.colors(), ma puoi anche sostituirli. Ad esempio, se vuoi che lo sfondo della barra di navigazione sia trasparente, ma che gli altri valori siano quelli predefiniti, esegui l'override di navigationBarContainerColor:

NavigationSuiteScaffold(
    navigationSuiteItems = { /* ... */ },
    navigationSuiteColors = NavigationSuiteDefaults.colors(
        navigationBarContainerColor = Color.Transparent,
    )
) {
    // Content...
}

In definitiva, puoi personalizzare ogni elemento nell'interfaccia utente di navigazione. Quando chiami la funzione item, puoi passare un'istanza di NavigationSuiteItemColors. La classe specifica i colori degli elementi in una barra di navigazione, una barra di navigazione laterale e un riquadro di navigazione. Ciò significa che puoi avere colori identici in ogni tipo di UI di navigazione oppure puoi variare i colori in base alle tue esigenze. Definisci i colori a livello di NavigationSuiteScaffold per utilizzare la stessa istanza dell'oggetto per tutti gli elementi e chiama la funzione NavigationSuiteDefaults.itemColors() per sostituire solo quelli che vuoi modificare:

val myNavigationSuiteItemColors = NavigationSuiteDefaults.itemColors(
    navigationBarItemColors = NavigationBarItemDefaults.colors(
        indicatorColor = MaterialTheme.colorScheme.primaryContainer,
        selectedIconColor = MaterialTheme.colorScheme.onPrimaryContainer
    ),
)

NavigationSuiteScaffold(
    navigationSuiteItems = {
        AppDestinations.entries.forEach {
            item(
                icon = {
                    Icon(
                        it.icon,
                        contentDescription = stringResource(it.contentDescription)
                    )
                },
                label = { Text(stringResource(it.label)) },
                selected = it == currentDestination,
                onClick = { currentDestination = it },
                colors = myNavigationSuiteItemColors,
            )
        }
    },
) {
    // Content...
}

Personalizzare i tipi di navigazione

Il comportamento predefinito di NavigationSuiteScaffold modifica l'interfaccia utente di navigazione in base alle classi di dimensioni della finestra. Tuttavia, potresti voler sovrascrivere questo comportamento. Ad esempio, se la tua app mostra un unico riquadro di contenuti di grandi dimensioni per un feed, l'app potrebbe utilizzare un riquadro di navigazione permanente per le finestre espanse, ma comunque ripristinare il comportamento predefinito per le classi di dimensioni delle finestre compatte e medie:

val adaptiveInfo = currentWindowAdaptiveInfo()
val customNavSuiteType = with(adaptiveInfo) {
    if (windowSizeClass.isWidthAtLeastBreakpoint(WIDTH_DP_EXPANDED_LOWER_BOUND)) {
        NavigationSuiteType.NavigationDrawer
    } else {
        NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo(adaptiveInfo)
    }
}

NavigationSuiteScaffold(
    navigationSuiteItems = { /* ... */ },
    layoutType = customNavSuiteType,
) {
    // Content...
}

Risorse aggiuntive