Adaptive Navigation erstellen

Die meisten Apps haben einige Ziele der obersten Ebene, auf die über die primäre Navigations-UI der App zugegriffen werden kann. In kompakten Fenstern, z. B. auf einem Standard-Smartphone-Display, werden die Ziele in der Regel in einer Navigationsleiste unten im Fenster angezeigt. In einem maximierten Fenster, z. B. einer Vollbild-App auf einem Tablet, ist eine Navigationsleiste neben der App in der Regel die bessere Wahl, da die Navigationssteuerungen leichter zu erreichen sind, wenn Sie das Gerät an den linken und rechten Seiten halten.

NavigationSuiteScaffold vereinfacht das Wechseln zwischen Navigations-UIs, indem die entsprechende Navigations-UI-Komponente basierend auf WindowSizeClass angezeigt wird. Dazu gehört auch die dynamische Änderung der Benutzeroberfläche bei Änderungen der Fenstergröße während der Laufzeit. Standardmäßig wird eine der folgenden UI-Komponenten angezeigt:

  • Navigationsleiste, wenn die Breite oder Höhe kompakt ist oder das Gerät im Querformat gehalten wird
  • Navigationsleiste für alle anderen Elemente
Abbildung 1. NavigationSuiteScaffold zeigt in kompakten Fenstern eine Navigationsleiste an.
Abbildung 2. NavigationSuiteScaffold zeigt in maximierten Fenstern einen Navigationsstreifen an.

Abhängigkeiten hinzufügen

NavigationSuiteScaffold ist Teil der Bibliothek Material3 adaptive navigation suite. Fügen Sie in der build.gradle-Datei Ihrer App oder Ihres Moduls eine Abhängigkeit für die Bibliothek hinzu:

Kotlin

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

Groovy

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

Scaffolding erstellen

Die beiden Hauptbestandteile von NavigationSuiteScaffold sind die Navigationssuite-Elemente und die Inhalte für das ausgewählte Ziel. Sie können die Navigationssuite-Elemente direkt in einem Composeable definieren. Es ist jedoch üblich, sie an anderer Stelle zu definieren, z. B. in einem Enum:

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),
}

Wenn Sie NavigationSuiteScaffold verwenden möchten, müssen Sie das aktuelle Ziel erfassen. Dazu können Sie rememberSaveable verwenden:

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

Im folgenden Beispiel wird mit der Funktion item des Parameters navigationSuiteItems (Typ NavigationSuiteScope) die Navigations-UI für ein einzelnes Ziel definiert. Die Ziel-UI wird in Navigationsleisten, Sidebars und Ausklappern verwendet. Um Navigationselemente zu erstellen, durchlaufen Sie AppDestinations (definiert im vorherigen Snippet):

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.
}

Im Lambda-Code für den Zielinhalt kannst du mit dem Wert currentDestination festlegen, welche Benutzeroberfläche angezeigt werden soll. Wenn Sie in Ihrer App eine Navigationsbibliothek verwenden, verwenden Sie sie hier, um das entsprechende Ziel anzuzeigen. Eine WHEN-Anweisung kann in folgenden Fällen ausreichen:

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

Farben ändern

Mit NavigationSuiteScaffold wird ein Surface über den gesamten Bereich erstellt, den das Gerüst einnimmt, in der Regel das gesamte Fenster. Außerdem wird mit dem Scaffolding die Navigationsoberfläche gezeichnet, z. B. ein NavigationBar. Sowohl die Oberfläche als auch die Navigations-UI verwenden die im Design Ihrer App angegebenen Werte. Sie können die Designwerte jedoch überschreiben.

Der Parameter containerColor gibt die Farbe der Oberfläche an. Standardmäßig ist das die Hintergrundfarbe Ihres Farbschemas. Der Parameter contentColor gibt die Farbe des Inhalts auf dieser Oberfläche an. Standardmäßig ist die Farbe „An“ der Farbe für containerColor zugewiesen. Wenn für containerColor beispielsweise die Farbe background verwendet wird, wird für contentColor die Farbe onBackground verwendet. Weitere Informationen zur Funktionsweise des Farbsystems finden Sie unter Material Design 3-Designvorlagen in Compose. Wenn Sie diese Werte überschreiben, verwenden Sie Werte, die in Ihrem Design definiert sind, damit Ihre App sowohl dunkle als auch helle Anzeigemodi unterstützt:

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

Die Navigations-UI wird vor der NavigationSuiteScaffold-Oberfläche gezeichnet. Die Standardwerte für die UI-Farben werden von NavigationSuiteDefaults.colors() bereitgestellt. Sie können diese Werte aber auch überschreiben. Wenn Sie beispielsweise möchten, dass der Hintergrund der Navigationsleiste transparent ist, die anderen Werte aber die Standardwerte sind, überschreiben Sie navigationBarContainerColor:

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

Sie können jeden Artikel in der Navigations-UI anpassen. Wenn Sie die Funktion item aufrufen, können Sie eine Instanz von NavigationSuiteItemColors übergeben. Die Klasse gibt die Farben für Elemente in einer Navigationsleiste, einer Navigationsleiste und einem Navigationsschubfach an. Das bedeutet, dass Sie für jeden Navigations-UI-Typ dieselben Farben verwenden können oder die Farben je nach Bedarf variieren können. Definieren Sie die Farben auf NavigationSuiteScaffold-Ebene, um dieselbe Objektinstanz für alle Elemente zu verwenden, und rufen Sie die Funktion NavigationSuiteDefaults.itemColors() auf, um nur die Farben zu überschreiben, die Sie ändern möchten:

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...
}

Navigationstypen anpassen

Beim Standardverhalten von NavigationSuiteScaffold ändert sich die Navigations-UI basierend auf Fenstergrößenklassen. Sie können dieses Verhalten jedoch auch überschreiben. Wenn Ihre App beispielsweise einen einzigen großen Inhaltsbereich für einen Feed anzeigt, könnte sie für maximierte Fenster einen permanenten Navigationsleisten verwenden, aber für kompakte und mittelgroße Fenstergrößenklassen auf das Standardverhalten zurückgreifen:

val adaptiveInfo = currentWindowAdaptiveInfo()
val customNavSuiteType = with(adaptiveInfo) {
    if (windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.EXPANDED) {
        NavigationSuiteType.NavigationDrawer
    } else {
        NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo(adaptiveInfo)
    }
}

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

Weitere Informationen