Создайте адаптивную навигацию

В большинстве приложений есть несколько пунктов назначения верхнего уровня, доступных через основной навигационный интерфейс приложения. В компактных окнах, например, на стандартном экране телефона, пункты назначения обычно отображаются на панели навигации в нижней части окна. В расширенном окне, например, в полноэкранном приложении на планшете, навигационная панель рядом с приложением обычно предпочтительнее, поскольку до элементов управления навигацией проще дотянуться, удерживая устройство за левую и правую стороны.

NavigationSuiteScaffold упрощает переключение между навигационными пользовательскими интерфейсами, отображая соответствующий навигационный пользовательский интерфейс, компонуемый на основе WindowSizeClass . Это включает динамическое изменение пользовательского интерфейса при изменении размера окна во время выполнения. По умолчанию отображается один из следующих компонентов пользовательского интерфейса:

  • Панель навигации , если ширина или высота компактны или если устройство находится в настольном положении
  • Навигационная планка для всего остального
Рисунок 1. NavigationSuiteScaffold отображает панель навигации в компактных окнах.
Рисунок 2. NavigationSuiteScaffold отображает навигационную панель в развернутых окнах.

Добавить зависимости

NavigationSuiteScaffold входит в состав библиотеки адаптивной навигации Material3 . Добавьте зависимость от библиотеки в файл build.gradle вашего приложения или модуля:

Котлин

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

Круто

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

Создайте подмости

Две основные части NavigationSuiteScaffold — это элементы набора навигации и содержимое выбранного пункта назначения. Вы можете напрямую определить элементы набора навигации в компонуемом объекте, но обычно они определяются в другом месте, например, в перечислении:

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

Чтобы использовать NavigationSuiteScaffold , необходимо отслеживать текущий пункт назначения, что можно сделать с помощью rememberSaveable :

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

В следующем примере параметр navigationSuiteItems (тип NavigationSuiteScope ) использует свою функцию item для определения навигационного интерфейса для отдельного пункта назначения. Этот интерфейс используется для всех панелей навигации, направляющих и выдвижных элементов. Для создания элементов навигации необходимо перебрать в цикле объекты AppDestinations (определённые в предыдущем фрагменте):

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

В лямбда-функции, задающей содержимое назначения, используйте значение currentDestination , чтобы определить, какой пользовательский интерфейс отображать. Если в вашем приложении используется библиотека навигации, используйте её здесь для отображения соответствующего назначения. Оператора when может быть достаточно:

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

Изменить цвета

NavigationSuiteScaffold создаёт Surface охватывающую всю область, занимаемую скаффолдом, обычно всё окно. Поверх неё скаффолд отображает определённый навигационный интерфейс, например NavigationBar . И поверхность, и навигационный интерфейс используют значения, заданные в теме вашего приложения, но вы можете переопределить значения темы.

Параметр containerColor задаёт цвет поверхности. Значение по умолчанию — цвет фона вашей цветовой схемы. Параметр contentColor задаёт цвет содержимого на этой поверхности. Значение по умолчанию — цвет «включения» для любого цвета, указанного для containerColor . Например, если containerColor использует цвет background , то contentColor использует цвет onBackground . Подробнее о работе цветовой системы см. в разделе «Темы Material Design 3» в Compose . При переопределении этих значений используйте значения, определённые в вашей теме, чтобы ваше приложение поддерживало тёмный и светлый режимы отображения:

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

Интерфейс навигации отображается поверх поверхности NavigationSuiteScaffold . Значения цветов интерфейса по умолчанию задаются методом NavigationSuiteDefaults.colors() , но вы также можете переопределить эти значения. Например, если вы хотите, чтобы фон панели навигации был прозрачным, а остальные значения оставались по умолчанию, переопределите navigationBarContainerColor :

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

В конечном счёте, вы можете настроить каждый элемент навигационного интерфейса. При вызове функции item вы можете передать экземпляр NavigationSuiteItemColors . Этот класс определяет цвета элементов панели навигации, навигационной направляющей и навигационной панели. Это означает, что вы можете использовать одинаковые цвета для каждого типа навигационного интерфейса или изменять их в зависимости от ваших потребностей. Определите цвета на уровне NavigationSuiteScaffold , чтобы использовать один и тот же экземпляр объекта для всех элементов, и вызовите функцию NavigationSuiteDefaults.itemColors() чтобы переопределить только те, которые вы хотите изменить:

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

Настройте типы навигации

Поведение NavigationSuiteScaffold по умолчанию изменяет навигационный интерфейс в зависимости от класса размера окна . Однако вы можете переопределить это поведение. Например, если ваше приложение отображает одну большую панель контента для ленты, оно может использовать постоянную панель навигации для расширенных окон, но по-прежнему использовать поведение по умолчанию для классов компактного и среднего размера окна:

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

Дополнительные ресурсы