I have a `LazyVerticalGrid` in a `Column` in a `Co...
# compose
s
I have a
LazyVerticalGrid
in a
Column
in a
ComposeView
that’s a child of a
NestedScrollView
and I am getting this:
Copy code
java.lang.IllegalStateException: Nesting scrollable in the same direction layouts like ScrollableContainer and LazyColumn is not allowed.
...
I have seen a couple of this in here as well, but can’t find any that matches the state of my layout. Please how can I fix this?
👀 1
a
As the message says it is not supported. If you can, set a fixed height on the
ComposeView
or the
LazyVerticalGrid
as a workaround. If you cannot, there's no way to fix it.
s
Thanks @Albert Chang. This is a huge limitation if you ask me.
c
It's just a different way of thinking (I think). A googler explained it to me once on here and it basically seems like I was trying to mirror my way of thinking of android view land over to compose and basically the answer was that in recycler view land if this worked, it actually should not have worked and it lead to subtle bugs or recycling not even properly happening. Anything that is scrollable is inherently infinite height and you can't have two infinite heights. @Andrey Kulikov you can correct me if I'm wrong. Either way the COmpose team should probably have some compose documentation directly around this sort of question as it is asked... A LOT. A lot of my team members still don't really understand how some of the recycler view implementations we currently have in production will be able to be replaced with compose.
a
Even in the Views world putting RecyclerView inside ScrollView is always a mistake in the performance sense. RecyclerView is only efficient when it has some fixed size so it can only create/reuse items for the visible viewport(like instead of totalCount == 100 create 2 items if only 2 of them a visible at the same time). Now you wrap RecyclerView inside the ScrollView and what happens: ScrollView wants the have the full size of its children, so it measures RecyclerView with infinite allowed height, which means RecyclerView has to create all 100 items at once completely disabling all the laziness and recycling. It is essentially the same as if you put one huge LinearLayout inside ScrollView. Now getting back to Compose: with the simplicity of the Compose APIs we noticed that a lot of people unnecessarily try to use the same pattern of nested scrollable containers which again results in a bad performance. And yes, I agree there are could be some valid use cases for it which we have to help solving, but for now Compose defensively not allowing you to go inefficient road. Instead we suggest to keep one scrollable container and use the power of LazyColumn dsl to specify headers, etc
👍 4
c
@Andrey Kulikov yeah. I think that's where a lot of us went wrong. Many of my teams apps had RVs inside of RVs. Hence why I think why compose needs clear documentation on this.
t
I had the same problem and just developed an extension function for LazyColumn to support grids. It is not perfect and just optimized for my needs but maybe it is helpful for you. Because than you can put every thing into one LazyColumn:
Copy code
inline fun <T> LazyListScope.gridItems(
    columns: Int,
    gridPadding: Dp = 0.dp,
    contentPadding: PaddingValues = PaddingValues(),
    items: List<T>,
    crossinline itemContent: @Composable LazyItemScope.(item: T) -> Unit
) {
    val chunkedItems = items.chunked(columns)
    itemsIndexed(chunkedItems) { index, rowList ->
        val layoutDirection = LocalLayoutDirection.current
        val topPadding = if (index > 0) gridPadding else contentPadding.calculateTopPadding()
        val startPadding = contentPadding.calculateLeftPadding(layoutDirection)
        val endPadding = contentPadding.calculateEndPadding(layoutDirection)
        val bottomPadding = contentPadding.calculateBottomPadding()
        Row(Modifier.padding(top = topPadding, start = startPadding, bottom = bottomPadding, end = endPadding)) {
            val rowModifier = Modifier.weight(1f)
            rowList.forEachIndexed { index, item ->
                if (index > 0) Spacer(Modifier.width(gridPadding))
                Box(rowModifier) {
                    itemContent(item)
                }
            }
            val emptyRows = (columns - rowList.size)
            repeat(emptyRows) { // fill empty cells
                Spacer(Modifier.width(gridPadding))
                Spacer(modifier = rowModifier)
            }
        }
    }
}
🙏 1
s
Thanks @Timo Drick. This might not work for my use case as I have the
LazyVerticalGrid
(LazyColumn in your case) in a view-system
NestedScrollView.
I get the explanation @Colton Idle and @Andrey Kulikov made though, and it seems the best solution is to have a fixed height somewhere.
I have the fixed height on the
LazyVerticalGrid
btw, and my expectation is that it would become scrollable, but that doesn’t seem to be the case.
t
You could also adapt my code so it makes a verticalGrid. Should not be to hard.
What do you mean with vertical grid?
My solution will create rows with <N> columns. So you can define how many columns you need inside of you LazyColumn.
Nest LazyRows inside of LazyColumn is working out of the box.
289 Views