https://kotlinlang.org logo
#compose
Title
# compose
s

Se7eN

06/01/2021, 4:04 PM
I'm animating the progress of this meter from 0 to 100 infinitely. When some data from the network is loaded, containing the progress value, I want it to transition to that value and stop the infinite animation. How can I achieve this? Code in thread.
Copy code
val infiniteTransition = rememberInfiniteTransition()
val progress by infiniteTransition.animateFloat(
    initialValue = 0f,
    targetValue = 100f,
    animationSpec = infiniteRepeatable(
        animation = tween(1000, easing = LinearEasing),
        repeatMode = RepeatMode.Reverse
    )
)
t

Tash

06/01/2021, 5:13 PM
To control the animation imperatively like that, might need to use
Animatable
instead, since it exposes a
stop()
s

Se7eN

06/01/2021, 5:26 PM
So I used
updateTransition
and came up with this. It's working fine, just wanna make sure if I did it right?
d

Doris Liu

06/01/2021, 6:53 PM
I don't think
updateTransition
is the best API for this use case.
Transition
expects a value associated with each state, and animates to that value when state changes. The initial infinite animation between 0-100 isn't exactly a state.
Animatable
would be more suitable for your use case, you can start with an
animateTo
using an infinite animation spec, and interrupt it any time with a new target based on the network data.
☝🏼 1
👍 1
☝️ 1
t

Tash

06/01/2021, 7:01 PM
The initial infinite animation between 0-100 isn’t exactly a state
To add to that, looks like the snippet is transforming between
MeterHeadState
and a float, clearly indicating that you are trying to make the 0-100 animation into a state just to make it work with
updateTransition()
.
👍 2
s

Se7eN

06/02/2021, 12:48 PM
Got it. I'll use
Animatable
and remove the
MeterHeadState
. Thank you both!
🙏🏼 1
Copy code
val progress = remember { Animatable(0f) }
LaunchedEffect(index) {
    if (index != null) {
        progress.animateTo(
            targetValue = index.value.toFloat(),
            animationSpec = spring(
                Spring.DampingRatioMediumBouncy,
                stiffness = Spring.StiffnessVeryLow
            )
        )
    } else {
        progress.animateTo(
            targetValue = 100f,
            animationSpec = infiniteRepeatable(
                animation = keyframes {
                    durationMillis = 800
                    0f at 0 with FastOutSlowInEasing
                    100f at 800 with FastOutSlowInEasing
                },
                repeatMode = RepeatMode.Reverse
            )
        )
    }
}
So I guess I'm doing it right now
d

Doris Liu

06/02/2021, 6:42 PM
Much cleaner! 👏
🎉 2
s

Se7eN

06/02/2021, 6:49 PM
Sorry for bothering again but is it fine to just return the value like this:
Copy code
@Composable
private fun animatedProgress(index: FGIndex?): Float {
    val progress = remember { Animatable(0f) }
    LaunchedEffect(index) {
        if (index != null) {
            progress.animateTo(
                targetValue = index.value.toFloat(),
                animationSpec = ProgressToIndexSpec
            )
        } else {
            progress.animateTo(
                targetValue = 100f,
                animationSpec = ProgressInfiniteSpec
            )
        }
    }

    return progress.value
}
d

Doris Liu

06/02/2021, 6:54 PM
No bother at all. That looks fine. Though I would defer the value read until later whenever possible - The location of state read determines the scope of invalidation. If the progress only needs to be read during draw, I'd return a
State<Float>
here instead (i.e.
progress.asState()
)
1
s

Se7eN

06/02/2021, 6:57 PM
Oh, I get it now. Thanks for all the help 🙂
👍 1
4 Views