Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Bottom sheet
enum class States {
EXPANDED,
COLLAPSED
}
@ExperimentalMaterialApi
@Composable
fun FullHeightBottomSheet(
header: @Composable () -> Unit,
body: @Composable () -> Unit
) {
val swipeableState = rememberSwipeableState(initialValue = States.EXPANDED)
val scrollState = rememberScrollState()
BoxWithConstraints {
val constraintsScope = this
val maxHeight = with(LocalDensity.current) {
constraintsScope.maxHeight.toPx()
}
val connection = remember {
object : NestedScrollConnection {
override fun onPreScroll(
available: Offset,
source: NestedScrollSource
): Offset {
val delta = available.y
return if (delta < 0) {
swipeableState.performDrag(delta).toOffset()
} else {
Offset.Zero
}
}
override fun onPostScroll(
consumed: Offset,
available: Offset,
source: NestedScrollSource
): Offset {
val delta = available.y
return swipeableState.performDrag(delta).toOffset()
}
override suspend fun onPreFling(available: Velocity): Velocity {
return if (available.y < 0 && scrollState.value == 0) {
swipeableState.performFling(available.y)
available
} else {
Velocity.Zero
}
}
override suspend fun onPostFling(
consumed: Velocity,
available: Velocity
): Velocity {
swipeableState.performFling(velocity = available.y)
return super.onPostFling(consumed, available)
}
private fun Float.toOffset() = Offset(0f, this)
}
}
Box(
Modifier
.swipeable(
state = swipeableState,
orientation = Orientation.Vertical,
anchors = mapOf(
0f to States.EXPANDED,
maxHeight to States.COLLAPSED,
)
)
.nestedScroll(connection)
.offset {
IntOffset(
0,
swipeableState.offset.value.roundToInt()
)
}
) {
Column(
Modifier
.fillMaxHeight()
.background(Color.White)
) {
header()
Box(
Modifier
.fillMaxWidth()
.verticalScroll(scrollState)
) {
body()
}
}
}
}
}
@Alexbeard
Copy link

Alexbeard commented Oct 25, 2021

What if in the bottom sheet content will be a lazylist, how we can track then a scrollState ?

@nkrebs13
Copy link

nkrebs13 commented Oct 27, 2021

@Alexbeard This was my use case as well. I believe I solved it with relatively few modifications.

  1. I changed val scrollState = rememberScrollState() to val scrollState = rememberLazyListState().

  2. In the onPreFling I changed scrollState.value == 0 to scrollState.firstVisibleItemIndex == 0 && scrollState.firstVisibleItemScrollOffset == 0

The .value in the original implementation describes "current scroll position value in pixels" so what I have accomplishes that I think for the LazyListState implementation of ScrollableState

  1. I changed the body part of the content at the end to include a LazyColumn
LazyColumn(
    modifier = Modifier.fillMaxWidth(),
    state = scrollState
) {
    body.invoke(this)
}
  1. I changed the body type for this Composable to be body: LazyListScope.() -> Unit,

  2. Profit?

I think that's it. It seems to be working, but let it be known that I've been playing around with my implementation for all of about 8 minutes so far

@pepos
Copy link

pepos commented Mar 24, 2022

@nkrebs13 8 minutes looks enough for me, ship it!

@AxonDragonScale
Copy link

AxonDragonScale commented Jun 12, 2022

Hey Guys, Where is the toOffset() method in swipeableState.performDrag(delta).toOffset() coming from? Its not resolving for me.

@AxonDragonScale
Copy link

AxonDragonScale commented Jun 12, 2022

Oh Sorry, Got it, Didn't see the extension function created below.

@doodlez79
Copy link

doodlez79 commented Jul 13, 2022

Hello, How can I make it so that I can move the bottom sheet only for the area with LazyColumn?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment