androidandroid-jetpack-composeandroid-animationmaterial-components-android

How to create a Composable that I can drag up and down on it to increase its height


I am required to create a UI in Jetpack Compose of a card that initially takes some constant height but the user can drag on it to increase or decrease its height and show more or less content. It's very similar to a Bottom Sheet in its behavior but I don't want my UI to be anchored to the bottom of the screen like how a Bottom Sheet is.

Here's how my card looks now:

enter image description here

Can you tell me how can I do this? I need to increase and decrease the height with user similar to what you have in a Bottom Sheet.


Solution

  • I managed to do it using Compose-Foundation AnchoredDraggable like below:

    Things taken into consideration while implementing this:

    @OptIn(ExperimentalFoundationApi::class)
    @Composable
    fun ExpandableColumn(
        modifier: Modifier = Modifier,
        content: @Composable ColumnScope.() -> Unit
    ) {
        val density = LocalDensity.current
    
        // TODO replace these fixed values with calculated values depending on content
        val collapsedHeight = 300.dp
        val expandedHeight = 600.dp
    
        val state = remember {
            AnchoredDraggableState<CardDragAnchors>(
                initialValue = CardDragAnchors.Collapsed,
                positionalThreshold = { distance: Float -> distance * 0.5f },
                velocityThreshold = { with(density) { 100.dp.toPx() } },
                animationSpec = tween(),
            ).apply {
                updateAnchors(
                    DraggableAnchors {
                        CardDragAnchors.Collapsed at with(density) { collapsedHeight.toPx() }
                        CardDragAnchors.Expanded at with(density) { expandedHeight.toPx() }
                    }
                )
            }
        }
    
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .height(with(density) { state.offset.toDp() })
                .anchoredDraggable(
                    state = state,
                    orientation = Orientation.Vertical,
                    enabled = true,
                    reverseDirection = true,
                )
                .then(modifier)
        ) {
    
            content()
    
        }
    }
    
    private sealed interface CardDragAnchors {
        data object Collapsed : CardDragAnchors
        data object Expanded : CardDragAnchors
    }