Animated grid in Jetpack Compose
In this article you will learn how to implement a simple animated grid of elements in Jetpack Compose.
Motivation and Context
Recently, in one of the apps I’m working on I came across a case where I needed to animate position changes in a grid of elements. My first idea was to try animateItemPlacement from LazyItemScope but unfortunately it’s not available for LazyVerticalGrid in Compose 1.1.0. The only way to achieve it was to write something custom.
First, let’s determine what data we need to display in the grid. For sure the number of columns, rows, and a list of items. We also need to be able to identify items so we can use a dedicated function — very similar to how LazyRow/Column works. With that we can create the following signature:
To implement an animated grid we can use simple Box and calculate offsets manually. It will later allow us to easily animate specific offsets. Knowing how many columns and rows we want to display, we can calculate the desired item size based on the available space. The best component for this job is BoxWithConstrains. It gives us available
maxHeight thanks to which we can calculate the maximum item size:
The next step is to calculate the required offset for every cell. We iterate over all columns and rows and create a flat list of
To be able to animate
DpOffset we can wrap it inside Animatable. Let's introduce the type alias and custom factory to make it simpler.
Now is the time for the most difficult part. We need to somehow assign offsets to the items. When the grid is displayed for the first time it’s easy, we just take the first offset for the first item, the second offset for the second item, and so on. The tricky part is to remember offsets in a way that we can reuse them. My idea for that is to associate offsets with item keys and recreate them every time keys change. During recreation, we can check if we already know the offset for a given key or not. If we do, we can reuse it, otherwise we have to create a new offset based on the index.
Once we assign offsets for all items, we can finally draw them on the screen. Nothing complicated here, we just iterate over and wrap each item inside Box with calculated size and offset.
Now you may think: wait but how does it animate? If we reuse item offsets together with creating new ones, we may end up with two items in the same position. And you are right!
The last missing part in our puzzle is to animate all item offsets to the new positions using LaunchedEffect:
And that’s it! Our complete solution takes only 50 lines of code ❤️ You can find it here. It’s not production-ready but with a few more tweaks it can be.
If you want to play with it, make sure to check out my example app.
Originally published at https://github.com.