androidandroid-jetpack-composedraggableswipeclickable

How can I make a swipeable composable only consume clicks and drags directly on content?


Background

@ExperimentalMaterialApi
@Composable
fun AnchoredBottomSheet(
    state: BottomSheetState,
    maxHeight: Float,
    content: @Composable () -> Unit,
) {
    val swipeableState = rememberSwipeableState(initialValue = state)

    val anchors = mapOf(
        BottomSheetState.Gone.toAnchor(maxHeight),
        BottomSheetState.Half.toAnchor(maxHeight/2),
        BottomSheetState.Full.toAnchor(0),
    )

    Column(
        modifier = Modifier
            .height(maxHeight.toDp())
            .swipeable(
                enabled = swipeableState.currentValue != BottomSheetState.Gone,
                state = swipeableState,
                orientation = Orientation.Vertical,
                anchors = anchors,
            )
            .offset {
                IntOffset(
                    0,
                    swipeableState.offset.value.roundToInt()
                )
            }
    ) {
        content()
    }
}

Problem

Question

How can I make the my bottom sheet implementation only consume clicks/drags that occur on the content of the bottom sheet?


Solution

  • The issue was in the ordering of the Modifier extension invocations. Modifier.swipeable() should happen after Modifier.offset().

    @ExperimentalMaterialApi
    @Composable
    fun AnchoredBottomSheet(
        state: BottomSheetState,
        maxHeight: Float,
        content: @Composable () -> Unit,
    ) {
        val swipeableState = rememberSwipeableState(initialValue = state)
    
        val anchors = mapOf(
            BottomSheetState.Gone.toAnchor(maxHeight),
            BottomSheetState.Half.toAnchor(maxHeight/2),
            BottomSheetState.Full.toAnchor(0),
        )
    
        Column(
            modifier = Modifier
                .offset {
                    IntOffset(
                        x = 0,
                        y = swipeableState.offset.value.roundToInt(),
                    )
                }
                .swipeable(
                    enabled = swipeableState.currentValue != BottomSheetState.Gone,
                    state = swipeableState,
                    orientation = Orientation.Vertical,
                    anchors = BottomSheetState.getAnchors(maxHeight),
                )
        ) {
            content()
        }
    }