android-jetpack-composecompose-multiplatform

How to make tween animation duration dynamic in compose?


I'm trying to create a form to change the animation duration. I've connected the DropdownMenu's click listener to animTime, and I've verified that animTime does change 3000 -> 300, but the actual animation speed still remains the same.

Any ideas what I'm doing wrong? I'm using org.jetbrains.compose:1.8.0-beta02 if that matters.

@Composable
fun test() {
    var animTime by remember { mutableIntStateOf(3000) }
    var easing by remember { mutableStateOf(FastOutSlowInEasing) }
    var containerSize by remember { mutableStateOf(IntSize.Zero) }
    val boxSize = 100.dp
    val moveX = max(0, containerSize.width - boxSize.px)

    val moveOffset by animateIntOffsetAsState(
        targetValue = IntOffset(moveX, 0),
        animationSpec = infiniteRepeatable(
            tween(
                durationMillis = animTime,
                easing = easing,
            )
        ),
    )

    Row(
        modifier = Modifier.fillMaxWidth()
    ) {
        Column(
            modifier = Modifier.weight(1f)
                .background(Color.Red)
                .onSizeChanged { containerSize = it }
        ) {
            Box(
                modifier = Modifier.size(boxSize)
                    .offset { moveOffset } // Offset must come before background (why?)
                    .background(Color.Cyan)
            )
        }

        Column(Modifier.weight(1f)) {
            Button(onClick = {
                if (animTime == 3000) animTime /= 10
                else animTime *= 10
            }) {
                Text("dur=$animTime")
            }
        }
    }
}

Solution

  • Use an Animatable within a looping coroutine solved my issue.

    val moveOffset2 = remember { Animatable(IntOffset.Zero, IntOffset.VectorConverter) }
    LaunchedEffect(animTime, moveX) {
        while (true) {
            moveOffset2.snapTo(IntOffset.Zero)
            moveOffset2.animateTo(IntOffset(moveX, 0), tween(animTime))
        }
    }
    
    Box(
        modifier = Modifier.size(boxSize)
            .offset { moveOffset2.value } // Offset must come before background (why?)
            .background(Color.Cyan)
    )