Skip to content

Intermediate: Android basics · Jetpack Compose UI · topic 25 of 50 · 30 min

Lists and grids

Explanation

LazyColumn and LazyRow show long, scrollable lists efficiently: only the items on screen (plus a few) are composed. Fill them with items(list, key = { it.id }) { item -> ... }. Stable keys keep scroll position and state correct when items move. LazyVerticalGrid shows a grid with GridCells.Fixed or GridCells.Adaptive.

Why lazy lists?

A WhatsApp chat list, a Flipkart search result, a bank statement: lists can have thousands of items. A normal Column with verticalScroll composes and measures every item, even those far off-screen, which wastes memory and makes the screen slow. Lazy layouts compose only the items that are visible (plus a small buffer), and reuse work as you scroll.

kotlin
data class Txn(val id: Long, val title: String, val amount: Int)

@Composable
fun Statement(txns: List<Txn>) {
    LazyColumn(
        contentPadding = PaddingValues(16.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp),
    ) {
        item { Text("October 2026", style = MaterialTheme.typography.titleMedium) }   // a single header
        items(txns, key = { it.id }) { txn ->
            Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
                Text(txn.title)
                Text("Rs ${txn.amount}")
            }
        }
    }
}
The block after LazyColumn is not composable UI but a DSL that describes items: item for one, items for many.

The data for a list should come from state, typically a List exposed by a ViewModel. When that list changes, for example after a new transaction arrives, the LazyColumn recomposes and shows the new items. Keep each row composable small and pass it only the data it needs.

Keys

By default, items are identified by their position. If the list changes (a new message at the top, an item deleted), positions shift and Compose may attach state, like an expanded card or a text field's content, to the wrong item. Giving each item a stable unique key, usually its database id, fixes this and lets Compose animate changes. Keys must be unique within the list.

Clickable items and dividers

kotlin
items(contacts, key = { it.phone }) { c ->
    ListItem(
        headlineContent = { Text(c.name) },
        supportingContent = { Text(c.phone) },
        modifier = Modifier.clickable { onOpen(c) },
    )
    HorizontalDivider()
}
ListItem is a ready Material 3 row layout. Pass a lambda like onOpen from the parent (state hoisting).

LazyRow

LazyRow works the same way horizontally, for carousels such as "Recently viewed" or category chips. A screen can have a LazyColumn containing LazyRows, as long as each lazy layout scrolls in a different direction.

Grids

kotlin
@Composable
fun ProductGrid(products: List<String>) {
    LazyVerticalGrid(
        columns = GridCells.Adaptive(minSize = 150.dp),   // as many columns as fit
        contentPadding = PaddingValues(8.dp),
        horizontalArrangement = Arrangement.spacedBy(8.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp),
    ) {
        items(products) { name ->
            Card { Text(name, Modifier.padding(16.dp)) }
        }
    }
}
GridCells.Fixed(2) always gives 2 columns; Adaptive gives 2 on a phone and more on a tablet.

Scroll state

rememberLazyListState() gives you the list's scroll state. You can read which item is first visible (to show a "scroll to top" button) and scroll in code with a coroutine: scope.launch { listState.animateScrollToItem(0) }.

Think of it like this: A lazy list is like a train's window: you only see a few fields at a time, and the scenery is painted just as it comes into view. A Column with all items is like printing the entire route map before the journey even starts.

Common mistakes

  • Using Column with verticalScroll for hundreds of items: slow and memory-hungry. Use LazyColumn.
  • Putting a LazyColumn inside a Column with verticalScroll: crash ("Vertically scrollable component was measured with an infinity maximum height constraints"). Make the whole screen one LazyColumn with item { } sections.
  • Duplicate keys: the app crashes. Keys must be unique.
  • Doing heavy work (like formatting dates for every item) on each recomposition. Prepare the data in the ViewModel.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Lists and grids · 1/6

LazyColumn | LazyRow | LazyVerticalGrid

Long lists need lazy layouts. They compose only the items on screen.

Prefer a video by a teacher? Find free videos on “jetpack compose lazycolumn items key lazyverticalgrid tutorial” on YouTube ↗

In real life: A restaurant menu with categories

A restaurant ordering app shows a long menu: a header for each category (Starters, Main course, Desserts) and dishes under each. One LazyColumn uses item { } for each category header and items(dishes, key = { it.id }) for the dishes. Only visible dishes are composed, so scrolling stays smooth on a budget phone, and stable keys keep each dish's quantity counter attached to the right dish when the menu is filtered.

kotlin
@Composable
fun Menu(categories: Map<String, List<Dish>>) {
    LazyColumn {
        categories.forEach { (category, dishes) ->
            item(key = category) { Text(category, style = MaterialTheme.typography.titleLarge) }
            items(dishes, key = { it.id }) { dish -> DishRow(dish) }
        }
    }
}
Dish and DishRow are your own data class and composable.

Practice (0/3 done)

Task 1 · Question

Why is LazyColumn better than Column(Modifier.verticalScroll(...)) for 5,000 items?

Task 2 · Question

Which grid gives more columns on a tablet than on a phone?

Task 3 · Practice

Build a contacts screen: a LazyColumn with a header item, then contacts sorted by name with key = phone, each a clickable ListItem with a divider. Add a "Scroll to top" button that appears when the first visible item index is above 5 and scrolls back with animateScrollToItem.

Quick quiz

Question 1

1.What does a key in items(list, key = { it.id }) do?

Question 2

2.What happens with duplicate keys in a LazyColumn?

Question 3

3.Which is the right way to build a scrolling screen with a header and a long list?

Question 4

4.Which composable shows a horizontal carousel efficiently?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What does a key in items(list, key = { it.id }) do?

Answer: Identifies each item stably so state and animations follow the right item

Keys survive reordering.

What happens with duplicate keys in a LazyColumn?

Answer: The app crashes

Keys must be unique.

Which is the right way to build a scrolling screen with a header and a long list?

Answer: One LazyColumn with item { header } and items { }

Nested same-direction scrolling crashes.

Which composable shows a horizontal carousel efficiently?

Answer: LazyRow

LazyRow is the horizontal lazy list.

What does lazy building mean for ListView.builder?Related

Answer: Items are built only when they are about to be visible

Saves memory and time.

From Lists with ListView

Which delegate always uses exactly 3 columns?Related

Answer: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3)

Fixed count fixes the columns.

From Grids with GridView

Which value keeps a header visible while you scroll its section, but in the normal flow before that?Related

Answer: sticky

sticky behaves normally until it reaches the given edge, then sticks.

From Positioning and stacking

Why must a ViewModel not hold an Activity reference?Related

Answer: The ViewModel outlives the activity, causing a memory leak

The activity is recreated on rotation.

From Interview questions: Android and Compose

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 1 more topic in “Jetpack Compose UI” before its exam: Text input and forms.
  2. Module 6 exam: Jetpack Compose UI · 13 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Navigation and app architecture. 50 topics left in the course before the final exam.

← Previous: Layouts: Column, Row, Box and Scaffold