Hi - I am struggling on a `LazyVerticalGrid` getti...
# compose
m
Hi - I am struggling on a
LazyVerticalGrid
getting the items centered horizontally. I want the items to be split per row starting from the center
This is how it currently looks like… I’d like to have `R7`being positioned in the horizontal center (under
5
)
Copy code
@Composable
fun ParcelGrid(
    modifier: Modifier = Modifier,
    parcels: List<ParcelDto>,
    userPermissions: List<Permission>,
    showDeadline: Boolean,
    removalEnabled: Boolean,
    onRemoveParcel: (parcel: ParcelDto) -> Unit,
    onTapParcel: (parcel: ParcelDto) -> Unit
) {
    LazyVerticalGrid(
        modifier = modifier,
        columns = GridCells.Adaptive(minSize = 100.dp),
        horizontalArrangement = Arrangement.Center,
        verticalArrangement = Arrangement.Center
    ) {
        items(parcels, { it.id }) {
            ParcelGridItem(
                parcel = it,
                userPermissions = userPermissions,
                showDeadline = showDeadline,
                removalEnabled = removalEnabled,
                onRemoveParcel = { onRemoveParcel(it) }) {
                onTapParcel(it)
            }
        }
    }
}
a
probably fillMaxWidth for grid can help
m
Nope, sorry - that does not help… and besides that: even if it wasn’t max width: as it can be seen from the screenshot above, there is space available in the third row… the first item in the third row (being the 7th item at all) should be in the center of the width being made up by the rows above
Oh - and btw: I already tried using
GridCells.Fixed(3)
instead of
GridCells.Adaptive(minSize = 100.dp)
s
With GridCells.Fixed(3), you can probably do your own calculcation regarding how many items you’ve got, and if the remainder of dividing by 3 is 1, you can try and give the last item a span of 3, so it takes all 3 slots of that grid? Then it’d probably be super wide, but you may also be able to give it max width * 0.33f, or wrapContentWidth(Alignment.Center) or something like that
a
there is space available in the third row…
Point of fillMaxWidth is not to fill screen but set fixed constraints. It works with defult Column alignment so i thought it can also help with grid
s
the
item
dsl takes in a
span
lambda, where you can do exactly that. It even has convenience values in there like maxCurrentLineSpan and maxLineSpan as specified here. So then you can do like
span = { GridItemSpan(this.maxLineSpan) }
for that last item, and it will take up 3 spaces automatically. Just need to make sure you pass it only to the correct item and not all of them so not all of them take 3 spaces 😄
m
Sure I can kill this with some math and such… but than I have to take different device orientation and device sizes into account… and I thought THIS is what
horizontalArrangement
would do? At least there is this question then: what is the purpose of
horizontalArrangement
in `LazyVerticalGrid`then?
s
If your grid takes up width 100dp. And it has three items of 30dp each, you got 10dp left. I assume arrangement decides how the three items will be laid out in the available space and where these leftover 10dp will be. At the end? The beginning? Between the items? Between and around the items? Got many options
m
Sure - a grid item has a width of
100.dp
. Having my device in portrait mode, this makes up 3 items per row… but in landscape mode there would be like 5 or so…
Not even speaking of tablets, btw
s
Regarding not using fixed items but an adaptive approach, yeah then you can't exactly do the math yourself. In such cases I'm not sure what the best approach is.
m
I spent some more thoughts on this. Even with some math it is not fully working fine. It would work, if there is only ONE last item. But it fails e.g. in a row of three items having TWO last items.
span
wouldn’t work in this case then…
Aaaaaand there is a solution that seems to work:
Copy code
FlowRow(modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(4.dp, Alignment.CenterHorizontally), verticalAlignment = <http://Alignment.Top|Alignment.Top>) {
        repeat(parcels.size) {
            ParcelGridItem(
                modifier = Modifier.padding(PaddingValues(bottom = 4.dp)),
                parcel = parcels[it],
                userPermissions = userPermissions,
                showDeadline = showDeadline,
                removalEnabled = removalEnabled,
                onRemoveParcel = { onRemoveParcel(parcels[it]) }) {
                onTapParcel(parcels[it])
            }
        }
    }
In short: using
FlowRow
instead of
LazyVerticalGrid
s
Yeah I assumed you need the laziness here. If not there is the excellent new flow row api, or you could even go with a custom layout like I’ve done here. But if you want the laziness you’d have to do something else like have the last
item
be a full width spanning one, and have it contain more than 1 item inside there, have it render the entire last row for example, with 2 items in one
item
lambda block or something like that. In any way, it’s possible but quite wacky 😄
This, assuming you don’t need any item animation when content changes should just work. Chunking them and placing the remainder as one
item {}
. It ain’t pretty, and it doesn’t handle non-fixed GridCells configuration, but maybe someone stumbles upon this and does require the laziness, so they can use this.
Copy code
val items = remember {
  List(23) {
    Color((0xFF000000..0xFFFFFFFF).random())
  }
}
val gridItems = remember(items) {
  items.chunked(3)
}
LazyVerticalGrid(
  columns = GridCells.Fixed(3),
  horizontalArrangement = Arrangement.SpaceAround,
  modifier = Modifier.size(300.dp),
) {
  for (gridItemChunk in gridItems.dropLast(1)) {
    items(gridItemChunk) { item ->
      YourItem(item)
    }
  }
  item(span = { GridItemSpan(maxLineSpan) }) {
    val lastItems = gridItems.last()
    Row {
      if (lastItems.size == 1) {
        Spacer(Modifier.weight(1f))
        YourItem(lastItems.first(), Modifier.weight(1f))
        Spacer(Modifier.weight(1f))
      } else {
        Spacer(Modifier.weight(1f))
        YourItem(lastItems[0], Modifier.weight(2f))
        Spacer(Modifier.weight(1f))
        YourItem(lastItems[1], Modifier.weight(2f))
        Spacer(Modifier.weight(1f))
      }
    }
  }
}
Also putting this here in case I need it in the future and don’t wanna think about it again 😅
s
This arrangement is not really supported atm in Lazy grids, you could try arranging items in LazyColumn instead You can also file a feature request with your use case if you think it would be useful for others :)