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.

Courses

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:

  1. Build off the previous bookshelf app exercise by converting the book item into a list of books.
  2. 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.

Import extension function
@Composable
fun PracticeBookList(bookList: List<Book>) {
    LazyColumn {
        items(bookList) { book ->
            BookListItem(book)
        }
    }
}

Here is the final result:

Animation of a book list built in Compose scrolling

Exercise 2: Todo List App

Description:

For this task:

  1. Build off the previous todo list app exercise by converting the task item composable into a scrollable list.
  2. 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:

Animation of a task list built in Compose scrolling