DEV Community

myougaTheAxo
myougaTheAxo

Posted on

LazyVerticalGrid & StaggeredGrid: Compose Grid Layout Guide

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)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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)
            )
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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))
                }
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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))
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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)
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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])
    }
}
Enter fullscreen mode Exit fullscreen mode

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 */ }
    )
}
Enter fullscreen mode Exit fullscreen mode

Scroll Performance

  • Avoid heavy recompositions: Use remember for expensive objects
  • Paginate on scroll: Detect scrollState.canScrollForward to load more
  • Use LazyColumn.state.firstVisibleItemIndex to 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)