Search code examples
kotlinandroid-jetpack-composeandroid-animation

Why does my animatedWidth variable doesn't change with the function animateTo?


I have an animation it's a green rectangle that grows in length inside a white outlined rectangle. It was working fine before I adjusted the layout. Now the green rectangle simply doesn't grow. If I change animatedWidth to 10f the rectangle appears. But it is supposed to start from zero and grow in length. The animation is called from a button which turns isVisible to true which shows the composable. This part works, because I can see the white rectangle.

Button(
    onClick = { isVisible = !isVisible }
) {
    Text(text = "Play animation")
}

if (isVisible) {
    Box(modifier = Modifier.fillMaxSize()) {
        OutlinedLinearProgressBar()
    }
}

This is the composable that contains my animation. The green rectangle is the one at the end and it is the size width variable that is supposed to change.

@Composable
fun OutlinedLinearProgressBar() {
    var animationPlayed by remember { mutableStateOf(false) }
    val animatedWidth = remember { Animatable(0f) }
    var canvasSize by remember { mutableStateOf(Size.Zero) }

    LaunchedEffect(Unit) {
        val targetWidth = canvasSize.width / 2.18f
        val duration = 1000
        with(animatedWidth) {
            launch {
                animateTo(
                    targetValue = targetWidth,
                    animationSpec = tween(durationMillis = duration),
                )
            }
        }
    }

    LaunchedEffect(key1 = true) { animationPlayed = true }

    Box(modifier = Modifier.fillMaxSize()) {
        Canvas(modifier = Modifier.fillMaxSize()) {
            canvasSize = size
            drawRect(color = Color.Black, size = size)

            val centerY = center.y
            drawRect(
                style = Stroke(width = 10f),
                color = Color.White,
                topLeft = Offset(x = size.width / 4f, y = centerY - (size.height / 10f) / 2f),
                size = Size(
                    width = size.width / 2f,
                    height = size.height / 10f,
                ),
            )
            drawRect(
                color = Color.Green,
                style = Fill,
                topLeft = Offset(x = size.width / 3.7f, y = centerY - (size.height / 10f) / 2.5f),
                size = Size(
                    width = animatedWidth.value,
                    height = size.height / 12.5f,
                ),
            )
        }
    }
}

Solution

  • The code you provided works for me, but there may be some problems with the key of your LaunchedEffect when unexpected recompositions occur, like changes in size. That depends on how this is used in your actual code.

    You do not even need the LaunchedEffect when you replace animatedWidth with

    val animatedWidth by animateFloatAsState(
        targetValue = canvasSize.width / 2.18f,
        animationSpec = tween(1000),
        label = "animatedWidth",
    )
    

    Since this accesses canvasSize directly it needs to be moved one line down, after the declaration of canvasSize.