LazyVerticalGrid & StaggeredGrid: Compose Grid Layout Guide
Jetpack Compose's grid layouts—LazyVerticalGrid and LazyVerticalStaggeredGrid—enable efficient, flexible multi-column displays for images, products, and content feeds. Master the patterns for production-grade apps.
LazyVerticalGrid: Fixed & Adaptive Columns
Fixed Column Count
LazyVerticalGrid(
columns = GridCells.Fixed(3),
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(100) { index ->
Box(
modifier = Modifier
.fillMaxWidth()
.height(150.dp)
.background(Color.Blue),
contentAlignment = Alignment.Center
) {
Text("Item $index", color = Color.White)
}
}
}
Adaptive Columns (Responsive)
LazyVerticalGrid(
columns = GridCells.Adaptive(minSize = 150.dp),
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(8.dp)
) {
items(50) { index ->
Card(modifier = Modifier.fillMaxWidth()) {
Image(
painter = painterResource(id = R.drawable.placeholder),
contentDescription = "Item $index",
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxWidth()
.height(150.dp)
)
}
}
}
GridItemSpan for Headers & Spanning Items
Create complex grid layouts with items that span multiple columns:
LazyVerticalGrid(
columns = GridCells.Fixed(4),
modifier = Modifier.fillMaxSize()
) {
item(span = { GridItemSpan(maxLineSpan) }) {
Text(
"Header Section",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(16.dp)
)
}
items(100) { index ->
Card(modifier = Modifier.fillMaxWidth()) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(120.dp)
.background(Color.LightGray),
contentAlignment = Alignment.Center
) {
Text("$index")
}
}
}
item(span = { GridItemSpan(maxLineSpan) }) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(100.dp)
.background(Color.DarkGray),
contentAlignment = Alignment.Center
) {
Text("Load More", color = Color.White)
}
}
}
Categorized Grids with Headers
Organize content by categories:
@Composable
fun CategorizedGrid() {
LazyVerticalGrid(
columns = GridCells.Fixed(2),
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(8.dp)
) {
val categories = mapOf(
"Electronics" to (1..6).toList(),
"Fashion" to (7..12).toList(),
"Books" to (13..18).toList()
)
categories.forEach { (category, items) ->
item(span = { GridItemSpan(maxLineSpan) }) {
Text(
category,
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(8.dp)
)
}
items(items) { id ->
Card(modifier = Modifier.fillMaxWidth()) {
Text("Item $id", modifier = Modifier.padding(16.dp))
}
}
}
}
}
LazyVerticalStaggeredGrid: Pinterest-Style Layouts
For non-uniform heights (like Pinterest feeds):
LazyVerticalStaggeredGrid(
columns = StaggeredGridCells.Fixed(2),
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(100) { index ->
Box(
modifier = Modifier
.fillMaxWidth()
.height((100 + (index % 5) * 30).dp)
.background(Color(kotlin.random.Random.nextInt(0xFF000000.toInt(), 0xFFFFFFFF.toInt())))
) {
Text("$index", color = Color.White, modifier = Modifier.padding(8.dp))
}
}
}
Adaptive Staggered Grid (Responsive Columns)
LazyVerticalStaggeredGrid(
columns = StaggeredGridCells.Adaptive(minSize = 200.dp),
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(8.dp)
) {
items(50) { index ->
Card(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(8.dp)) {
Image(
painter = painterResource(id = R.drawable.placeholder),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxWidth()
.height((100 + (index % 3) * 50).dp)
)
Text("Product $index", fontWeight = FontWeight.Bold)
}
}
}
}
Performance Optimization
Use key & contentType
LazyVerticalGrid(
columns = GridCells.Fixed(3),
modifier = Modifier.fillMaxSize()
) {
items(
count = items.size,
key = { index -> items[index].id }, // Unique identifier
contentType = { index -> items[index].type } // Content category
) { index ->
GridItem(items[index])
}
}
Lazy Image Loading Pattern
items(imageUrls.size) { index ->
AsyncImage(
model = imageUrls[index],
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxWidth()
.height(150.dp),
onSuccess = { /* Cache metrics */ }
)
}
Scroll Performance
-
Avoid heavy recompositions: Use
rememberfor expensive objects -
Paginate on scroll: Detect
scrollState.canScrollForwardto load more -
Use
LazyColumn.state.firstVisibleItemIndexto implement "jump to top"
Grid layouts are fundamental to modern mobile apps. Master these patterns for scalable, beautiful UIs.
8 Android App Templates → https://myougatheax.gumroad.com
Top comments (0)