I'm trying to achieve a scroll-based animation in Jetpack Compose, where items in a LazyColumn
animate into view as they come on screen. This is similar to how animations could be applied in RecyclerView
in the classic Views system, where items animate as they appear in the visible viewport.
I've tried using AnimatedVisibility
, animateItemPlacement
and animateItem
with LazyColumn
, but neither approach seems to work as expected. Here’s my current setup:
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.primaryContainer)
) {
items(500) {
Item(modifier = Modifier.animateItem())
}
}
@Composable
private fun Item(modifier: Modifier = Modifier) {
Column(
modifier
.fillMaxWidth()
.padding(horizontal = 10.dp, vertical = 5.dp)
.background(MaterialTheme.colorScheme.background),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
"Jihan Khan",
style = MaterialTheme.typography.headlineLarge.copy(color = MaterialTheme.colorScheme.onBackground)
)
Spacer(Modifier.height(10.dp))
Text(
"[email protected]",
style = MaterialTheme.typography.bodyMedium.copy(color = MaterialTheme.colorScheme.onBackground)
)
}
}
I also tried wrapping the Item with a Box
and applying the animateItemPlacement
or animateItem
modifier on it, but that didn’t work either. I’m looking for guidance on how to achieve the desired animation effect for LazyColumn items in Jetpack Compose. Any insights would be much appreciated!
animateItemPlacement
is superseded by animateItem
. That only animates when the list of items changes (i.e. a new item is added or the item order is changed).
If the list stays the same and you only want to animate when an item becomes visible you can use AnimatedVisibility
:
items(500) {
Box(Modifier.height(100.dp)) {
var visible by remember { mutableStateOf(false) }
LaunchedEffect(Unit) { visible = true }
AnimatedVisibility(visible) {
Item(/*...*/)
}
}
}
Note:
The AnimatedVisibility
only triggers when the visible
paramter changes. Therefore it must start with false
and needs to be set to true
in the LaunchedEffect later on.
When the AnimatedVisibility
starts with false
the entire content is hidden and won't take up any space in the LazyColumn. Therefore all other items in the LazyColumn are also loaded because they all won't take up space and they will all fit, before the first animation even starts. To prevent that I wrapped AnimatedVisibility
in a Box with a defined height that acts as a placeholder, even when AnimatedVisibility
is not visible.
I chose 100.dp
, but you want to adjust that to the actual height of your items.
If the default animation of AnimatedVisibility
doesn't appeal to you you can adjust it by passing something else to the enter
parameter which defaults to fadeIn() + expandIn()
(which animates the transparancy and the size respectively).