I'm working on a Jetpack Compose application where I have multiple rows of components that I want to animate simultaneously. The animation in the first row(scale) is different from the second row(CrossFade). I have achieved the concept, but the timing of both animations is different. Currently, the second animation starts only after the first animation ends. I need both animations to happen simultaneously, and after animation ends be visible for 2 seconds, and then repeat.
The desired effect I'm aiming for is illustrated below. But upon observation, you might notice that the animation isn't simultaneous.
https://jmp.sh/s/Km7SqnrBma8SRSfA3pKV
@Composable
fun <T> AnimateRotatingList(
list: List<T>,
modifier: Modifier = Modifier,
animationSpec: AnimationSpec<Float> = tween(durationMillis = 2000),
updateCurrentIndex: (Int) -> Unit,
itemContent: @Composable (item: T) -> Unit
) {
Box(modifier = modifier) {
if (list.isEmpty()) return
if (list.size == 1) {
itemContent(list.first())
return
}
var currentIndex by remember(list) { mutableIntStateOf(0) }
val current = remember(list, currentIndex) { list[currentIndex] }
val next = remember(list, currentIndex) { list[(currentIndex + 1) % list.size] }
val scale = remember(list) { Animatable(1f) }
LaunchedEffect(scale, animationSpec) {
while (true) {
delay(2000)
scale.animateTo(
targetValue = 0f,
animationSpec = animationSpec,
)
scale.snapTo(1f)
currentIndex = (currentIndex + 1) % list.size
updateCurrentIndex(currentIndex)
}
}
Box(modifier = Modifier.graphicsLayer {
scaleY = scale.value
transformOrigin = TransformOrigin(0.5f, 0f)
}) {
itemContent(current)
}
Box(modifier = Modifier.graphicsLayer {
scaleY = 1 - scale.value
transformOrigin = TransformOrigin(0.5f, 1f)
}) {
itemContent(next)
}
}
}
data class Event(
val start: String,
val end: String,
val loc: String,
val loca: String
)
@Composable
fun FollowEventCardHome() {
val eventData = listOf(
Event("A", "B", "T1", "T2"),
Event("B", "C", "T3", "T4"),
Event("C", "D", "T5", "T6"),
)
var currentIndex by remember(eventData) { mutableIntStateOf(0) }
val current = remember(eventData, currentIndex) { eventData[currentIndex] }
val next = remember(eventData, currentIndex) { eventData[(currentIndex + 1) % eventData.size] }
Column {
AnimateRotatingList(
list = eventData,
modifier = Modifier
.padding(top = 150.dp),
updateCurrentIndex = { currentIndex = it }
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 30.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = it.start,
fontSize = 20.sp,
)
Text(
text = it.end,
fontSize = 20.sp,
)
}
}
Spacer(modifier = Modifier.padding(20.dp))
Crossfade(targetState = currentIndex) { index ->
val currentItem = eventData[index]
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 30.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(text = current.loc)
Text(text = current.loca)
}
}
}
}
The AnimateRotatingList composable animates a list of events, rotating through them every 2 seconds. The FollowEventCardHome composable uses this to animate two rows with different animations. However, the second row starts animating only after the first row's animation ends.
How can I make both rows animate simultaneously with the same timing and repeat every 2 seconds?
Thanks in advance for any help or suggestions!