Search code examples
androidkotlinanimationdragandroid-jetpack-compose

Reset offset animation on draggable item in Jetpack Compose


I have a green square that I can drag vertically. But whenever I stop dragging it, I want it to reset the offset to the start with an animation. I tried it like this, but I can't figure it out. Does someone know how to do it?

@Composable
fun DraggableSquare() {
    var currentOffset by remember { mutableStateOf(0F) }
    val resetAnimation by animateIntOffsetAsState(targetValue = IntOffset(0, currentOffset.roundToInt()))

    var shouldReset = false

    Box(contentAlignment = Alignment.TopCenter, modifier = Modifier.fillMaxSize()) {
        Surface(
            color = Color(0xFF34AB52),
            modifier = Modifier
                .size(100.dp)
                .offset {
                    when {
                        shouldReset -> resetAnimation
                        else -> IntOffset(0, currentOffset.roundToInt())
                    }
                }
                .draggable(
                    state = rememberDraggableState { delta -> currentOffset += delta },
                    orientation = Orientation.Vertical,
                    onDragStopped = {
                        shouldReset = true
                        currentOffset = 0F
                    }
                )
        ) {}
    }
}

Solution

  • You can define the offset as an Animatable.
    While dragging use the method snapTo to update the current value as the initial value and the onDragStopped to start the animation.

    val coroutineScope = rememberCoroutineScope()
    val offsetY  =  remember { Animatable(0f) }
    
    Box(contentAlignment = Alignment.TopCenter, modifier = Modifier.fillMaxSize()) {
        Surface(
            color = Color(0xFF34AB52),
            modifier = Modifier
                .size(100.dp)
                .offset {
                    IntOffset(0, offsetY.value.roundToInt())
                }
                .draggable(
                    state = rememberDraggableState { delta ->
                        coroutineScope.launch {
                            offsetY.snapTo(offsetY.value + delta)
                        }
                    },
                    orientation = Orientation.Vertical,
                    onDragStopped = {
                        coroutineScope.launch {
                            offsetY.animateTo(
                                targetValue = 0f,
                                animationSpec = tween(
                                    durationMillis = 3000,
                                    delayMillis = 0
                                )
                            )
                        }
                    }
                )
        ) {
        }
    }
    

    enter image description here