Оптимизируйте свои подборки
Сохраняйте и классифицируйте контент в соответствии со своими настройками.
Рецепт нижнего слоя
В этом примере показано, как отобразить пункт назначения в виде модального нижнего окна.
Как это работает
Чтобы отобразить пункт назначения в виде нижнего листа, необходимо выполнить два действия:
Используйте BottomSheetSceneStrategy : создайте экземпляр BottomSheetSceneStrategy и передайте его параметру sceneStrategy компонуемого объекта NavDisplay .
Добавьте метаданные к целевому элементу : для целевого элемента, который вы хотите отобразить в виде нижнего листа, добавьте BottomSheetSceneStrategy.bottomSheet() в его метаданные. Это делается в функции entry .
В этом примере RouteB настроен как нижнее всплывающее окно. При переходе с RouteA на RouteB , RouteB будет отображаться в модальном окне, которое выдвигается снизу экрана.
Содержимое нижнего листа можно оформить по своему усмотрению. В этом рецепте содержимое обрезается так, чтобы углы были закруглены.
Для получения более подробной информации см. официальную документацию по пользовательским макетам .

Исследовать
Полный рецепт можно посмотреть на GitHub.
arrow_forward /*
* Copyright 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.nav3recipes.bottomsheet
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Text
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.dropUnlessResumed
import androidx.navigation3.runtime.NavKey
import androidx.navigation3.runtime.entryProvider
import androidx.navigation3.runtime.rememberNavBackStack
import androidx.navigation3.ui.NavDisplay
import com.example.nav3recipes.content.ContentBlue
import com.example.nav3recipes.content.ContentGreen
import com.example.nav3recipes.ui.setEdgeToEdgeConfig
import kotlinx.serialization.Serializable
@Serializable
private data object RouteA : NavKey
@Serializable
private data class RouteB(val id: String) : NavKey
class BottomSheetActivity : ComponentActivity() {
@OptIn(ExperimentalMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) {
setEdgeToEdgeConfig()
super.onCreate(savedInstanceState)
setContent {
val backStack = rememberNavBackStack(RouteA)
val bottomSheetStrategy = remember { BottomSheetSceneStrategy<NavKey>() }
NavDisplay(
backStack = backStack,
onBack = { backStack.removeLastOrNull() },
sceneStrategies = listOf(bottomSheetStrategy),
entryProvider = entryProvider {
entry<RouteA> {
ContentGreen("Welcome to Nav3") {
Button(onClick = dropUnlessResumed {
backStack.add(RouteB("123"))
}) {
Text("Click to open bottom sheet")
}
}
}
entry<RouteB>(
metadata = BottomSheetSceneStrategy.bottomSheet()
) { key ->
ContentBlue(
title = "Route id: ${key.id}",
modifier = Modifier.clip(
shape = RoundedCornerShape(16.dp)
)
)
}
}
)
}
}
}
package com.example.nav3recipes.bottomsheet
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.ModalBottomSheetProperties
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.compose.rememberLifecycleOwner
import androidx.navigation3.runtime.NavEntry
import androidx.navigation3.runtime.NavMetadataKey
import androidx.navigation3.runtime.get
import androidx.navigation3.runtime.metadata
import androidx.navigation3.scene.OverlayScene
import androidx.navigation3.scene.Scene
import androidx.navigation3.scene.SceneStrategy
import androidx.navigation3.scene.SceneStrategyScope
import com.example.nav3recipes.bottomsheet.BottomSheetSceneStrategy.Companion.bottomSheet
/** An [OverlayScene] that renders an [entry] within a [ModalBottomSheet]. */
@OptIn(ExperimentalMaterial3Api::class)
internal data class BottomSheetScene<T : Any>(
override val key: T,
override val previousEntries: List<NavEntry<T>>,
override val overlaidEntries: List<NavEntry<T>>,
private val entry: NavEntry<T>,
private val modalBottomSheetProperties: ModalBottomSheetProperties,
private val onBack: () -> Unit,
) : OverlayScene<T> {
override val entries: List<NavEntry<T>> = listOf(entry)
override val content: @Composable (() -> Unit) = {
val lifecycleOwner = rememberLifecycleOwner()
ModalBottomSheet(
onDismissRequest = onBack,
properties = modalBottomSheetProperties,
) {
CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
entry.Content()
}
}
}
}
/**
* A [SceneStrategy] that displays entries that have added [bottomSheet] to their [NavEntry.metadata]
* within a [ModalBottomSheet] instance.
*
* This strategy should always be added before any non-overlay scene strategies.
*/
@OptIn(ExperimentalMaterial3Api::class)
class BottomSheetSceneStrategy<T : Any> : SceneStrategy<T> {
override fun SceneStrategyScope<T>.calculateScene(entries: List<NavEntry<T>>): Scene<T>? {
val lastEntry = entries.lastOrNull() ?: return null
val bottomSheetProperties = lastEntry.metadata[BottomSheetKey] ?: return null
return bottomSheetProperties.let { properties ->
@Suppress("UNCHECKED_CAST")
BottomSheetScene(
key = lastEntry.contentKey as T,
previousEntries = entries.dropLast(1),
overlaidEntries = entries.dropLast(1),
entry = lastEntry,
modalBottomSheetProperties = properties,
onBack = onBack
)
}
}
companion object {
/**
* Function to be called on the [NavEntry.metadata] to mark this entry as something that
* should be displayed within a [ModalBottomSheet].
*
* @param modalBottomSheetProperties properties that should be passed to the containing
* [ModalBottomSheet].
*/
fun bottomSheet(modalBottomSheetProperties: ModalBottomSheetProperties = ModalBottomSheetProperties()) =
metadata {
put(BottomSheetKey, modalBottomSheetProperties)
}
object BottomSheetKey : NavMetadataKey<ModalBottomSheetProperties>
}
}
Контент и образцы кода на этой странице предоставлены по лицензиям. Java и OpenJDK – это зарегистрированные товарные знаки корпорации Oracle и ее аффилированных лиц.
Последнее обновление: 2026-05-09 UTC.
[[["Прост для понимания","easyToUnderstand","thumb-up"],["Помог мне решить мою проблему","solvedMyProblem","thumb-up"],["Другое","otherUp","thumb-up"]],[["Отсутствует нужная мне информация","missingTheInformationINeed","thumb-down"],["Слишком сложен/слишком много шагов","tooComplicatedTooManySteps","thumb-down"],["Устарел","outOfDate","thumb-down"],["Проблема с переводом текста","translationIssue","thumb-down"],["Проблемы образцов/кода","samplesCodeIssue","thumb-down"],["Другое","otherDown","thumb-down"]],["Последнее обновление: 2026-05-09 UTC."],[],[]]