I want to animate only once and I'm trying different options. I wanted to use animate*AsState but that doesn't work since you have to kind of trigger it by doing some kind of SideEffect with LaunchedEffect when entering in that screen like this:
@Composable
fun StartAnimation(width: Float = 0f) {
var startAnimation by remember { mutableStateOf(false) }
val widthAnimation = animateDpAsState(targetValue = if(startAnimation) width.dp else 0.dp, tween(600))
Box(
modifier = Modifier
.padding(top = 30.dp)
.padding(horizontal = 30.dp)
.fillMaxWidth()
.height(20.dp)
.background(Color.Gray),
contentAlignment = Alignment.CenterStart
) {
Box(
Modifier
.background(Color.Black)
.width(widthAnimation.value)
.height(20.dp)
.background(Color.Black)
)
}
LaunchedEffect(key1 = true) {
startAnimation = true
}
}
Maybe there's a better way to do this and I'm missing it?
CodePudding user response:
Just create a custom fire-and-forget system. Works as a single-shot animator and can be spawned practically as many times as you like, in parallel.
@Composable
fun animateFloatAsState(
initialValue: Float,
targetValue: Float,
delay: Long = 0,
animationSpec: AnimationSpec<Float> = spring<Float>(),
visibilityThreshold: Float = 0.01f,
finishedListener: ((Float) -> Unit)? = null
): State<Float> {
var trigger by remember { mutableStateOf(false) }
return animateFloatAsState(
targetValue = if (trigger) targetValue else initialValue,
animationSpec = animationSpec,
visibilityThreshold = visibilityThreshold,
finishedListener = finishedListener
).also {
LaunchedEffect(Unit) {
delay(delay)
trigger = true
}
}
}
This is for Float
, but can as easily be translated to fit Dp
. I think it is just replacing the word Float
with Dp
CodePudding user response:
I don't think there is anything that will let you get rid of LaunchedEffect. But this might be better, at least you don't have to use that artificial variable:
val animatableWidth = remember { Animatable(0.dp, Dp.VectorConverter) }
LaunchedEffect(width) {
animatableWidth.animateTo(width.dp, tween(600))
}