Przepis na planszę dolną
Ten przepis pokazuje, jak wyświetlić miejsce docelowe jako modalny arkusz u dołu ekranu.
Jak to działa
Aby wyświetlić miejsce docelowe jako arkusz u dołu ekranu, musisz wykonać 2 czynności:
-
Użyj
BottomSheetSceneStrategy: utwórz instancjęBottomSheetSceneStrategyi przekaż ją do parametrusceneStrategyfunkcji kompozycyjnejNavDisplay. -
Dodaj metadane do miejsca docelowego: w przypadku miejsca docelowego, które chcesz wyświetlać jako panel u dołu ekranu, dodaj do jego metadanych wartość
BottomSheetSceneStrategy.bottomSheet(). Odbywa się to w funkcjientry.
W tym przykładzie element RouteB jest skonfigurowany jako arkusz u dołu. Gdy przejdziesz z RouteA do RouteB, RouteB pojawi się w modalnym arkuszu u dołu ekranu, który wysunie się z jego dolnej części.
Treść panelu dolnego można w razie potrzeby ostylować. W tym przepisie treści są przycięte, aby miały zaokrąglone rogi.
Więcej informacji znajdziesz w oficjalnej dokumentacji dotyczącej niestandardowych układów.
/* * 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() }, sceneStrategy = 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.navigation3.runtime.NavEntry import androidx.navigation3.scene.OverlayScene import androidx.navigation3.scene.Scene import androidx.navigation3.scene.SceneStrategy import androidx.navigation3.scene.SceneStrategyScope /** An [OverlayScene] that renders an [entry] within a [ModalBottomSheet]. */ @OptIn(ExperimentalMaterial3Api::class) internal 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) = { ModalBottomSheet( onDismissRequest = onBack, properties = modalBottomSheetProperties, ) { 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() val bottomSheetProperties = lastEntry?.metadata?.get(BOTTOM_SHEET_KEY) as? ModalBottomSheetProperties 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]. */ @OptIn(ExperimentalMaterial3Api::class) fun bottomSheet( modalBottomSheetProperties: ModalBottomSheetProperties = ModalBottomSheetProperties() ): Map<String, Any> = mapOf(BOTTOM_SHEET_KEY to modalBottomSheetProperties) internal const val BOTTOM_SHEET_KEY = "bottomsheet" } }