Lazy Layouts in Jetpack Compose
Learn how to use Lazy Composables in Jetpack Compose to simply display data in your app. By Enzo Lizama.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Lazy Layouts in Jetpack Compose
20 mins
- Getting Started
- Understanding Lazy Lists
- Understanding LazyListScope
- Adding Lazy Composables in Your App
- Adding a LazyRow
- Adding a LazyColumn
- Adding a LazyVeriticalGrid
- Spacing Your Data Items
- Adding Space to a LazyRow
- Adding Space to a LazyColumn
- Adding Space to a LazyVerticalGrid
- Dealing With State on Lazy Composables
- Passing State to the Lazy Composables
- Understanding remember and derivedStateOf
- Using State to Scroll to the Top of the List
- Performance Concerns with Lazy Composables
- Adding Keys to List Items
- Avoiding Operations Inside of Composables
- Where to Go From Here
Adding a LazyColumn
Next, open CatFeedScreen.kt and replace the // TODO: Show cats in a scrollable column
with the following code:
LazyColumn {
stickyHeader {
CuteCatsHeader()
}
items(cats) {
CatItem(cat = it)
}
}
Remember that the DSL for Lazy composables allows you to emit items of different types. In this case, you add a stickyHeader
at the top of the list, followed by a list of CatItem
using items
. Per the stickyHeader
documentation, it’s an experimental feature for now. The annotation ExperimentalFoundationApi
has been added to the top of the file above your imports:
@file:OptIn(ExperimentalFoundationApi::class)
Adding a LazyVeriticalGrid
Last but not least, the LazyVerticalGrid
makes it easy to implement a grid view. This layout is also an experimental version, so the ExperimentalFoundationApi
annotation is useful for this as well. To implement this, find the //TODO: Display cats in grid view
comment and replace it with the code below:
LazyVerticalGrid(
cells = GridCells.Fixed(2),
) {
item {
CuteCatsHeader()
}
items(cats) {
CatItem(cat = it)
}
}
The code above adds a LazyVerticalGrid
to serve as the grid view. This layout offers some different elements — like the cells
parameter where you define what kind of grid you want, fixed or adaptative.
Here, you use a fixed length of two items using GridCells.Fixed(2)
. At the end, you’re just adding the list of CatItem
, like in the previous step, but this time it will render in grid mode. Also, the item
receiver emits an item intending it to be part of the list of elements that render on the grid, no matter if it’s of the same type or not. This is incredibly beneficial for multiple scenarios. In the old RecyclerView
/ GridView
approach, you needed to create multiple adapters for a View
, but now you just need a single item.
Build and run, to grasp what you achieved tap on the toggle icon on the top right corner and it will automatically change the layout. Here’s what you’ll see:
Congrats! You finished this section successfully. In the following sections, you’re going to improve this app and take it to another level.
Spacing Your Data Items
You may have noticed that the elements inside your Lazy composables are very close together — and it doesn’t look ideal. Fortunately, Lazy composables offer attributes to handle these scenarios efficiently.
To add some spacing between items, you can use the Arrangement.spacedBy
via the verticalArrangement
and horizontalArrangement
parameters. To add padding around the edges of the content, pass PaddingValues
to the contentPadding
parameter.
Adding Space to a LazyRow
Open the CatItem.kt file and go to the CatItem
composable. Find the LazyRow
that you implemented previously and add this code:
LazyRow(
modifier = Modifier.align(Alignment.CenterHorizontally),
// New horizontal content spacing
horizontalArrangement = Arrangement.spacedBy(12.dp),
)
...
}
The code above adds space between the items inside the row of tags. As you can imagine, the horizontalArrangement
parameter is only available for LazyRow
just as verticalArrangement
is only available for LazyColumn
.
Adding Space to a LazyColumn
Next, move to CatFeedScreen.kt and then to the LazyListCats
composable. Find the LazyColumn
you implemented previously and add these lines of code as parameters:
@Composable
fun LazyListCats(cats: List<Cat>, state: LazyListState) {
LazyColumn(
// New content padding
contentPadding = PaddingValues(horizontal = 32.dp, vertical = 16.dp),
// New vertical spacing
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
...
}
The content padding adds some padding around the content of the LazyColumn
— specifically, 32.dp
horizontal and 16.dp
vertical. Very easy, right? As you’ve already learned, the arrangement will add spacing between the items of the column, but this time on the vertical axis.
Adding Space to a LazyVerticalGrid
In the same file, look for the LazyGridCats
composable. Find the LazyColumn
you implemented previously, and add these lines of code as parameters:
@Composable
fun LazyGridCats(cats: List<Cat>, state: LazyListState) {
LazyVerticalGrid(
cells = GridCells.Fixed(2),
// Content padding for the grid
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
// LazyGrid supports both vertical and horizontal arrangement
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
) {
...
}
}
Notice the LazyVerticalGrid
also supports the content padding and arrangement, and in this case, both horizontal and vertical at the same time. It’s very helpful for dealing with different spacing scenarios on lists and grids without modifying the child composable.
Build and run, to grasp what you achieved tap on the toggle icon on the top right corner and it will automatically change the layout. Here’s what you’ll see:
Hooray! You achieved a new goal. The app now looks amazing, and you learned about some of the properties the Lazy composables offer to you. Now it’s time to add more fantastic features to this app.
Dealing With State on Lazy Composables
One of the biggest advantages of Lazy composables over non-lazy approaches like Column
or Row
is that you can interact with the state of the layout. But what exactly is the state? It’s an object you can use to control and observe scrolling. You’ll access this using the rememberLazyListState
method. You can easily access different attributes of this object that allow you to create fabulous features — like the scroll-to-the-top feature you’ll add now with just a few lines of code.
Passing State to the Lazy Composables
You probably already noticed the LazyGridCats
and LazyListCats
composables have an unused LazyListState
parameter. To take advantage of it, go to the CatsFeedScreen.kt file, then find the following composables. Finally, change the following to define the state parameter for both composables.
@Composable
fun LazyGridCats(cats: List<Cat>, state: LazyListState) {
LazyVerticalGrid(
cells = GridCells.Fixed(2),
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
// Add LazyListState controller for grid
state = state,
) {
...
}
}
@Composable
fun LazyListCats(cats: List<Cat>, state: LazyListState) {
LazyColumn(
contentPadding = PaddingValues(horizontal = 32.dp, vertical = 16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
// Add LazyListState controller for column
state = state,
) {
...
}
}
This will allow you to have control and give instructions to the Lazy composables in a very straightforward way.
Understanding remember and derivedStateOf
In the same file, find the //TODO: Use derivedStateOf to check index state
comment and replace it with the following code:
val showScrollToTop = remember {
derivedStateOf {
lazyListState.firstVisibleItemIndex > 0
}
}
showScrollToTop
is a variable that’s true
if the first visible item on the list is greater than the first item in the entire list.
Here’s how this works:
-
remember
is a method that returns the value produced during the composition of the composable. During recomposition, it will always return the value produced by composition. That’s incredibly useful to perform some heavy operations and avoid being called again for every recomposition. -
derivedStateOf
creates an object whose value returns a cached result. Calling the value from this object repeatedly won’t cause the operation to call again. Taking advantage of it, theshowScrollToTop
variable will change only when the instance ofLazyListState
changes its value, and not for every recomposition or invocation of the variable.