Lazy Layouts in Jetpack Compose

Learn how to use Lazy Composables in Jetpack Compose to simply display data in your app. By Enzo Lizama.

5 (4) · 1 Review

Download materials
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

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:

https://koenig-media.raywenderlich.com/uploads/2022/06/2-add-lazy-composables.gif

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:

https://koenig-media.raywenderlich.com/uploads/2022/06/3-spacing-composables.gif

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, the showScrollToTop variable will change only when the instance of LazyListState changes its value, and not for every recomposition or invocation of the variable.