How to Implement LazyRow in Jetpack Compose?

Jetpack Compose LazyRow

In this article, we’ll learn how to implement LazyRow in Jetpack Compose. If you are familiar with XML, it is the RecyclerView for displaying the items horizontally.

Prerequisites:

What is LazyRow in Jetpack Compose?

LazyRow is used to display a horizontally scrolling list. It only renders the currently visible items on the screen. As a result, it is memory efficient.

Example:

Jetpack Compose LazyRow with Custom List Example

Let’s play with the lazy row in the Android Studio.

First, create an empty Jetpack Compose project and add the following dependency in the app-level gradle file. We need it for the icons.

// this dependency contains material icons
// if you want to use it, enable Proguard because of its large size
implementation "androidx.compose.material:material-icons-extended"

Next, open MainActivity. Create a composable called MyUI() and call it from the onCreate() method. We’ll write our code in it.

MainActivity for the Material 3 Jetpack Compose:

// add the following packages
import android.content.Context
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Article
import androidx.compose.material.icons.outlined.Favorite
import androidx.compose.material.icons.outlined.Help
import androidx.compose.material.icons.outlined.Lightbulb
import androidx.compose.material.icons.outlined.Notifications
import androidx.compose.material.icons.outlined.Paid
import androidx.compose.material.icons.outlined.Pending
import androidx.compose.material.icons.outlined.QuestionAnswer
import androidx.compose.material.icons.outlined.VerifiedUser
import androidx.compose.material.icons.outlined.WatchLater
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            YourProjectNameTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Column(
                        modifier = Modifier.fillMaxSize()
                    ) {
                        MyUI()
                    }
                }
            }
        }
    }
}

@Composable
fun MyUI() {

}

MainActivity for the Material 2 version:

import android.content.Context
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Article
import androidx.compose.material.icons.outlined.Favorite
import androidx.compose.material.icons.outlined.Help
import androidx.compose.material.icons.outlined.Lightbulb
import androidx.compose.material.icons.outlined.Notifications
import androidx.compose.material.icons.outlined.Paid
import androidx.compose.material.icons.outlined.Pending
import androidx.compose.material.icons.outlined.QuestionAnswer
import androidx.compose.material.icons.outlined.VerifiedUser
import androidx.compose.material.icons.outlined.WatchLater
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            YourProjectNameTheme(darkTheme = false) {
                Surface(
                    modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background
                ) {
                    Column(
                        modifier = Modifier.fillMaxSize()
                    ) {
                        MyUI()
                    }
                }
            }
        }
    }
}

@Composable
fun MyUI() {

}

Note: I am using the Material 3 Jetpack Compose. If you are working with Material 2, you may see a slightly different appearance, but the code will still work as expected.

The LazyRow API looks like this:

@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
)

modifier – The modifier to change the layout.

state – The state object to control the list’s state.

contentPadding – It adds padding to the whole layout.

reverseLayout – It changes the item’s order. When true, items are laid out in reverse order.

horizontalArrangement – It is the horizontal arrangement of the children.

verticalAlignment – It is the vertical arrangement of the children (items). We can add spacing between items using this parameter.

flingBehaviorFling behavior.

userScrollEnabled – Whether scrolling via the user gestures or accessibility actions is allowed. You can still scroll programmatically using the state even when it is disabled.

content – The content of the list.

We use the following methods to add the content to the list:

  • item()
  • itemsIndexed(items: List)
  • items(count: Int) or items(items: List)

Simple LazyRow Example:

Let’s display numbers using the items() method.

@Composable
fun MyUI() {
    LazyRow {
        items(count = 20) { index ->
            Text(
                modifier = Modifier.padding(all = 6.dp),
                text = "$index",
                fontSize = 22.sp,
                fontWeight = FontWeight.Bold
            )
        }
    }
}

Output:

Simple LazyRow Example

LazyRow Item Click:

To make the items clickable, add the clickable() modifier to them.

@Composable
fun MyUI() {
    val contextForToast = LocalContext.current.applicationContext

    LazyRow {
        items(count = 20) { index ->
            Column(modifier = Modifier.clickable {
                Toast.makeText(contextForToast, "Click: $index", Toast.LENGTH_SHORT).show()
            }) {
                Text(
                    modifier = Modifier.padding(all = 6.dp),
                    text = "$index",
                    fontSize = 22.sp,
                    fontWeight = FontWeight.Bold
                )
            }
        }
    }
}

Output:

LazyRow Item Click

Related: 3 Ways to Remove the Ripple in Jetpack Compose

LazyRow with Custom List:

Let’s make the following list:

Jetpack Compose LazyRow with Custom List

Here is the complete code:

@Composable
fun MyUI() {
    val optionsList = prepareOptionsList()

    LazyRow(
        horizontalArrangement = Arrangement.spacedBy(space = 12.dp),
        contentPadding = PaddingValues(horizontal = 8.dp, vertical = 12.dp)
    ) {
        items(optionsList) { item ->
            ItemLayout(optionsList = item)
        }
    }
}

// single item layout
@Composable
private fun ItemLayout(
    optionsList: OptionsList,
    context: Context = LocalContext.current.applicationContext
) {
    Card(
        shape = RoundedCornerShape(size = 12.dp),
        // if you are using Material 2, change the following to elevation = 4.dp
        elevation = CardDefaults.cardElevation(
            defaultElevation = 4.dp
        )
    ) {
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .clickable {
                    Toast.makeText(context, optionsList.option, Toast.LENGTH_SHORT).show()
                }
                .padding(all = 8.dp),
        ) {
            Icon(
                modifier = Modifier.size(size = 36.dp),
                imageVector = optionsList.icon,
                contentDescription = null,
                tint = Color(0xFF7850E8)
            )
        }
    }
}

// add items to the list
private fun prepareOptionsList(): MutableList<OptionsList> {
    val optionsList = mutableListOf<OptionsList>()

    optionsList.add(OptionsList(icon = Icons.Outlined.Favorite, option = "Saved Items"))
    optionsList.add(OptionsList(icon = Icons.Outlined.Paid, option = "Payment History"))
    optionsList.add(OptionsList(icon = Icons.Outlined.Lightbulb, option = "New Ideas"))
    optionsList.add(OptionsList(icon = Icons.Outlined.WatchLater, option = "Items History"))
    optionsList.add(OptionsList(icon = Icons.Outlined.Article, option = "Shared Articles"))
    optionsList.add(OptionsList(icon = Icons.Outlined.Notifications, option = "Previous Notifications"))
    optionsList.add(OptionsList(icon = Icons.Outlined.VerifiedUser, option = "Verification Badge"))
    optionsList.add(OptionsList(icon = Icons.Outlined.Pending, option = "Pending Tasks"))
    optionsList.add(OptionsList(icon = Icons.Outlined.QuestionAnswer, option = "FAQs"))
    optionsList.add(OptionsList(icon = Icons.Outlined.Help, option = "Support"))

    return optionsList
}

data class OptionsList(val icon: ImageVector, val option: String)

Output:

Jetpack Compose LazyRow with Custom List

We have created OptionsList data class to store the item’s icon and name. The icon is displayed on the screen and the name shows up when we tap on the icon.

The ItemLayout() composable renders the single item. It takes context and optionsList parameters. The optionsList parameter contains the item’s icon and name. We are displaying the icon in a Card layout. The context parameter is used to display the toast message.

In the MyUI(), we have called prepareOptionsList() method. It returns the list of data. Next, we added LazyRow() composable and passed our optionsList to the items() method. In each iteration, the ItemLayout() gets called and the corresponding item is rendered on the screen.

Related: Expandable Card Animation in Jetpack Compose

LazyRow and LazyColumn use the same methods with identical use cases. Refer to the LazyColumn article to learn more about customizing the list.

This is all about LazyRow in Jetpack Compose. I hope you have learned something new. If you have any doubts, comment below.

For more information, look at the Android docs.

Related Articles:

Leave a Comment