androidkotlinuser-interfaceandroid-jetpack-compose

Offset Not Working in Swiper Element - Jetpack Compose


I am trying to make a swiper element in my app, one that behaves like the swiper Apple phones use when picking up a call. In this process, I have used pointer input and offset to acquire the user action as well as to update the visuals. However, even when I hardcoded the offset, the icon doesnt budge. Its a circle in a rounded rectangle that should be swiping sideways.

Box(
    Modifier
        .padding(top = 40.dp)
        .width(350.dp)
        .height(60.dp)
        .align(Alignment.CenterHorizontally)
        .clip(RoundedCornerShape(45.dp))
        .background(White.copy(0.4f))
) {

    // Slider Circle
    val density= LocalDensity.current
    val maxOffset=with(density){280.dp.toPx()}
    var offsetX by remember {mutableFloatStateOf(0f)

    Box(
        Modifier
            .size(60.dp)
            .padding(6.dp)
            .clip(RoundedCornerShape(45.dp))
            .background(White)
            // Set the offset when scrolled (Does NOT WORK)
            .offset (with(density){(offsetX/density.density).dp}, 0.dp)
            .pointerInput(Unit) {
                 detectDragGestures { change, dragAmount ->
                     change.consume() //Handles the Drag event

                     //Limit the X
                     offsetX=(dragAmount.x+offsetX).coerceIn(0f,maxOffset)
                     Log.i("Slider","$offsetX")
                }
            } 
            .zIndex(1f)
        )
}

I used logcat to ensure that the offset was correct, which it was. I clamped it and used density to ensure it doesn't go out of bounds. However, the icon just didn't move.


Solution

  • Please have a look at the offset Modifier documentation:

    Offset the content by offset px

    As the inner Box Composable has no actual content, the offset Modifier has nothing it can move around.

    You can directly add the offset Modifier to the parent Box instead.
    Also, you can simply use the draggable Modifier instead of the pointerInput like this:

    @Composable
    fun SwiperDemo() {
    
        val density = LocalDensity.current
        val maxOffset = with(density){ 290.dp.toPx() }
        var offsetX by remember { mutableFloatStateOf(0f) }
    
        Box(
            Modifier
                .padding(top = 40.dp)
                .width(350.dp)
                .height(60.dp)
                .clip(RoundedCornerShape(45.dp))
                .background(Green.copy(0.4f))
                .offset { IntOffset(offsetX.roundToInt(), 0) }  // apply on parent Box
        ) {
    
            Box(
                Modifier
                    .size(60.dp)
                    .padding(6.dp)
                    .clip(RoundedCornerShape(45.dp))
                    .background(Green)
                    .draggable(
                        orientation = Orientation.Horizontal,
                        state = rememberDraggableState { delta ->
                            val localOffset = offsetX + delta
                            offsetX = localOffset.coerceIn(0f, maxOffset)
                        }
                    )
            )
        }
    }
    

    Output:

    Screen Recording