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
One of the most common things apps do is display data. From your phone contacts to your favorite artist’s songs on Spotify, you’re always viewing sorted information that’s been formatted in some way — columns, rows, grids, and more.
Different platforms do this in different ways, of course, and methods have changed over time. Today’s Android apps use Jetpack Compose with Lazy composables — a modern, easy and efficient solution to display large lists of data. Android developers have evolved from using the now-deprecated ListView
to the current RecyclerView
. Both methods use XML
code to represent the user interface and create adapters to handle each element.
Jetpack Compose was introduced at Google I/O 2019. It completely removed the XML
code and offered a much easier way to handle common features like displaying data.
In this tutorial, you’ll learn about Lazy composables in Jetpack Compose. Specifically, you’ll learn:
- What a Lazy composable is and how it works under the hood.
- How to work with
LazyColumn
,LazyRow
and Lazy grids. - About the main benefits of using them over the non-lazy options.
Getting Started
Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial.
You’re going to build an app that allows you to display information about cute cats in list and grid formats using Lazy composables. The sample app has a very simple project structure:
-
data.model: The
Cat
model that represents the structure of what you’re going to retrieve from the internet service. Cute cats! - data.network: The requests to external services. Retrofit is the third-party library you’re going to use during this project to make HTTP requests. It’s straightforward and useful.
-
data.repository:
FeedRepository
has the responsibility to make the API calls and handle the response. - ui.theme: In this package lives some theme configurations that come by default with the Compose starter project boilerplate. They’re not the main focus of this article, though.
-
ui.cats: This is the starting point and the main package for this tutorial. You’re going to work with composables and learn how to integrate them into the app. The
CatItem
represents the list item, andCatFeedScreen
is the whole composable screen that displays the information for those cute cats. You can also find theCatFeedViewModel
that deals with the current state of the app here.
Build and run.
You’ll see an empty screen — that’s because you haven’t put the Lazy composables in place yet.
Understanding Lazy Lists
To better understand the benefits Lazy composables offer, you first have to understand what they are and how they work.
Imagine that you want to display a large amount of data with an unknown number of items. If you decide to use a Column/Row
layout, this could translate into a lot of performance issues because all the items will compose whether they’re visible or not. The Lazy option lets you lay out the components when they’re visible in the widget viewport. The available list of components includes LazyColumn
, LazyRow
and their grid alternatives.
Lazy composables follow the same set of principles as the RecyclerView
widget but remove all that annoying boilerplate code.
Understanding LazyListScope
An interesting detail about Lazy composables is that they’re slightly different from other kinds of layouts because instead of expecting a @Composable
instance, they offer a DSL block from LazyListScope
. DSL, or Domain Specific Language, allows the app to create specific instructions to solve a specific problem. In these scenarios, Kotlin uses type-safe builders to create a DSL that fits perfectly for building complex hierarchical data structures in a semi-declarative way. The LazyListScope
plays the role of a receiver scope in LazyRow
and LazyColumn
.
@LazyScopeMarker
interface LazyListScope {
// 1
fun item(key: Any? = null, content: @Composable LazyItemScope.() -> Unit)
// 2
fun items(
count: Int,
key: ((index: Int) -> Any)? = null,
itemContent: @Composable LazyItemScope.(index: Int) -> Unit
)
// 3
@ExperimentalFoundationApi
fun stickyHeader(key: Any? = null, content: @Composable LazyItemScope.() -> Unit)
}
Here’s what’s happening in the code above:
- The
item
receiver allows adding a singlecomposable
item into the Lazy layout. You can add as manyitem
receivers as you like, but if you want to add many, check theitems
option below. - The
items
receiver expects a count of items instead of defining the content of every item individually. Here, you define the length of the list and create the specifications for every item. - Finally, the
stickyHeader
adds a sticky item at the top. It will remain pinned even when scrolling. The header will remain pinned until the next header takes its place. This is very useful for sub-list scenarios like contact apps or movie categories.
In the next section, you’re going to practice what you’ve learned about Lazy composables and learn how to implement them in the app you’re building.
Adding Lazy Composables in Your App
Now it’s time for the fun part: coding.
First, be sure you already have the right dependencies for this tutorial. Open the build.gradle file for the module, and notice it has the following dependencies:
def compose_version = "1.1.1"
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
Adding a LazyRow
Now that you have confirmed the dependencies, open CatItem.kt and look for the // TODO : Add list of tags
comment. Replace it with this:
LazyRow(
modifier = Modifier.align(Alignment.CenterHorizontally)
) {
items(cat.tags) {
CatTag(tag = it)
}
}
The code above takes advantage of LazyRow
to display the tags of a cat item in a horizontal scrollable view using the CatTag
composable function.
If you’re a very detailed reader, you’ll notice the items
method you’re implementing is slightly different from the previous one. This one expects a List
of items instead of the count. But how is this possible?
Just remember that Kotlin offers extension functions to make your life easier. Command-click
or Control-click
the method, and you’ll be redirected to the implementation. The code is shown below:
inline fun <T> LazyListScope.items(
items: List<T>,
noinline key: ((item: T) -> Any)? = null,
crossinline itemContent: @Composable LazyItemScope.(item: T) -> Unit
) = items(items.size, if (key != null) { index: Int -> key(items[index]) } else null) {
itemContent(items[it])
}
Notice how the method passes the item’s size to LazyListScope.items
and also deals with the keys for the scenario when they’re not null.