Jetpack Compose Tutorial: Lesson 4
Today, I kept going with Google's Compose Essentials course. Here's a peek into what I explored and learned.
Part 1: Lists
Lists on Compose are pretty straightforward. You can use the LazyColumn
or LazyRow
composables to create a list of items. The LazyColumn
composable is a vertically scrolling list that only composes and lays out the currently visible items. The LazyRow
composable is the horizontally scrolling variant.
LazyColumn {
items(messages) { message ->
MessageCard(message)
}
}
The way that these lists are set up, there is no need for complicated adapters.
The data objects are passed directly into the items
function and the MessageCard
composable is used to display each item.
This would make it really easy to make lists of different types of items. Using RecyclerView Adapters would have you set up view type management, bind the data to the views and tailor the UI to suit the viewtype. AirBnb's Epoxy framework was created to make this easier, but it still required a lot of setup.
The fact that composables are deterministic, means that I don't need to worry about the minutiae of the list item's view.
I can trust that the MessageCard
composable will properly display the data that I pass into it.
Part 2: Animation
This chapter introduces a few new concepts:
- State
- Recomposition
- Animation
There's a lot to unpack in these concepts but the brief lesson was to dip your feet into the water and get a feel for how these concepts work. The exercise adds a state to remember whether a message is expanded. Each message has it's own state value.
var isExpanded by remember { mutableStateOf(false) }
Every time the state is updated, the composable is recomposed. This means that the composable is redrawn with the new state value.
The important note is that when using these states, you might need to import these functions:
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
The animation in the exercise adds a new attribute to the Modifier of the Surface composable.
Surface(
...
modifier = Modifier.animateContentSize().padding(1.dp)
)
This acts like the animateLayoutChanges
attribute in Android XML.
Deep Dive Challenges
Scenario Exercise 1: Bookshelf App
Description:
For this task:
- Build off the previous bookshelf app exercise by converting the book item into a list of books.
- Add a simple animation for when the user taps on a book item.
Implementation:
Building the list is pretty straightforward.
I just need to add a LazyColumn
to use the BookListItem
to display each book.
@Composable
fun PracticeBookList(bookList: List<Book>) {
LazyColumn {
items(bookList.size) { index ->
BookListItem(bookList[index])
}
}
}
Here the items
function iterates through the list of books by index.
There is an extension function that allows you to use the items
function with the list of items directly.
@Composable
fun PracticeBookList(bookList: List<Book>) {
LazyColumn {
items(bookList) { book ->
BookListItem(book)
}
}
}
Here is the final result:
Exercise 2: Todo List App
Description:
For this task:
- Build off the previous todo list app exercise by converting the task item composable into a scrollable list.
- Add a simple animation for when the user taps on an item.
Implementation:
Just like the exercise before, I need to add a LazyColumn
to use the TodoListItem
to display each task.
@Composable
fun PracticeTodoList(taskList: List<Task>) {
LazyColumn {
items(taskList) { task ->
TodoListItem(task)
}
}
}
Again, I used the extension function that allows you to use the items
function with the list of items directly.
Creating the list of tasks to pass into the preview:
@Preview
@Composable
fun PreviewPracticeTodoList() {
PracticeBookTheme {
Surface {
PracticeTodoList(
listOf(
Task(
taskName = "Throw out the trash",
dueDate = Date(),
done = false,
),
Task(
taskName = "Take out recycling",
dueDate = Date(),
done = false,
),
Task(
taskName = "Wash the dishes",
dueDate = Date(),
done = false,
),
Task(
taskName = "Clean the bathroom",
dueDate = Date(),
done = false,
),
Task(
taskName = "Scrub the floors",
dueDate = Date(),
done = false,
),
Task(
taskName = "Wipe the windows",
dueDate = Date(),
done = false,
),
Task(
taskName = "Dust the shelves",
dueDate = Date(),
done = false,
),
)
)
}
}
}
Here is the final result: