
In this article, we’ll learn how to implement LazyColumn in Jetpack Compose. If you are familiar with XML, it is the RecyclerView (for displaying the items vertically).
Prerequisites:
What is LazyColumn in Android?
LazyColumn is used to display a vertically scrolling list. It only renders the currently visible items. As a result, it is memory efficient.
Example:

Let’s see how to implement the LazyColumn in Android Studio.
First, create an empty Compose project and add the following dependency to 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 and create a composable called MyUI(). We’ll write our code in it.
MainActivity for Material 3 Jetpack Compose:
// we need 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.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.itemsIndexed
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.Divider
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.Alignment
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.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() {
}
For the Material 2 version:
// we need 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.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.graphics.Color
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 LazyColumn API looks like this:
@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
)
modifier – The modifier to change the layout.
state – The state object to control the list’s state.
contentPadding – It adds padding for the whole layout.
reverseLayout – It changes the item’s order. When true, items are laid out in reverse order.
verticalArrangement – It is the vertical arrangement of the children (items). We can add spacing between items using this parameter.
horizontalAlignment – It is the horizontal arrangement of the children.
flingBehavior – Fling 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 LazyColumn Example:
Let’s display items using the item() and items() methods.
@Composable
fun MyUI() {
LazyColumn {
item {
Text(text = "First Item")
}
items(count = 14) { countValue ->
Text(text = "Items: $countValue")
}
item {
Text(text = "Last Item")
}
}
}
Output:

Spacing between Items in LazyColumn:
We can add spacing (padding) between the items using the verticalArrangement parameter.
@Composable
fun MyUI() {
LazyColumn(
verticalArrangement = Arrangement.spacedBy(space = 8.dp)
) {
item {
Text(text = "First Item")
}
items(count = 14) { countValue ->
Text(text = "Items: $countValue")
}
item {
Text(text = "Last Item")
}
}
}
Output:

Displaying Items from a List:
Let’s display a list of items. We use the items(items: List) method to display the list.
@Composable
fun MyUI() {
val fruits = listOf<String>("Apple", "Mango", "Banana", "Orange", "Watermelon", "Papaya")
LazyColumn(
verticalArrangement = Arrangement.spacedBy(space = 8.dp)
) {
item {
Text(
text = "Fruits List:",
fontSize = 22.sp
)
Spacer(modifier = Modifier.height(height = 8.dp)) // extra space below the heading
}
items(items = fruits) { item ->
Text(
text = item,
fontSize = 20.sp
)
}
}
}
Output:

Displaying Items with Index:
Sometimes, when you display a list, you may want to indicate the item’s index also. In such cases, use the itemsIndexed(items: List) method.
@Composable
fun MyUI() {
val fruits = listOf<String>("Apple", "Mango", "Banana", "Orange", "Watermelon", "Papaya")
LazyColumn(
verticalArrangement = Arrangement.spacedBy(space = 8.dp)
) {
item {
Text(
text = "Fruits List:",
fontSize = 22.sp
)
Spacer(modifier = Modifier.height(height = 8.dp)) // extra space below the heading
}
itemsIndexed(items = fruits) { index, item ->
Text(
text = "${index+1}. $item",
fontSize = 20.sp
)
}
}
}
Output:

LazyColumn with Divider:
We can add the divider using the Divider() composable.
@Composable
fun MyUI() {
val fruits = listOf<String>("Apple", "Mango", "Banana", "Orange", "Watermelon", "Papaya")
LazyColumn(
verticalArrangement = Arrangement.spacedBy(space = 8.dp)
) {
item {
Text(
text = "Fruits List:",
fontSize = 22.sp
)
Spacer(modifier = Modifier.height(height = 8.dp)) // extra space below the heading
Divider() // divider below the heading
}
itemsIndexed(items = fruits) { index, item ->
Text(
text = "${index+1}. $item",
fontSize = 20.sp
)
Spacer(modifier = Modifier.height(height = 4.dp)) // a margin of 4 dp
Divider()
}
}
}
Output:

LazyColumn Item Click:
To make the items clickable, add clickable() modifier on the items.
@Composable
fun MyUI() {
val fruits = listOf<String>("Apple", "Mango", "Banana", "Orange", "Watermelon", "Papaya")
val contextForToast = LocalContext.current.applicationContext
LazyColumn(
verticalArrangement = Arrangement.spacedBy(space = 8.dp)
) {
item {
Text(
text = "Fruits List:",
fontSize = 22.sp
)
Spacer(modifier = Modifier.height(height = 8.dp)) // extra space below the heading
}
itemsIndexed(items = fruits) { index, item ->
Column(
modifier = Modifier
.fillMaxWidth()
.clickable {
Toast.makeText(contextForToast, "Click: $item", Toast.LENGTH_SHORT).show()
}
) {
Text(
text = "${index + 1}. $item",
fontSize = 20.sp
)
Spacer(modifier = Modifier.height(height = 4.dp)) // a margin of 4 dp
}
}
}
}
Output:

LazyColumn Custom List:
Let’s see how to make the following list.

Here is the code:
@Composable
fun MyUI() {
val optionsList = prepareOptionsList()
LazyColumn(
verticalArrangement = Arrangement.spacedBy(space = 24.dp), // gap between items
contentPadding = PaddingValues(all = 22.dp) // padding for LazyColumn layout
) {
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)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable {
Toast
.makeText(context, optionsList.option, Toast.LENGTH_SHORT)
.show()
}
.padding(vertical = 10.dp, horizontal = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
modifier = Modifier.size(size = 36.dp),
imageVector = optionsList.icon,
contentDescription = null,
tint = Color(0xFF6650a4)
)
Spacer(modifier = Modifier.width(width = 12.dp))
Text(
text = optionsList.option,
fontSize = 16.sp
)
}
}
}
// prepare 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:

We have created OptionsList data class to store the item’s icon and name.
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 them in a Card layout. The context parameter is used to display the toast message.
In the MyUI(), we have called prepareOptionsList() method. The method returns the optionsList. Next, we added LazyColumn() 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.
ListItem:
Jetpack Compose provides ListItem() method. We can add items using it in a list. It comes with predefined slots for heading, leading, and trailing content.
@Composable
fun ListItem(
headlineContent: @Composable () -> Unit,
modifier: Modifier = Modifier,
overlineContent: @Composable (() -> Unit)? = null,
supportingContent: @Composable (() -> Unit)? = null,
leadingContent: @Composable (() -> Unit)? = null,
trailingContent: @Composable (() -> Unit)? = null,
colors: ListItemColors = ListItemDefaults.colors(),
tonalElevation: Dp = ListItemDefaults.Elevation,
shadowElevation: Dp = ListItemDefaults.Elevation,
)
headlineContent – The content of the headline.
modifier – The Modifier to be applied to the list item.
overlineContent – The content displayed above the headline.
supportingContent – The supporting content of the list item.
leadingContent – The leading content of the list item.
trailingContent – The trailing content such as a switch, or checkbox.
colors – Colors of the item at different states.
tonalElevation – The tonal elevation of the item.
shadowElevation – The shadow elevation of the item.
Example:
@Composable
fun MyUI() {
LazyColumn {
items(count = 50) {
ListItem(
headlineContent = { Text("Item $it") },
leadingContent = {
Icon(
imageVector = Icons.Default.Favorite,
contentDescription = null,
tint = Color(0xFF6650a4)
)
}
)
}
}
}
Output:

This is all about LazyColumn in Jetpack Compose. I hope you have learned something new. If you have any doubts, comment below.
Related Articles:
References: