I want to put a bunch of buttons 48x48dp buttons o...
# compose-android
c
I want to put a bunch of buttons 48x48dp buttons on the screen laid out from right to left. but i always want the amount of buttons i show to be perfect. So on a phone it might show 8 across, while on a tablet it might show 15 across. I'm arguing with myself over the best way to do this. Is there a way to do this without measuring the width first/custom layout? For example, if I had 200 dips of width, then I'd show 4 buttons. if I had 400, I'd show 8. If I had 350dp of width, id show 7.
f
so you want to know the size of the container to place the children. You can do any of these • use
onPlaced
and get the width of the parent • use a
Layout
• use a
BoxWithConstraints
if you just want to wrap to the 2nd (or 3rd or ...) row, there is also
FlowRow
(or something along those lines) There are probably other solutions besides these
a
+1 to
FlowRow
!
f
I understood the question as "I want to know how many fit and I'll render those", that's why I put
FlowRow
separately (as that draws additional rows), but if that's the intention, then it's definitively the simplest solution
c
I was thinking about flowRow, but can I limit the number of rows in flowRow to 1?
flowRow with a max row count of 1 could work i think
f
well, the idea behind a
FlowRow
is to wrap whatever doesn't fit, so it wouldn't make sense to allow limiting to 1 row. Looks like you'll have to build your own solution here.
if you don't mind a 1 frame delay,
onPlaced
is easy enough to use
Copy code
private val ItemDimension = 128.dp

val colors = listOf(
    Color.Red,
    Color.Green,
    Color.Blue,
    Color.Cyan,
    Color.Magenta,
)

@Preview(widthDp = 420)
@Composable
fun FitsN() {
    WeatherSampleTheme {
        Surface(
            color = MaterialTheme.colorScheme.background
        ) {
            val density = LocalDensity.current
            val itemWidthPx = with(density) { ItemDimension.toPx() }
            var items by remember {
                mutableIntStateOf(0)
            }
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .onPlaced { layoutCoordinates ->
                        items = (layoutCoordinates.size.width / itemWidthPx).toInt()
                    },
                horizontalArrangement = Arrangement.SpaceEvenly,
            ) {
                repeat(items) {
                    Item(
                        index = it,
                        modifier = Modifier.size(ItemDimension)
                    )
                }
            }
        }
    }
}

@Composable
fun Item(
    index: Int,
    modifier: Modifier = Modifier,
) {
    Box(
        modifier = modifier.background(
            color = colors[index % colors.size]
        ),
        contentAlignment = Alignment.Center,
    ) {
        Text(text = "Item $index")
    }
}
this is a more generic solution with
Layout
. It uses central alignment, it could easily be extended to allow configuring how the additional space is distributed, similar to how row and column behave https://gist.github.com/fvilarino/da067aca6f079e720e07d9f2a76b2315
🌟 1
c
thank you! I am going to try out my own onPlaced setup, and compare it to yours. just as a fun excercise when im back at my desk. Next will be your generic layout solution. cheers buddy
👍 1
a
Don't use
onPlaced
because it will result in both bad performance and bad UX. See this thread.
c
Oh hm! I actually just came here to ask why/how @Francesc knew that using onPlaced resulted with a 1 frame delay. seems like i should use
Layout
instead (but the onPlaced code is so easy to read 😭 )
f
onPlaced
does not imply bad performance, if your layout is not changing, then it's fine to use. If you are aware of the delay you can always not draw anything until the size is known with a simple
if
check. For simple cases,
onPlaced
is a good enough solution
c
gotcha. thanks for clarifying. I'm trying out your other gist and that also works great.
f
That's the better performance solution, albeit more complex, but also reusable
c
QQ with the impl in the gist. Do you know if there's an easy way to know how many items would fit in the list? I guess not unless you try to actually lay them out? Example. I'd like to replace this list of items
with this
f
You need to measure them first, using the intrinsic size, no other way thatI I know
c
hm. not sure i follow. but let me hack on it.
sorry for all the noob questions. i really haven't touched all the custom drawing/layout stuff. 🙈
f
You can't know how many fit until you know the size of the container and the children, so unless those are fixed, you have to measure during composition
a
You don't need to use intrinsic size. Check how
FlowRow
is implemented.
c
they will be fixed size in my case.
I feel like I know how I'd do it with BoxWithConstraints. 😂
f
But not the container, right?
If everything is fixed size then it's straightforward
c
no, not the container
although this is pretty crappy. its kinda close to what i want. might just go with it.
Copy code
BoxWithConstraints{
    val internalItemSize = 48.dp
    val howManyCanFit: Float = this.maxWidth.value/internalItemSize.value
    val rows = 2
    val columns: Int = howManyCanFit.roundToInt()
    FlowRow(
        modifier = Modifier.padding(4.dp),
        horizontalArrangement = Arrangement.spacedBy(4.dp),
        maxItemsInEachRow = columns
    ) {
        val itemModifier = Modifier
            .padding(vertical = 4.dp)
            .size(internalItemSize)
            .background(Color.Blue)
        repeat(rows * columns) {
            Spacer(modifier = itemModifier)
        }
    }
bah. its kinda crappy.
a
I built my own sort of FlowRow because I also needed it to be on one line, but I also wanted it to start from the right hand side of the view. One thing I noticed is that you can borrow a lot of the implementation and tools FlowRow uses under the hood, like the function for calculating position in a row (if you want to go that road, I found those things very helpful).
c
thanks. i started a new question because i think this one has kinda gone from how to implement my initial ask to forking a flow row.
👍 1
Created a FR for maxRows on FlowRows https://issuetracker.google.com/issues/293577082
👏 1
101 Views