(upbeat music)
- [Chris] In this screencast,
we'll walk through the Jetpack Compose list APIs,
and show you how simple it is
to display a long list of items in Compose.
The app we're going to use
is a simple contacts list application,
which you can see written on the right,
and in this video,
we're going to complete each of the tasks you can see
to display a fully featured list UI.
Let's jump into the first task on our code.
Currently, the list is implemented as a simple column,
and our first task is to change that
to use a component called LazyColumn.
LazyColumn only lays out the items
which are visible on screen.
If you've used the Android RecyclerView widget,
it's based on the same principles.
Let's go ahead and swap out Column for LazyColumn.
You'll notice that we now see an error message,
that we can't use our contact list item composable
in our LazyColumn.
This is because LazyColumn's API
is different to most of the layouts in Compose
and is built as a DSL.
Instead of the forEach,
we're going to use the items function provided by LazyList,
passing in our list of contacts.
The lambda defines the content of each of the items
so we can keep the existing code, and that's it.
We've converted our column to be a LazyColumn.
If we quickly check out the preview
and then try running it,
we can see that it scrolls as expected.
We now have a simple list containing one type of item,
but LazyColumn makes it easy
to display many different item types.
Let's group our contacts by their initial
and add a header between the groups.
First up, we're going to need to group our list of contacts.
To do that, we use contacts.groupby function,
return the first character of their name.
Bit of a disclaimer here.
This group in code should really live in your view model
rather than within UI,
but for the sake of this demo, we're going to keep it here.
We now need to update our items code, too.
As we're now iterating for a map,
we need to loop for all the map entries
and then use items on each sublist of contacts.
If we quickly run it,
you'll see that it now looks exactly the same as before.
Now we need to add our header.
To add a single item, all we need to do
is call item function.
We can use our CharacterHeader composable,
which we already have set up.
When you run that, you can now see that we have headers
for each of the group of contacts.
Here's something really cool, though.
Compose also has experimental support for sticky headers.
Instead of item,
we just need to use a stickyHeader function.
StickyHeader is experimental,
which means that we need to opt in to use it.
If we run that, you'll now see
that the group headers stay in place
once they're scrolled off.
If you've ever tried to achieve this pattern
with RecyclerView,
you'll know that it's very difficult;
Compose, though, makes it super easy.
So what next?
Well, for long lists, it could be helpful
to the user to display a scroll to top button.
If you break this down, there's two steps to this.
The first is only showing the button once the user
has scrolled past a certain point,
and the second is scroll another list
once the button is clicked.
So let's tackle the first step.
We can add our ScrollToTopButton composable
to our outer box,
and this allows us to float the button above the list.
You can see it running on the top left of the screen.
We'll move it to the bottom later.
To react to the scroll position changes in LazyColumn,
we need to use the LazyListState.
We can remember our own instance by calling
rememberLazyListState
and then pass that to our LazyColumn, like so.
LazyListState contains a lot of information
about what items are currently displayed,
but we're going to keep it simple.
We'll display the button
if the first item is not visible.
To do that, we're going to create
a property called showButton,
which is true whenever the firstVisibleItemIndex
is greater than zero.
We can now wrap our button with AnimatedVisibility
to animate the button on and off screen.
We tweak the animation to be a simple fade in and out,
add some padding, and then float the button
to the bottom of the screen.
If you run the app now,
we'll see that the button now shows and hides
based on the scroll position.
If we scroll to the bottom of the list,
you can now see the button is covering
the last item in the list.
To fix that, we can add some content padding.
We're going to use the contentPadding parameter
on LazyColumn and pass a PaddingValues instance
with the bottom set to 80 dips.
If you run that and scroll to the bottom of the list,
you'll see that the last item is no longer covered.
So we now have the button being displayed correctly,
but we need to make the button actually work.
We've only used LazyListState so far
to read the scroll position,
but it also provides functions
to update the scroll position, too.
The function we're going to use for this demo
is the scrolltoitem function,
which immediately scrolls the list of the given index.
The scrolltoitem is a suspending function,
which means that we need to run it within a coroutine.
To do that, we need a coroutine scope to be able to launch,
and Compose has us covered.
We can use the rememberCoroutineScope function
which provides us with a coroutine scope,
which is scoped to our composable's lifecycle.
With that, we can call launch on our scope
and then call scrollToItem,
passing in zero to snap to the first item, and that's it.
If you run the app and then scroll
and then press the button,
you can see that we now scroll back to the first item.
That was an overview of the Jetpack Compose list APIs,
but there's more.
In this demo, we've only spoken about using LazyColumn
to implement a vertical list,
but Compose also provides other types of lazy containers,
such as LazyRow, which enables horizontal lists,
and the experimental LazyVerticalGrid.
They all are very similar APIs,
so you can reuse what you've learned in this video
for all of them.
To learn more about Jetpack Compose,
check out the pathway linked in the description.
Thanks for watching.
(upbeat music)