
In this article, we’ll learn how to implement lazy staggered grid layouts in Android Jetpack Compose.
Prerequisites:
What are Lazy Staggered Grid Layouts in Jetpack Compose?
Lazy staggered APIs display items in a grid layout and allow each item to have a different height or width.
One example of a lazy staggered API is the Pinterest app. The app shows images with different heights in a vertically scrollable container.
For this article, create an empty Jetpack Compose project and open MainActivity.kt. Create a composable called MyUI() and call it from the onCreate().
// we need the following packages
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.staggeredgrid.LazyHorizontalStaggeredGrid
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
import androidx.compose.foundation.lazy.staggeredgrid.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import kotlin.random.Random
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() {
}
We’ll write our code in the MyUI().
There are two lazy staggered grid APIs in Jetpack Compose:
Note: As of Compose 1.4.3, these APIs are experimental.
1. Lazy Vertical Staggered Grid:
It displays a vertically scrollable grid. The items can have different heights. The API looks like this:
@ExperimentalFoundationApi
@Composable
fun LazyVerticalStaggeredGrid(
columns: StaggeredGridCells,
modifier: Modifier = Modifier,
state: LazyStaggeredGridState = rememberLazyStaggeredGridState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
verticalItemSpacing: Dp = 0.dp,
horizontalArrangement: Arrangement.Horizontal = Arrangement.spacedBy(0.dp),
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
userScrollEnabled: Boolean = true,
content: LazyStaggeredGridScope.() -> Unit
)
The columns parameter decides the number of columns. There are two options – Fixed and Adaptive. To learn about them, read Lazy Grid Layouts article.
Simple Example:
Let’s create the following output:

Each box has a random color and height. So, first, create a data class.
data class ListItem(
val height: Dp,
val color: Color
)
We can generate random values using Kotlin’s Random package:
val items = (1..100).map {
ListItem(
// random height
height = Random.nextInt(100, 300).dp,
// random color
color = Color(
red = Random.nextInt(255),
green = Random.nextInt(255),
blue = Random.nextInt(255),
alpha = 255
)
)
}
We can add these items using the itemsIndexed() method:
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun MyUI() {
val items = (1..100).map {
ListItem(
// random height
height = Random.nextInt(100, 300).dp,
// random color
color = Color(
red = Random.nextInt(255),
green = Random.nextInt(255),
blue = Random.nextInt(255),
alpha = 255
)
)
}
LazyVerticalStaggeredGrid(
columns = StaggeredGridCells.Fixed(count = 3), // 3 columns
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(all = 12.dp),
horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
verticalItemSpacing = 8.dp
) {
itemsIndexed(items) { index, item ->
Box(
modifier = Modifier
.fillMaxWidth()
.height(height = item.height)
.background(color = item.color, shape = RoundedCornerShape(size = 10.dp)),
contentAlignment = Alignment.Center
) {
Text(
text = "$index",
color = Color.White,
fontWeight = FontWeight.Bold
)
}
}
}
}
data class ListItem(
val height: Dp,
val color: Color
)
Output:

Adaptive Staggered Grid:
Instead of a fixed size, we can create a grid with an adaptive number of columns.
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun MyUI() {
val items = (1..100).map {
ListItem(
// random height
height = Random.nextInt(100, 300).dp,
// random color
color = Color(
red = Random.nextInt(255),
green = Random.nextInt(255),
blue = Random.nextInt(255),
alpha = 255
)
)
}
LazyVerticalStaggeredGrid(
columns = StaggeredGridCells.Adaptive(minSize = 80.dp), // adaptive
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(all = 12.dp),
horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
verticalItemSpacing = 8.dp
) {
itemsIndexed(items) { index, item ->
Box(
modifier = Modifier
.fillMaxWidth()
.height(height = item.height)
.background(color = item.color, shape = RoundedCornerShape(size = 10.dp)),
contentAlignment = Alignment.Center
) {
Text(
text = "$index",
color = Color.White,
fontWeight = FontWeight.Bold
)
}
}
}
}
data class ListItem(
val height: Dp,
val color: Color
)
Output:

2. Lazy Horizontal Staggered Grid:
It displays a horizontally scrollable grid. The items can have different widths. The API looks similar to the staggered vertical grid:
@ExperimentalFoundationApi
@Composable
fun LazyHorizontalStaggeredGrid(
rows: StaggeredGridCells,
modifier: Modifier = Modifier,
state: LazyStaggeredGridState = rememberLazyStaggeredGridState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(0.dp),
horizontalItemSpacing: Dp = 0.dp,
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
userScrollEnabled: Boolean = true,
content: LazyStaggeredGridScope.() -> Unit
)
Simple Example:
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun MyUI() {
val items = (1..100).map {
ListItem(
// random width
width = Random.nextInt(100, 300).dp,
// random color
color = Color(
red = Random.nextInt(255),
green = Random.nextInt(255),
blue = Random.nextInt(255),
alpha = 255
)
)
}
LazyHorizontalStaggeredGrid(
rows = StaggeredGridCells.Fixed(count = 3),
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(all = 12.dp),
verticalArrangement = Arrangement.spacedBy(space = 8.dp),
horizontalItemSpacing = 8.dp
) {
itemsIndexed(items) { index, item ->
Box(
modifier = Modifier
.fillMaxHeight()
.width(width = item.width)
.background(color = item.color, shape = RoundedCornerShape(size = 10.dp)),
contentAlignment = Alignment.Center
) {
Text(
text = "$index",
color = Color.White,
fontWeight = FontWeight.Bold
)
}
}
}
}
data class ListItem(
val width: Dp,
val color: Color
)
Output:

Adaptive Staggered Grid:
We can set the adaptive size using the same StaggeredGridCells.Adaptive() API.
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun MyUI() {
val items = (1..100).map {
ListItem(
// random width
width = Random.nextInt(100, 300).dp,
// random color
color = Color(
red = Random.nextInt(255),
green = Random.nextInt(255),
blue = Random.nextInt(255),
alpha = 255
)
)
}
LazyHorizontalStaggeredGrid(
rows = StaggeredGridCells.Adaptive(minSize = 100.dp), // adaptive
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(all = 12.dp),
verticalArrangement = Arrangement.spacedBy(space = 8.dp),
horizontalItemSpacing = 8.dp
) {
itemsIndexed(items) { index, item ->
Box(
modifier = Modifier
.fillMaxHeight()
.width(width = item.width)
.background(color = item.color, shape = RoundedCornerShape(size = 10.dp)),
contentAlignment = Alignment.Center
) {
Text(
text = "$index",
color = Color.White,
fontWeight = FontWeight.Bold
)
}
}
}
}
data class ListItem(
val width: Dp,
val color: Color
)
Output:

This is all about lazy staggered grid layouts in Jetpack Compose. I hope you have learned something new. If you have any doubts, leave a comment below.
Related Articles:
- How to Animate Visibility in Jetpack Compose?
- How to Check Internet Connection in Android Programmatically?
- 3 Ways to Disable Ripple Effect in Jetpack Compose
- Custom Fonts in Android Studio
References: