natario1
10/13/2023, 10:46 AMskipPartiallyExpanded
flag. No combination seems to be safe.Zoltan Demant
10/13/2023, 11:34 AMascii
10/13/2023, 12:24 PMZoltan Demant
10/13/2023, 12:32 PMnatario1
10/13/2023, 1:09 PMModalBottomSheetContainer
at the root of the app and then I can use my version of ModalBottomSheet
from anywhere as if it were a modal even though it uses the scaffold under the hood. Way less buggy.
In fact, at a first glance I don’t see why modals can’t be implemented like this by default. Material3 is using a custom view, a composition nested inside the main composition, window manager.addView(), with all the inevitable problems about interop, window insets, and so on.private class ModalBottomSheetProvider {
val content: MutableState<(@Composable ColumnScope.() -> Unit)?> = mutableStateOf(null)
val onDismiss: MutableState<(() -> Unit)?> = mutableStateOf(null)
}
private val LocalModalBottomSheetProvider = compositionLocalOf<ModalBottomSheetProvider> { error("No provider") }
@Composable
fun ModalBottomSheet(
onDismiss: () -> Unit,
content: @Composable ColumnScope.() -> Unit
) {
val provider = LocalModalBottomSheetProvider.current
DisposableEffect(content) {
provider.content.value = content
onDispose { provider.content.value = null }
}
DisposableEffect(onDismiss) {
provider.onDismiss.value = onDismiss
onDispose { provider.onDismiss.value = null }
}
}
@Composable
fun ModalBottomSheetContainer(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
val provider = remember { ModalBottomSheetProvider() }
val state = rememberBottomSheetScaffoldState(
bottomSheetState = rememberModalBottomSheetState()
)
val sheetValue = state.bottomSheetState.currentValue
LaunchedEffect(sheetValue) {
if (sheetValue == SheetValue.Hidden) provider.onDismiss.value?.invoke()
}
val sheetContent = provider.content.value
LaunchedEffect(sheetContent) {
if (sheetContent != null) state.bottomSheetState.expand()
else state.bottomSheetState.hide()
}
CompositionLocalProvider(LocalModalBottomSheetProvider provides provider) {
BottomSheetScaffold(
scaffoldState = state,
sheetContent = { provider.content.value?.invoke(this) },
// Should help in skipping partially expanded state.
// The flag in rememberModalBottomSheetState() throws when using scaffold.
sheetPeekHeight = 0.dp,
modifier = modifier,
content = { content() }
)
}
}
ascii
10/13/2023, 1:35 PMnatario1
10/13/2023, 1:38 PMBottomSheetScaffold
under the hood while letting you call ModalBottomSheet { … }
even from nested hierarchies. Of course it has drawbacks (for example, composition locals are not kept). But still better than text field jumping around