Last active
July 2, 2025 22:19
-
-
Save eygraber/218406422590a739f0e284743265b047 to your computer and use it in GitHub Desktop.
A Material3 BottomSheetScene and BottomSheetSceneStrategy for AndroidX Nav3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@OptIn(ExperimentalMaterial3Api::class) | |
@Immutable | |
data class BottomSheetSceneProperties( | |
val modalBottomSheetModifier: Modifier = Modifier, | |
val properties: ModalBottomSheetProperties = ModalBottomSheetDefaults.properties, | |
val skipPartiallyExpanded: Boolean = false, | |
val confirmValueChange: (SheetValue) -> Boolean = { true }, | |
val contentWindowInsets: @Composable (SheetState) -> WindowInsets = defaultWindowInsets(), | |
val dragHandle: @Composable (() -> Unit)? = { BottomSheetDefaults.DragHandle() }, | |
val sheetMaxWidth: Dp = BottomSheetDefaults.SheetMaxWidth, | |
val tonalElevation: Dp = 0.dp, | |
val shape: @Composable () -> Shape = { BottomSheetDefaults.ExpandedShape }, | |
val containerColor: @Composable () -> Color = { BottomSheetDefaults.ContainerColor }, | |
val contentColor: @Composable () -> Color = { contentColorFor(containerColor()) }, | |
val scrimColor: @Composable () -> Color = { BottomSheetDefaults.ScrimColor }, | |
) { | |
companion object { | |
@OptIn(ExperimentalMaterial3Api::class) | |
fun defaultWindowInsets(): @Composable ( | |
SheetState, | |
) -> WindowInsets = { | |
BottomSheetDefaults.windowInsets | |
} | |
} | |
} | |
@OptIn(ExperimentalMaterial3Api::class) | |
internal class BottomSheetScene<T : Any>( | |
override val key: Any, | |
override val previousEntries: List<NavEntry<T>>, | |
override val overlaidEntries: List<NavEntry<T>>, | |
private val entry: NavEntry<T>, | |
private val properties: BottomSheetSceneProperties, | |
private val onBack: (count: Int) -> Unit, | |
) : OverlayScene<T> { | |
override val entries: List<NavEntry<T>> = listOf(entry) | |
override val content: @Composable (() -> Unit) = { | |
val sheetState = rememberModalBottomSheetState( | |
skipPartiallyExpanded = properties.skipPartiallyExpanded, | |
confirmValueChange = properties.confirmValueChange, | |
) | |
ModalBottomSheet( | |
onDismissRequest = { onBack(1) }, | |
sheetState = sheetState, | |
modifier = properties.modalBottomSheetModifier, | |
sheetMaxWidth = BottomSheetDefaults.SheetMaxWidth, | |
shape = properties.shape(), | |
properties = properties.properties, | |
containerColor = properties.containerColor(), | |
contentColor = properties.contentColor(), | |
tonalElevation = properties.tonalElevation, | |
scrimColor = properties.scrimColor(), | |
dragHandle = properties.dragHandle, | |
contentWindowInsets = { | |
properties.contentWindowInsets(sheetState) | |
}, | |
) { entry.Content() } | |
} | |
} | |
class BottomSheetSceneStrategy<T : Any>() : SceneStrategy<T> { | |
@Composable | |
override fun calculateScene( | |
entries: List<NavEntry<T>>, | |
onBack: (count: Int) -> Unit, | |
): Scene<T>? { | |
val lastEntry = entries.lastOrNull() | |
val bottomSheetProperties = lastEntry?.metadata?.get(BOTTOM_SHEET_KEY) as? BottomSheetSceneProperties | |
return bottomSheetProperties?.let { properties -> | |
BottomSheetScene( | |
key = lastEntry.contentKey, | |
previousEntries = entries.dropLast(1), | |
overlaidEntries = entries.dropLast(1), | |
entry = lastEntry, | |
properties = properties, | |
onBack = onBack, | |
) | |
} | |
} | |
companion object { | |
@OptIn(ExperimentalMaterial3Api::class) | |
fun bottomSheet( | |
bottomSheetProperties: BottomSheetSceneProperties = BottomSheetSceneProperties() | |
): Map<String, Any> = mapOf(BOTTOM_SHEET_KEY to bottomSheetProperties) | |
internal const val BOTTOM_SHEET_KEY = "bottom_sheet" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment