Chapters

Hide chapters

Jetpack Compose by Tutorials

Second Edition · Android 13 · Kotlin 1.7 · Android Studio Dolphin

Section VI: Appendices

Section 6: 1 chapter
Show chapters Hide chapters

4. Building Lists With Jetpack Compose
Written by Prateek Prasad

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

In previous chapters, you learned about different elements in Compose and how to group and position them inside layouts to build complex UIs. Using that knowledge, you can create most screens you can think of.

However, you’ve not learned how to build one of the most common UI components mobile apps use. What happens when you have to display more elements than you can fit on the screen? In that case, while the elements are all composed, the limited screen size prevents you from seeing all of them. There are even situations where you want to dynamically add new elements on the screen and still be able to see them all, like a social media feed for instance.

The solution to this problem is allowing your content to scroll, either vertically or horizontally. The traditional way of achieving this in XML is to use ScrollView, which allows you to scroll content vertically. For horizontal scrolling, you use HorizontalScrollView. Both of them can have only one child view inside them, so to add multiple elements, you need to use a single layout that wraps those elements.

Jetpack Compose gives you a new way to achieve the same result — using scrollable and lazily composed containers.

In this chapter, you’ll learn how to make lists and grids in Jetpack Compose. You’ll learn how to show content that scrolls vertically or horizontally and how to build an alternative for the traditional RecyclerView using composable functions.

Using Vertical Scrolling Modifiers

As you know by now, Column is the replacement for LinearLayout in the vertical orientation. In Jetpack Compose, you can use the same Column composable with an additional modifier that enables scrolling! Let’s see how to implement a simple scrolling Column.

To follow along with the code examples, open Android Studio and select Open an Existing Project. Then, navigate to 04-building-lists-with-jetpack-compose/projects and select the starter folder.

Once the project builds, you’ll see the following structure:

Project Structure
Project Structure

You’ll start off by building a vertically scrollable Column after which you’ll explore its horizontal counterpart. To do that, open ScrollingScreen.kt and you’ll see two composable functions — ScrollingScreen() and MyScrollingScreen():

@Composable
fun ScrollingScreen() {
  MyScrollingScreen()

  BackButtonHandler {
    JetFundamentalsRouter.navigateTo(Screen.Navigation)
  }
}

@Composable
fun MyScrollingScreen() {
  //TODO add your code here
}

@Composable
fun BookImage(@DrawableRes imageResId: Int, @StringRes contentDescriptionResId: Int){
  Image(
    bitmap = ImageBitmap.imageResource(imageResId),
    contentDescription = stringResource(contentDescriptionResId),
    contentScale = ContentScale.FillBounds,
    modifier = Modifier.size(476.dp, 616.dp)
  )
}

As in the previous chapters, ScrollingScreen() is already set up to handle the back navigation, so you only need to implement MyScrollingScreen(). There is also a BookImage composable which is predefined. It creates an image of a book in a specific size with the image and content description passed as a parameter.

Change the code of MyScrollingScreen() to the following, and include the required imports with the help of Android Studio:

@Composable
fun MyScrollingScreen(modifier: Modifier = Modifier) {
  Column(modifier = modifier.verticalScroll(rememberScrollState())) {
    BookImage(R.drawable.advanced_architecture_android, R.string.advanced_architecture_android)
    BookImage(R.drawable.kotlin_aprentice, R.string.kotlin_apprentice)
    BookImage(R.drawable.kotlin_coroutines, R.string.kotlin_coroutines)
  }
}

Here, you added three existing BookImage composables to the Column. You used existing drawable and string resources for the parameters. To make the Column scrollable, you called verticalScroll() , and passed in rememberScrollState(). This creates a scroll state based on the scroll orientation and persists the scroll position so it isn’t lost after recomposition.

What happens here is that you’ll show a Column, a vertical list of items. But if the items no longer fit the screen, it will be scrollable and you’ll be able to go through each item individually.

Build and run the app, then select Scrolling from the navigation menu. You’ll see the three images, one below the other — but unfortunately, they don’t fit on the screen together. Luckily, you made the screen scrollable! :]

Scroll down to see the images that aren’t displayed yet.

Scrolling Column
Scrolling Column

Using a scrollable Column is very easy, but there is much more you can do with it. Let’s explore how it works.

Exploring the Scrollable Modifier

Look at its source code to see what a verticalScroll can do and how it works when you use it:

fun Modifier.verticalScroll(
  state: ScrollState,
  enabled: Boolean = true,
  flingBehavior: FlingBehavior? = null,
  reverseScrolling: Boolean = false
)

Using Horizontal Scrolling Modifiers

Vertical scrolling now works on your screen — but in some cases you need a horizontal scroll, instead.

@Composable
fun MyScrollingScreen(modifier: Modifier = Modifier) {
  Row(modifier = modifier.horizontalScroll(rememberScrollState())) { // here
    ...
  }
}
Scrolling Row
Ldjuxnoqt Cuf

Lists in Compose

To display a large collection of elements in Android, you used the RecyclerView. The only elements RecyclerView renders are the ones visible on the screen. Only after the user begins to scroll does it render the new elements and display them on screen. It then recycles the elements that go off the screen into a pool of view holders.

Introducing LazyColumn & LazyRow

LazyColumn and LazyRow are used for vertical and horizontal scenarios, respectively.

Creating Lists With LazyColumn & LazyRow

There are many awesome books in our kodeco.com library and in different categories. It’s best to show them all categorized, so you can easily pick and choose your favorites.

Book Categories
Duuf Mohafeweuj

@Composable
fun ListScreen() {
  MyList()
  BackButtonHandler {
    JetFundamentalsRouter.navigateTo(Screen.Navigation)
  }
}

@Composable
fun MyList() {
  //TODO add your code here
}

@Composable
fun ListItem(bookCategory: BookCategory, modifier: Modifier = Modifier) {
  //TODO add your code here
}
@Composable
fun MyList() {
  LazyColumn {
    items(items) { item -> ListItem(item) }
  }
}
@Composable
fun ListItem(bookCategory: BookCategory, modifier: Modifier = Modifier) {
  Column(modifier = Modifier.padding(8.dp)) {
    Text(
      text = stringResource(bookCategory.categoryResourceId),
      fontSize = 22.sp,
      fontWeight = FontWeight.Bold,
      color = colorResource(id = R.color.colorPrimary)
    )
    Spacer(modifier = modifier.height(8.dp))

    // TODO
  }
}
LazyRow {
  items(bookCategory.bookImageResources) { items ->
    BookImage(items)
  }
}
@Composable
fun BookImage(imageResource: Int) {
  Image(
    modifier = Modifier.size(170.dp, 200.dp),
    painter = painterResource(id = imageResource),
    contentScale = ContentScale.Fit,
    contentDescription = stringResource(R.string.book_image)
  )
}
List
Talm

Exploring Lists

Now you understand the difference and how to implement specific lists, take a look at the signature for LazyColumn and LazyRow:

@Composable
fun LazyColumn(
  modifier: Modifier = Modifier,
  state: LazyListState = rememberLazyListState(),
  contentPadding: PaddingValues = PaddingValues(0.dp),
  reverseLayout: Boolean = false,
  verticalArrangement: Arrangement.Vertical =
      if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
  horizontalAlignment: Alignment.Horizontal = Alignment.Start,
  flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
  userScrollEnabled: Boolean = true,
  content: LazyListScope.() -> Unit
)

@Composable
fun LazyRow(
  modifier: Modifier = Modifier,
  state: LazyListState = rememberLazyListState(),
  contentPadding: PaddingValues = PaddingValues(0.dp),
  reverseLayout: Boolean = false,
  horizontalArrangement: Arrangement.Horizontal =
      if (!reverseLayout) Arrangement.Start else Arrangement.End,
  verticalAlignment: Alignment.Vertical = Alignment.Top,
  flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
  userScrollEnabled: Boolean = true,
  content: LazyListScope.() -> Unit
)
interface LazyListScope {

    fun item(key: Any? = null,contentType: Any? = null,content: @Composable LazyItemScope.() -> Unit)

    fun items(
        count: Int,
        key: ((index: Int) -> Any)? = null,
        contentType: (index: Int) -> Any? = { null },
        itemContent: @Composable LazyItemScope.(index: Int) -> Unit
    )

    @ExperimentalFoundationApi
    fun stickyHeader(key: Any? = null, contentType: Any? = null, content: @Composable LazyItemScope.() -> Unit)
}

Grids in Compose

When working with a RecyclerView, you can use different types of LayoutManagers to place your elements on the screen in different ways. To make grids, for example, you use a GridLayoutManager and then set the number of columns inside the grid.

Grid Calculation
Wqor Hamxegezouy

Implementing a Grid

Open GridScreen.kt and take a moment to look inside. You’ll find the usual function to handle the navigation and a list containing the icons that you’ll use as the grid’s content. At the bottom of the file, you’ll find the following composable functions that you need to implement:

@Composable
fun GridScreen() {
  //TODO add your code here

  BackButtonHandler {
    JetFundamentalsRouter.navigateTo(Screen.Navigation)
  }
}

@Composable
fun GridIcon(iconResource: ImageVector) {
  //TODO add your code here
}
@Composable
fun GridScreen() {
  LazyVerticalGrid( //1
    modifier = Modifier.fillMaxSize(), //2
    columns = GridCells.Fixed(3), //3
    content = {
      items(items.size) { index -> //4
        GridIcon(items[index]) //5
      }
    }
  )

  BackButtonHandler {
    JetFundamentalsRouter.navigateTo(Screen.Navigation)
  }
}

Implementing GridIcon

Each GridItem() will show the icon you passed in. Replace the GridIcon with the following code to achieve such behavior:

@Composable
fun GridIcon(iconResource: ImageVector) {
  Icon(
    imageVector = iconResource, //1
    tint = colorResource(R.color.colorPrimary), //2
    contentDescription = stringResource(R.string.grid_icon), //3
    modifier = Modifier //4
      .size(80.dp)
      .padding(20.dp)
  )
}
Grid With Three Columns
Vjop Qusk Lymou Xaqirkr

Key Points

  • Use Column with the verticalScroll modifier to make the content vertically if it doesn’t fit the screen.
  • Use Row with the horizontalScroll modifier to make the content scroll horizontally if it doesn’t fit the screen.
  • You can make your own composables scrollable by adding the verticalScroll or horizontalScroll modifiers.
  • Use scroll modifiers only for a fixed amount of content.
  • For dynamic and larger amounts of content, use the lazy counterparts of Row, Column and Grid instead.
  • The composable alternatives to RecyclerView are called LazyColumn and LazyRow for the vertical and horizontal scenarios, respectively.
  • You can group lists inside each other to make content scrollable in both directions.
  • To make grids, use a LazyVerticalGrid.
  • You can use LazyRow and LazyColumn components if you want to manually add items to the list, allowing you to build headers and footers. Learn more about them here: https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/package-summary#lazycolumn.

Where to Go From Here?

In this chapter, you learned how to make scrollable content, scrollable lists for dynamically created elements and custom grids.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now