How to place a sticky bottom row/bar at ModalBottomSheet using jetpack compose

1.2k Views Asked by At

When placing items within ModalBottomSheet they get stacked expanding the Bottom sheet till they reach a certain threshold of height, beyond which the user has to scroll the Bottom sheet up to load the overflowing content.
I'm trying to place a bottom row of composables like a Button and another rounded button, that's always fixed (whether the ModalBottomSheet is expanded or not).

What I've tried (using scaffold/column to wrap the whole bottom sheet content) always results in the bottom row being placed at the bottom, only at the expanded state.

That's that prototype that I'm aiming at

enter image description here

enter image description here

2

There are 2 best solutions below

2
On

I can handle this via offset. First, create a value to calculate the height of ModalBottomSheet:

var modalHeight by remember { mutableStateOf(0) }

After that, add this:

 ModalBottomSheet(
    modifier = Modifier
        .padding(0.dp)
        .onGloballyPositioned {
            modalHeight = it.size.height
        })

And then you can handle any element with offset for example i have a LazyRow:

 LazyRow(
            modifier = Modifier
                .fillMaxWidth()
                .requiredHeight(170.dp)
                .offset {
                    IntOffset(
                        0,
                        (modalHeight - bottomSheetState.requireOffset() - footerHeight).toInt()
                    )
                }
                .fillMaxWidth()
                .padding(top = 12.dp, end = 0.dp, start = 0.dp, bottom = 42.dp)
                .onGloballyPositioned {
                    footerHeight = (((it.size.height * 1)) + bottomPadding + 0).toInt()
                }
                .shadow(4.dp, RoundedCornerShape(1.dp), spotColor = Color.Gray)
                .background(color = Color.White, RoundedCornerShape(1.dp))
            ,
            horizontalArrangement = Arrangement.SpaceAround,
            contentPadding = PaddingValues(16.dp)
        )

You can use every element, like Text, Button and etc...

bottomPadding: I created this for space between the device buttons and my element. footerHeight: this is the height of the element that you are using; if the element is static, use the static value.

0
On

I managed to do this by applying the bottom sheet offset to the button.

if (showBottomSheet) {
          ModalBottomSheet(
            onDismissRequest = {
              showBottomSheet = false
            },
            sheetState = sheetState
          ) {
            // Sheet content
            Box(
              modifier = Modifier.fillMaxHeight(),
              contentAlignment = Alignment.BottomEnd
            ) {
              LazyColumn {
                items(50) {
                  Text(
                    modifier = Modifier.fillMaxWidth(),
                    text = "Hello $it"
                  )
                }
              }
              Button(modifier = Modifier
                .wrapContentWidth()
                .padding(20.dp)
                .offset {
                  IntOffset(
                    x = 0,
                    y = -sheetState.requireOffset().toInt()
                  )
                },
                onClick = {
                  scope.launch {
                    sheetState.hide()
                    showBottomSheet = false
                  }
                }) {
                Text(text = "Close")
              }
            }
          }
        }

Notice that I used a Box container to put the button on top of the list. Then, I aligned the content of the Box to the bottom. When you drag the bottom sheet up, it doesn't change its size. It only changes the offset. So, I took that offset and simply moved the button up for the same amount.