Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jetpack Compose: How to put a LazyVerticalGrid inside a scrollable Column?

When trying to put a LazyVerticalGrid inside a scrollable Column I get the following error:

java.lang.IllegalStateException: Nesting scrollable in the same direction layouts like LazyColumn and Column(Modifier.verticalScroll()) is not allowed. If you want to add a header before the list of items please take a look on LazyColumn component which has a DSL api which allows to first add a header via item() function and then the list of items via items().

I am not making a traditional list, I just have alot of elements that are too big to fit on the screen. Therefore I want the column to scroll so I can see all the elements. Here is my code:

@ExperimentalFoundationApi
@Composable
fun ProfileComposable(id: String?) {
    val viewModel: ProfileViewModel = viewModel()
    if (id != null) {
        viewModel.getProfile(id)
        val profile = viewModel.profile.value
        val scrollState = rememberScrollState()
        if (profile != null) {
            Column(modifier = Modifier
                .fillMaxWidth()
                .fillMaxHeight()
                .verticalScroll(scrollState)) {
                Row() {
                    ProfilePic(profile.getImgUrl(), profile.name)
                    Column(Modifier.padding(16.dp)) {
                        ProfileName(profile.name)
                        Stats(profile.stats) //      <--------------- the offending composable
                    }
                }
                Sprites(sprites = profile.sprites)
                TextStat(profile.id.toString(), "Pokemon Number")
                TextStat(profile.species.name, "Species")
                TextStat(profile.types.joinToString { it.type.name }, "Types")
                TextStat(profile.weight.toString(), "Weight")
                TextStat(profile.forms.joinToString { it.name }, "Forms")
            }
        } else {
            Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
                CircularProgressIndicator()
            }
        }
    } else {
        Text("Error")
    }
} 

The Stats() composable contains the LazyVerticalGrid which causes the error:

@ExperimentalFoundationApi
@Composable
fun Stats(stats: List<Stat>) {
    LazyVerticalGrid(cells = GridCells.Fixed(2)) {
        itemsIndexed(stats) { index, item ->
            StatBox(stat = item)
        }
    }
}

I do not want the grid to scroll, I just want to display a grid within a scrollable column.

like image 243
Kes Walker Avatar asked Jun 10 '21 10:06

Kes Walker


People also ask

How do you make a column scrollable in compose?

We can make the Column scrollable by using the verticalScroll() modifier.

What is lazy column in jetpack compose?

A LazyColumn is a vertically scrolling list that only composes and lays out the currently visible items. It's similar to a Recyclerview in the classic Android View system.

What is mutableStateOf in jetpack compose?

mutableStateOf creates an observable MutableState<T> , which is an observable type integrated with the compose runtime. interface MutableState<T> : State<T> { override var value: T } Any changes to value will schedule recomposition of any composable functions that read value .

Is jetpack compose reactive?

Jetpack Compose is a modern toolkit designed to simplify UI development. It combines a reactive programming model with the conciseness and ease of use of the Kotlin programming language.


3 Answers

I just ran into this problem myself. As @gaohomway said, your best bet is to use the experimental FlowRow() from Google's Accompanist library.

Here is a working code snippet as an example:

@Composable
fun ProfileScreen2() {
    LazyColumn(
        modifier = Modifier
            .fillMaxSize()
    ) {
        item {
            Box(modifier = Modifier.fillMaxWidth().height(200.dp).background(color = Red))
        }

        item {
            Box(modifier = Modifier.fillMaxWidth().height(200.dp).background(color = Gray))
        }

        item {
            FlowRow() {
                SampleContent()
            }
        }
    }
}

@Composable
internal fun SampleContent() {
    repeat(60) {
        Box(
            modifier = Modifier
                .size(64.dp)
                .background(Blue)
                .border(width = 1.dp, color = DarkGray),
            contentAlignment = Alignment.Center,
        ) {
            Text(it.toString())
        }
    }
}

Displays this scrollable page (don't mind the nav bar at the bottom):

like image 169
Jaidyn Belbin Avatar answered Oct 21 '22 20:10

Jaidyn Belbin


I had a similar use-case, the goal was to design a profile screen that has a bunch of information and statistics on top, and then comes to the posts as a Grid in the bottom of the screen.

I ended up using the LazyVerticalGrid for the whole list and setting full span for the items that need to fill the entire screen:

LazyVerticalGrid(cells = GridCells.Fixed(3)) {
    item(span = { GridItemSpan(3) }) { TopInfo() }
    item(span = { GridItemSpan(3) }) { SomeOtherInfo() }
    item(span = { GridItemSpan(3) }) { BottomInfo() }
    items(gridItems) { GridItemView(it) }
}
like image 28
MohammadReza Avatar answered Oct 21 '22 18:10

MohammadReza


Reason

Nesting scrollable in the same direction layouts like LazyColumn and Column(Modifier.verticalScroll()) is not allowed.

Can't find LazyVerticalGrid forbidden scrolling temporarily

Alternatives

Alternative library from Android official Jetpack Compose Flow Layouts

FlowRow {
    // row contents
}

FlowColumn {
    // column contents
}

like image 1
gaohomway Avatar answered Oct 21 '22 20:10

gaohomway