How to create GridView using Jetpack Compose
Asked Answered
B

10

42

How to create Gridview in Jetpack compose without using recycler view or android.widget.gridview ?

Buggery answered 28/10, 2019 at 18:32 Comment(1)
Since Compose version 1.0.0-alpha09 there is a standard component: LazyVerticalGridRoseliaroselin
H
59

With 1.x.y the LazyVerticalGrid composable provides experimental support for displaying items in a grid.

val numbers = (0..20).toList()

LazyVerticalGrid(
    columns = GridCells.Fixed(4)
) {
    items(numbers.size) {
        Column(horizontalAlignment = Alignment.CenterHorizontally) {
            Text(text = "Number")
            Text(text = "  $it",)
        }
    }
}

The columns = GridCells.Fixed(4) would mean that there are 4 columns 1/4 of the parent wide.

enter image description here

val numbers = (0..20).toList()

LazyVerticalGrid(
    columns = GridCells.Adaptive(minSize = 64.dp)
) {
    items(numbers) {
        Column(horizontalAlignment = Alignment.CenterHorizontally) {
            Text(text = "Number")
            Text(text = "  $it",)
        }
    }
}

columns = GridCells.Adaptive(minSize = 64.dp) would mean that there will be as many columns as possible and every column will be at least 64.dp and all the columns will have equal width.

enter image description here

Hoskinson answered 28/10, 2019 at 19:10 Comment(4)
it might be removed in the future as Google suggest to use LazyColumn and Row/LazyRow instead of LazyVerticalGrid. developer.android.com/reference/kotlin/androidx/compose/…Pvc
how to use it with paging?Blurt
@adam-noor grid layouts are still supported here: developer.android.com/reference/kotlin/androidx/compose/… They deprecated the one in the old package though.Monad
pass "columns" instead of "cells" to LazyVerticalGridLanguishment
R
24

UPD: Compose version 1.0.0-alpha09 introduces standard component:

LazyVerticalGrid

Yet another solution based on LazyColumnFor (Jetpack Compose version 1.0.0-alpha04)

@Composable
fun <T> LazyGridFor(
    items: List<T>,
    rowSize: Int = 1,
    itemContent: @Composable BoxScope.(T) -> Unit,
) {
    val rows = items.chunked(rowSize)
    LazyColumnFor(rows) { row ->
        Row(Modifier.fillParentMaxWidth()) {
            for ((index, item) in row.withIndex()) {
                Box(Modifier.fillMaxWidth(1f / (rowSize - index))) {
                    itemContent(item)
                }
            }
        }
    }
}
    
@Preview("LazyGridFor: example")
@Composable()
fun LazyGridForPreview() {
    val data = (1..100).map(Integer::toString)
    LazyGridFor(data, 3) { item ->
        Text(item)
    }
}
Roseliaroselin answered 5/10, 2020 at 22:57 Comment(2)
I don't see how can we use LazyVerticalGrid with paging data...Blurt
LazyColumnFor doesn't exist no moreBlurt
V
10

I have created a adaptative grid layout:

Preveiw

Jetpack compose adaptative grid layout landscape|tablette

Jetpack compose adaptative grid layout

Code

LazyColumn(modifier = modifier) {
            ...
            val numberOfItemsByRow = LocalConfiguration.current.screenWidthDp / 200 // you can replace 200 by the minimum size you want your cells to have.

            items(items = trendingGameList.chunked(numberOfItemsByRow)) { rowItems ->
                Row(
                    horizontalArrangement = Arrangement.spacedBy(14.dp),
                    modifier = Modifier.padding(horizontal = 16.dp),
                ) {
                    for (game in rowItems) {
                        GameCard(game = game, onClick = { }, modifier = Modifier.weight(1F))
                    }
                }
                Spacer(Modifier.height(14.dp))
            }
            ...
        }

The full code is here.

I decided to implement my own adaptative grid layout because the existing LazyVerticalGrid is experimental and can be removed in the future, and to use it you have to annotate the compostables that uses it with @ExperimentalFoundationApi recursively:

@ExperimentalFoundationApi 
@Composable
fun A {
    LazyVerticalGrid {
        ...
    }
}

@ExperimentalFoundationApi 
@Composable
fun B {
 A {..}
}

@ExperimentalFoundationApi 
@Composable
fun C {
 B {..}
}
...

OR use the @OptIn(ExperimentalFoundationApi::class) which require the -Xopt-in=kotlin.RequiresOptIn compiler argument.

Vasos answered 6/7, 2021 at 17:40 Comment(1)
how to use it with paging data?Blurt
S
8

As @Pavel Marchenko mentioned, LazyVerticalGrid is added from version 1.0.0-alpha09

Here is a quick example:

    LazyVerticalGrid(
        cells = GridCells.Adaptive(96.dp),
        contentPadding = PaddingValues(16.dp),
    ) {
        items(bookList) { book ->
            Image(book.cover, modifier = Modifier.padding(8.dp))
        }
    }
Skeptic answered 27/12, 2020 at 16:14 Comment(1)
how to use it with paging data?Blurt
H
6

Update to @Pavel Marchenko answer as some of the compose function's names changed: LazyColumn() instead of LazyColumnFor() and use of items() functions is needed:

@Composable
fun <T> LazyGridFor(
  items: List<T>,
  rowSize: Int = 1,
  itemContent: @Composable BoxScope.(T) -> Unit,
) {
  LazyColumn {
    items(items = items.chunked(rowSize)) { row ->
      Row(Modifier.fillParentMaxWidth()) {
        for ((index, item) in row.withIndex()) {
          Box(Modifier.fillMaxWidth(1f / (rowSize - index))) {
            itemContent(item)
          }
        }
      }
    }
  }
}
Hypochondriac answered 25/8, 2021 at 7:18 Comment(2)
This will cause paging3 not to loadUsk
how to use it with paging data?Blurt
M
1

I have created a custom gridview for using android jetpack compose until they will not support official recycleview for Gridlayout in Compose.

@Composable
fun <T> GridView(
cols: Int = 0,
list: List<T>,
child: @Composable() (dataModal: T) -> Unit
) {

val rows = (list.size / cols) + (if (list.size % cols > 0) 1 else 0)
VerticalScroller(modifier = 
Modifier.fillMaxHeight().fillMaxHeight().drawBackground(color = colorResource(
    id = R.color.color_bg_application
))) {
    Table(columns = cols) {
        for (r in 0 until rows) {
            tableRow {
                for (c in 0 until cols) {
                    //Cell
                    val i = (r * cols) + c
                    if (i < list.size) {
                        child(list[i])
                    } else {
                        break
                    }
                }
            }
        }

    }
}
}

USE

 GridView(cols = 4, list = model.list,child = { Item( it) })

Item declaration

@Composable
fun Item(t: T) {
....
}
Metaphase answered 22/4, 2020 at 6:48 Comment(0)
A
1

Since LazyVerticalGrid can't be used in a LazyColumn, can't use it in a scrollable page. I created a library for custom LazyGrid implementation in compose. It's lazy so it performs really well. There are different item placement types for different scenarios and it's really easy to use:

LazyColumn {
    item {
        Text(text = "Title")
    }
    LazyGrid(
        rows = listOf(),
        elementPerRow = 4,
        itemPlacementType = ItemPlacementType.FixedSize(itemWidth = 80.dp),
        contentPadding = PaddingValues(horizontal = 16.dp)
    ) { item, modifier ->
        CustomGridItem(item, modifier, onClick = { /* if needed */ })
    }
}

There is also a collapsible version. Further information, documentation, source code and a demo can be found in: https://github.com/yusufarisoy/lazy-grid

dependencies {
    implementation 'com.github.yusufarisoy:lazy-grid:1.0.0'
}

It's also restartable and skippable for compose compiler to work with best performance. About Compose metrics: https://chris.banes.dev/composable-metrics/

Apocalyptic answered 29/4, 2022 at 13:19 Comment(1)
Your LazyGrid can only be used inside LazyListScope. That's just loop to create item inside column, same answer as @Krzysiulele, not very helpful.Greenebaum
R
1

LazyVerticalGrid Consumes the scroll state thereby making it useless inside a LazyColum

Here an implementation using only rows and columns

sealed class GridCells {

abstract val columns: Int

data class Fixed(val count: Int) : GridCells() {
    override val columns: Int = count
}

object Adaptive : GridCells() {
    override val columns: Int = 1
}}


@Composable
fun VerticalGrid(
    modifier: Modifier = Modifier,
    gridCells: GridCells,
    totalItems: Int,
    contentPadding: PaddingValues = PaddingValues(0.dp),
    content: @Composable (Int) -> Unit,
) {
Column(modifier = modifier) {
    val columnCount = when (gridCells) {
        is GridCells.Fixed -> gridCells.columns
        is GridCells.Adaptive -> ceil(sqrt(totalItems.toDouble())).toInt()
    }

    val rowCount = ceil(totalItems.toDouble()/columnCount).toInt()

    for(i in 0 until rowCount){
        Row(Modifier.fillMaxWidth()) {
            for (j in 0 until columnCount) {
                val index = j + i * columnCount
                Box(
                    Modifier
                        .weight(1f)
                        .padding(contentPadding)
                ) {
                    if (index < totalItems) {
                        content(index)
                    }
                }
            }
        }
    }
}

}

Rains answered 30/3, 2023 at 13:32 Comment(1)
awesome it will solved my nested scrolling issue in lazyColumInflammatory
A
0

Made a couple of changes to @Madhav 's answer (using compose v1.0.0-alpha01):

@Composable
fun <T> GridView(
        cols: Int = 1,
        list: List<T>,
        rowModifier: Modifier = Modifier,
        colModifier: Modifier = Modifier,
        child: @Composable (dataModal: T) -> Unit
) {

    val rows = (list.size / cols) + (if (list.size % cols > 0) 1 else 0)
    
    ScrollableColumn(modifier = colModifier) {
        
        for (r in 0 until rows) {
            Row(modifier = rowModifier, horizontalArrangement = Arrangement.SpaceAround) {
                for (cell in 0 until cols) {
                    val i = (r * cols) + cell
                    if (i < list.size) { child(list[i]) } else { break }
                }
            }
        }
    }

}

Usage:

GridView(cols = 2, list = listOf("1", "2", "3", "4",)) {
     Text(text = it)
}
Arcboutant answered 31/8, 2020 at 15:30 Comment(0)
C
0

I can't add a LazyVerticalGrid inside a LazyColumn so found this way. Let's check it.

@OptIn(ExperimentalLayoutApi::class)
@Composable
fun CustomGridView(
    cols: Int,
    count: Int,
    builder: @Composable (Int) -> Unit
) {
    FlowRow(
        maxItemsInEachRow = cols,
    ) {
        repeat(count) {
            Box(Modifier.fillMaxWidth((1.0 / cols).toFloat())) {
                builder(it)
            }
        }
    }
}

Example:

 CustomGridView (
        cols = 2,
        count = data.count(),
        ) {
        index ->
        DetailsPieChartItem(
            data = Pair(data.keys.elementAt(index),  data.values.elementAt(index)),
            color = colors[index]
        )
    }

Preview:

enter image description here

Cyrillus answered 17/12, 2023 at 10:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.