Jetpack Compose: hasExpandedState in SheetState

174 Views Asked by At

I am creating my first decent app on compose and I need your help with SheetState to make ModalBottomSheet. I want this modal bottom sheet to be only on hidden and partially expanded state.

val hasExpandedState: Boolean
        get() = swipeableState.hasAnchorForValue(Expanded)

SheetState class has such a variable hasExpandedState, which as I understood could help me, but it is actually a value, so it's value couldn't be changed in my code. How to make ModalBottomSheet only in such states, is this hasExpandedState needed?

hasExpandedState can not be reassigned

1

There are 1 best solutions below

0
On

The expanded state can be disabled by modifying the confirmValueChange parameter of the ModalBottomSheet state. The following code allows the sheet value to be set only to a hidden or partially expanded.

val sheetState = rememberModalBottomSheetState(
    confirmValueChange = { sheetValue ->

        // Confirm a value change only if the sheet value
        // is hidden or partially expanded.

        sheetValue == SheetValue.Hidden
            || sheetValue == SheetValue.PartiallyExpanded

    }
)

The partialExpand() function will animate the ModalBottomSheet to a partially expanded state when invoked. Keep in mind that this function is a suspend function, which means that it has to be invoked inside a coroutine context. It can be done by launching a coroutine and calling the function.

var isBottomSheetVisible by remember { mutableStateOf(false) }
val coroutineScope = rememberCoroutineScope()

Button(
    onClick = {
        coroutineScope.launch {
            isBottomSheetVisible = true
            sheetState.partialExpand()
        }
    }
) {
    Text(text = "Expand Partially")
}

The hide() function will animate the ModalBottomSheet to a hidden state when invoked. This function is also a suspend function and it requires a coroutine context. Also, it's important to update the state which is associated with the visibility of the ModalBottomSheet composable, by wrapping it inside an invokeOnCompletion block, which will be executed as soon as the sheetState.hide() execution is completed. It's recommended to include it since hide() function animates the state.

Button(
    onClick = {
        coroutineScope
            .launch { sheetState.hide() }
            .invokeOnCompletion { isBottomSheetVisible = false }
    }
) {
    Text(text = "Hide")
}

Here is an example of a ModalBottomSheet implementation:

// The conditional state controlling the [ModalBottomSheet]
// composable visibility.
var isBottomSheetVisible by remember { mutableStateOf(false) }

// The coroutine scope required for partially expanding / hiding
// the [ModalBottomSheet].
val coroutineScope = rememberCoroutineScope()

// The actual state of the [ModalBottomSheet].
val sheetState = rememberModalBottomSheetState(
    confirmValueChange = { sheetValue ->

        // Confirm a value change only if the sheet value
        // is hidden or partially expanded.

        sheetValue == SheetValue.Hidden
                || sheetValue == SheetValue.PartiallyExpanded

    }
)

Surface(
    modifier = Modifier.fillMaxSize(),
    color = MaterialTheme.colorScheme.background
) {

    Box(
        modifier = Modifier.fillMaxSize()
    ) {

        Button(
            modifier = Modifier.align(Alignment.Center),
            onClick = {

                // Partially expand the [ModalBottomSheet] by using
                // the [coroutineScope] and launching a coroutine.

                coroutineScope.launch {
                    isBottomSheetVisible = true
                    sheetState.partialExpand()
                }

            }
        ) {
            Text(text = "Show")
        }

        // Controlling the [ModalBottomSheet] composable visibility
        // by using the [isBottomSheetVisible] conditional state.
        if (isBottomSheetVisible) {

            ModalBottomSheet(
                onDismissRequest = { isBottomSheetVisible = false },
                sheetState = sheetState
            ) {

                Button(
                    modifier = Modifier,
                    onClick = {

                        // Hide the [ModalBottomSheet] by using
                        // the [coroutineScope] to launch a coroutine
                        // and update the [isBottomSheetVisible]
                        // conditional state when the coroutine is finished.

                        coroutineScope
                            .launch { sheetState.hide() }
                            .invokeOnCompletion { isBottomSheetVisible = false }

                    }
                ) {
                    Text(text = "Hide")
                }

                Spacer(modifier = Modifier.weight(1f))

            }

        }

    }

}

I highly recommend checking out the Official Documentation.