Adaptive Navigation erstellen

Die meisten Apps haben einige Ziele der obersten Ebene, die über die primäre Navigations-UI der App zugänglich sind. In kompakten Fenstern, z. B. auf einem Standard-Smartphone-Display, werden die Ziele normalerweise 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 Navigationssteuerelemente leichter zu erreichen sind, wenn das Gerät links und rechts gehalten wird.

NavigationSuiteScaffold vereinfacht das Umschalten zwischen Navigations-UIs, indem das entsprechende zusammensetzbare Navigations-UI basierend auf WindowSizeClass angezeigt wird. Dazu gehört auch, dass die Benutzeroberfläche während der Laufzeit dynamisch an Änderungen der Fenstergröße angepasst wird. Standardmäßig wird eine der folgenden UI-Komponenten angezeigt:

  • Navigationsleiste, wenn die Breite oder Höhe kompakt ist oder sich das Gerät in der Tischposition befindet
  • Navigationsleiste für alles andere
Abbildung 1. NavigationSuiteScaffold zeigt eine Navigationsleiste in kompakten Fenstern an.
Abbildung 2. NavigationSuiteScaffold zeigt einen Navigationsstreifen in maximierten Fenstern an.

Abhängigkeiten hinzufügen

NavigationSuiteScaffold ist Teil der Bibliothek Material3 Adaptive Navigation Suite. Fügen Sie 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'

Gerüst erstellen

Die beiden Hauptbereiche von NavigationSuiteScaffold sind die Elemente der Navigationsleiste und die Inhalte für das ausgewählte Ziel. Sie können die Elemente der Navigationsleiste direkt in einem Composable 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 Zielvorhaben erfassen. Dazu können Sie rememberSaveable verwenden:

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

Im folgenden Beispiel wird mit dem Parameter navigationSuiteItems (Typ NavigationSuiteScope) die Navigations-UI für ein einzelnes Ziel definiert. Dazu wird die Funktion item verwendet. Die Ziel-UI wird in Navigationsleisten, Rails und Drawers verwendet. Um Navigationselemente zu erstellen, durchlaufen Sie Ihre AppDestinations (die im vorherigen Snippet definiert ist):

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

Verwenden Sie in der Lambda-Funktion für Zielinhalte den currentDestination-Wert, um zu entscheiden, welche Benutzeroberfläche angezeigt werden soll. Wenn Sie in Ihrer App eine Navigationsbibliothek verwenden, können Sie sie hier nutzen, um das entsprechende Ziel anzuzeigen. Eine „when“-Anweisung kann 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 für den gesamten Bereich erstellt, den das Gerüst einnimmt, in der Regel das gesamte Fenster. Außerdem wird mit dem Scaffold die jeweilige Navigations-UI gezeichnet, z. B. eine NavigationBar. Sowohl die Oberfläche als auch die Navigations-UI verwenden die in Ihrem App-Theme angegebenen Werte. Sie können die Theme-Werte jedoch überschreiben.

Mit dem Parameter containerColor wird die Farbe der Oberfläche angegeben. Standardmäßig wird die Hintergrundfarbe Ihres Farbschemas verwendet. Mit dem Parameter contentColor wird die Farbe von Inhalten auf dieser Oberfläche angegeben. Die Standardeinstellung ist die „on“-Farbe der für containerColor angegebenen Farbe. Wenn beispielsweise containerColor die Farbe background verwendet, wird für contentColor die Farbe onBackground verwendet. Weitere Informationen zur Funktionsweise des Farbsystems finden Sie unter Material 3-Theming in Compose. Wenn Sie diese Werte überschreiben, verwenden Sie Werte, die in Ihrem Theme definiert sind, damit Ihre App den dunklen und hellen Anzeigemodus unterstützt:

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

Die Navigations-Benutzeroberfläche wird vor der NavigationSuiteScaffold-Oberfläche dargestellt. 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 sein sollen, überschreiben Sie navigationBarContainerColor:

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

Letztendlich können Sie jedes Element 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 Navigationsspalte und einem Navigationsbereich an. Das bedeutet, dass Sie für alle Arten von Navigations-Benutzeroberflächen identische Farben verwenden oder die Farben nach Bedarf variieren können. Definieren Sie die Farben auf der Ebene NavigationSuiteScaffold, um für alle Elemente dieselbe Objektinstanz zu verwenden, und rufen Sie die Funktion NavigationSuiteDefaults.itemColors() auf, um nur die Elemente 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

Das Standardverhalten von NavigationSuiteScaffold ändert die Navigations-UI basierend auf Fenstergrößenklassen. Sie können dieses Verhalten jedoch überschreiben. Wenn Ihre App beispielsweise einen einzelnen großen Bereich mit Inhalten für einen Feed anzeigt, kann sie für maximierte Fenster einen permanenten Navigationsbereich verwenden, aber trotzdem auf das Standardverhalten für kompakte und mittelgroße Fenster zurückgreifen:

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

Zusätzliche Ressourcen