Reputation: 3582
I have a custom layout which is a calendar like screen that the user can drag around as a whole. Events that are visible to the user in the time frame visible on the screen get drawn with placeables/measurable.
I have a feature that as the user partially drags events off screen, the titles in the content sticky to the top of the event until it's fully dragged off screen.
When this happens, I add a little image up top to indicate the event continues above it
E.g
"Event Name"
turns into
^
"Event Name"
I want to animate the "Event Name" text moving down as the ^ appears or disappears instead of it simply jumping down when I insert/remove the ^ on recomposition.
It looked like this is what I needed to animate the text properly
@Composable
fun TestItem(event: Event) {
val offset: Float by animateFloatAsState(if (event.isPartiallyOffScreen)) 100f else 0f)
Box(Modifier.background(color=event.color)) {
Text(
text = event.name,
modifier = Modifier.graphicsLayer(
translationY = offset
)
)
}
}
And this does indeed animate the text smoothly downward and upward as the content switches between on/off screen, the problem though, is I have dozens of these on the screen, and it only works for the first item in a column until a gap happens.
E.g
Event 811 will animate the text down as it goes off screen, as Event 195 starts going off screen it just jumps from 0 to 100 the moment it happens, and then Event 757 will work and animate text properly, although after making a gif of it, 757 stutters a little too.
It's almost like because the Event 811 composable ceases to exist the moment it's fully off screen, it thinks Event 195 is Event 811 as it takes that spot in the placeable order and it's now starting to go offscreen and reuses the previous values? By the time it reaches 757 it's been gone for many drags, and it kinda works as expected?
Update: I've since confirmed that if I stop removing items from the list if they are off screen, it works as expected on all items, but the performance hit and jank are considerable as there are upwards of 80-100 items being drawn, most off screen, so that's not really an option.
I've tried using LaunchedEffects as well with a unique key from the event, and it doesn't work either?
This is the only stateful thing in the composable, everything else is just values being set from the constructor content
I'm kind of stumped on what to do with this problem.
Upvotes: 1
Views: 1854
Reputation: 3582
After confirming that my screen would work if I didn't remove off screen composables, it confirmed my suspicion that the recompositions were getting confused due to the items all being the same.
I thought using keys on the animations would solve the issue, but it didn't. What I needed was a key on the composable as described in this answer
https://stackoverflow.com/a/70191854/2408033
So for me, I needed to wrap each event in the custom layout in a key
composable
Layout(
modifier = modifier.fillMaxHeight()
content = {
eventList.forEach { event ->
key(event) {
TestItem(displayPerformance, adjustedStart)
}
}
},
) { measureables, constraints ->
Upvotes: 2