kotlinandroid-studioandroid-jetpack-compose

How to Use LazyVerticalGrid Inside a LazyColumn with Full-Width Header in Jetpack Compose?


I'm trying to create a layout in Jetpack Compose where I have a LazyColumn containing a full-width header and a LazyVerticalGrid below it. The grid should wrap its content height and take the full width of the screen. However, I'm encountering issues with the layout and scrolling behavior.

The error: java.lang.IllegalStateException: Vertically scrollable component was measured with an infinity maximum height constraints, which is disallowed.

@Composable
fun ScrollableGridWithHeader() {
    val items = remember { List(20) { "Item #$it" } }

    LazyColumn {
        // Full-width header
        item {
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(100.dp)
                    .background(Color.Red),
                contentAlignment = Alignment.Center
            ) {
                Text(text = "Upload Images", color = Color.White)
            }
        }

        // LazyVerticalGrid
        item {
            LazyVerticalGrid(
                cells = GridCells.Fixed(2), // or use GridCells.Adaptive(140.dp) if preferred
                modifier = Modifier
                    .fillMaxWidth()
                    .wrapContentHeight(),
                verticalArrangement = Arrangement.spacedBy(8.dp),
                horizontalArrangement = Arrangement.spacedBy(8.dp)
            ) {
                items(items.size) { index ->
                    Box(
                        modifier = Modifier
                            .size(140.dp)
                            .background(Color.Gray),
                        contentAlignment = Alignment.Center
                    ) {
                        Text(text = items[index])
                    }
                }
            }
        }
    }
}

Solution

  • You can't have a vertically scrolling Composable inside another vertically scrolling Composable without setting a fixed height to the nested Composable. In your case however, I would suggest an alternative solution.

    If you want to include a full-width header that displays at the top in your LazyVerticalGrid that also scrolls with the content, you can use span for an item like this:

    @Composable
    fun ScrollableGridWithHeader() {
    
        val items = remember { List(20) { "Item #$it" } }
    
        LazyVerticalGrid(
            columns = GridCells.Fixed(2),
            modifier = Modifier
                .fillMaxWidth()
                .wrapContentHeight(),
            verticalArrangement = Arrangement.spacedBy(8.dp),
            horizontalArrangement = Arrangement.spacedBy(8.dp)
        ) {
            item(span = { GridItemSpan(maxLineSpan) }) {  // let item span across all columns in Grid
                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(100.dp)
                        .background(Color.Red),
                    contentAlignment = Alignment.Center
                ) {
                    Text(text = "Upload Images", color = Color.White)
                }
            }
            items(items.size) { index ->
                Box(
                    modifier = Modifier
                        .size(140.dp)
                        .background(Color.Gray),
                    contentAlignment = Alignment.Center
                ) {
                    Text(text = items[index])
                }
            }
        }
    }
    

    Please also note that with recent Compose versions, the LazyVerticalGrid takes a columns parameter instead of cells.

    Output:

    Screenshot