Can someone tell me what is wrong here: > java....
# compose
l
Can someone tell me what is wrong here:
java.lang.IllegalStateException: Vertically scrollable component was measured with an infinity maximum height constraints, which is disallowed.
...
Copy code
val scrollState = rememberScrollState()

Column(
	modifier = Modifier
		.fillMaxHeight()
		.verticalScroll(scrollState)
) {
    Card(modifier = Modifier.fillMaxWidth())
    Card(modifier = Modifier.fillMaxWidth()) {
        LazyVerticalGrid() {}
    }
}
The parent is the content slot of
BottomSheetScaffold
. I know it has to do with the
LazyVerticalGrid
, but both Cards measure the height by its content so I don't understand what the problem is. EDIT:
Copy code
Card(modifier = Modifier.fillMaxWidth().weight(1f, false))
    Card(modifier = Modifier.fillMaxWidth().weight(1f, false)) {
        LazyVerticalGrid() {}
    }
Applying
weight
on both Cards solves the exception but now the
LazyVerticalGrid
is scrollable. I would like to wrap content height of the latter
Card
, is this possible?
c
In theory no because the full content height of a lazy list is not known until all items are loaded in the viewport. Weight makes sense here since if this is in bottom sheet, you probably want the lazy list container height to grow with the height of the parent.
l
@Chris Sinco [G] Do I have any options? Can I force the height of the latter
Card
to use the scroll ability of the parent
Column
? Now the Card's height ends at the bottom of the screen (see screenshot) but the intend of the scrollable Column is to avoid this and give the latter Card the height it needs for its content.
Copy code
val scrollState = rememberScrollState()

Column(
	modifier = Modifier
		.fillMaxSize()
		.padding(16.dp)
		.border(2.dp, Color.Blue)
		.verticalScroll(scrollState)
) {
	Card(modifier = Modifier.height(300.dp).fillMaxWidth()) {
		Column(modifier = Modifier.fillMaxWidth().background(Color.Red)) {}
	}
	DashboardCard(onNavigation = { navigation = it })
}
This would be the goal. I have given the latter Card a fix height of 800.dp for demonstration. In the screenshot I have scrolled down within the Column.
c
A lazy list that is sized to its full content size loses the advantage of being lazy. So in your case, if you want the entire BottomSheet to vertically scroll with its contents (including the Card with the lazy list), you may have to use Column instead of LazyColumn
l
@Chris Sinco [G] I'm not using
LazyColumn
did you mean
LazyVerticalGrid
?
What exactly does lazy mean when you say it loses the advantage of being lazy?
c
Lazy lists have performance benefits because you are not rendering every item in in the collection you pass it, only the ones visible in the viewport/screen/fixed size. Because of that, measuring say the total height of 100 items isn’t possible unless you render all 100 of them. Rendering that many will be needed if you want that parent Column vertical scroll to work since it now knows that the viewport is smaller than the full content size. But… now that you’ve rendered 100 items, scrolling may not be snappy.
This logic applies to every lazy construct (Row/Column/VerticalGrid) because they all assume you are only rendering items visible in the viewport, which we assume is a much smaller subset than the entire collection of items you want to display
l
Ah ok, I got it. I will ditch the LazyVerticalGrid for a custom Layout then. Thanks for the insights Chris ❤️
c
Looking again at the code above, what you can try is have a single LazyVerticalGrid, then emit the first two items as
item { Card(…) }
, then emit
items
with the list of the longer list of things. I’m not entirely sure how well that will work in a bottom sheet though.
With the single item emits having a grid span to cover a whole row
Kinda like this
Copy code
@Composable
fun PokemonList(
    loading: Boolean = false,
    pokemon: List<Pokemon>,
    onPokemonSelected: (Pokemon) -> Unit = {}
) {
    LazyVerticalGrid(
        cells = GridCells.Fixed(2),
        verticalArrangement = Arrangement.spacedBy(4.dp),
        horizontalArrangement = Arrangement.spacedBy(4.dp),
        contentPadding = PaddingValues(32.dp),
        content = {
            item({ GridItemSpan(2) }) {
                Text(
                    text = "Pokedex",
                    style = MaterialTheme.typography.h4,
                    modifier = Modifier.padding(
                        top = 64.dp, bottom = 24.dp
                    )
                )
            }
            if (loading) {
                item({ GridItemSpan(2) }) {
                    Column(
                        horizontalAlignment = Alignment.CenterHorizontally
                    ) {
                        CircularProgressIndicator(
                            color = Color.Black,
                            modifier = Modifier
                                .size(48.dp)
                                .padding(vertical = 24.dp)
                        )
                    }
                }
            } else {
                items(pokemon) { pokemon ->
                    PokeDexCard(
                        pokemon = pokemon,
                        onPokemonSelected = onPokemonSelected
                    )
                }
            }
        }
    )
}
l
I will have a look on it later/tomorrow, it's late here. Thanks for your time so far
@Chris Sinco [G] I had a look into this approach and I discarded it because nested items are not possible right? e.g.
Copy code
item {
  Card() { // Main Card
      items {
         CardGridItem() {} // GridItems
     }
  }
}
I can't compose the GridItems into the DashboardCard. I have a question about the
weight
modifier. When I have a parent
Column(modifier.fillMaxSize().verticalScroll())
with 2 siblings where one of them has a weight of 1f, the non-weighted sibling will be content wrapped and the weighted sibling will occupy the remaining height, i.e. fill the screen height, correct? And is it also true that the weight will prevent the
verticalScroll
form working in any case?