Jetpack Compose: lists primer

Android Oct 10, 2021

It's been a while since Jetpack Compose is finally in a stable form. Some say that might not be ready for prime time (yet) and not every UI can be implemented using Compose.

Nevertheless, it's the future of Android UI and I think it's more than capable to handle all but few complicated scenarios. As a bonus, you also get a very enjoyable experience when building UIs (instead of being a chore)!

Without further ado, let's have a taste of one of the most basic building blocks of a modern UI: lists. No more RecyclerView.Adapter or any other adapter. Compose takes away all the complexities of building an efficient list.

Small list

For a simple small list, things are quite simple: use a Column and the items will stack one after the other.

Keep in mind that the number of items on such a list must be kept small. All the items are drawn on the screen. So potential performance issues might emerge if the list is long.

For instance, if we have a list of tasks that we want to display you can use the following snippet.

    @Composable
    fun smallList(tasks: List<Task>) {
        Column {
            tasks.forEach { task ->
                Text(task.text)
            }
        }
    }

Large list

For a "real-world" list, where only some items are visible and you need to scroll to view the rest, you can use LazyColumn. This is the replacement for the verbose RecyclerView.Adapter of the XML-based Android UI world.

Using this composable, only the visible items will be drawn on the screen. As soon as the list is scrolled, the new items will be drawn on the screen with efficiency (i.e. some views that go out of sight might be reused). But these are behind the screens. All you need to do is provide the items to be drawn.

    @Composable
    fun largeList(tasks: List<Task>) {
        LazyColumn(
            contentPadding = // 1.
                PaddingValues(horizontal = 16.dp, vertical = 8.dp))
        {
            items(tasks, // 2.
                key = { task -> task.id } // 3.
            ) { task ->
                Text( // 4.
                    task.text,
                    Modifier // 5.
                        .clickable( // 6.
                            onClick = {
                                viewModel.onItemClick(task)
                            },
                            interactionSource = MutableInteractionSource(),
                            indication = rememberRipple(bounded = true), // 7.
                        )
                )
            }
        }
    }
  1. For having some space between each row items.
  2. Use the items() method to provide the rows that we want to display in the list.
  3. By default, each item in the list is using the item position as a unique identifier. This can cause problems when the ordering of the list changes. State of reordered items might be lost. When possible, provide a real unique identifier of the underlying data.
  4. In this simplified case, each row in the list is represented by a simple Text. But here you can have any Composable method representing each row.
  5. Use the Modifier of the row composable to customize each row.
  6. This is to make the row clickable (and providing what happens when the row is clicked).
  7. This is for having a "ripple" effect when clicking on a row. Alternatively, you can just provide a theme for the entire app (learn more).

Further reading

This aims to be a quick primer for lists in Compose. But there are some quite cool things you can do easily with LazyColumn such as sticky headers, reacting, and controlling scroll position.

Notable things you cannot do at the moment are having items animations and drag-and-drop reordering (but hopefully will soon be implemented; some workarounds/hacks/external libraries exist).

Happy coding!

Tags

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.