42

I'm dipping my toes into Jetpack Compose, but I'm stumped by the behaviour of Row. I have a text next to an icon button, and I want the icon button to be anchored to the side with a minimum width of 48dp, and have text wrap around it. Like this:

Layout goal

But the text does not wrap, it eats up all the space in the Row:

Layout previews

@Composable
fun SampleLayout(text: String) {
                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.SpaceBetween,
                    ) {
                        Text(text)
                        IconButton(
                            onClick = {  },
                        ) {
                            Icon(
                                imageVector = androidx.compose.material.icons.Icons.Default.StarBorder,
                                null
                            )
                        }
                    }
}

@Preview(showBackground = true, backgroundColor = 0x006EAEA0, fontScale = 1.5F)
@Composable
fun SamplePreview1() {
    Box(Modifier.padding(16.dp)) {
        SampleLayout("helooooo")
    }
}

@Preview(showBackground = true, backgroundColor = 0x006EAEA0, fontScale = 1.5F)
@Composable
fun SamplePreview2() {
    Box(Modifier.padding(16.dp)) {
        SampleLayout("helooooooooooooooooooooooooooo")
    }
}
@Preview(showBackground = true, backgroundColor = 0x006EAEA0, fontScale = 1.5F)
@Composable
fun SamplePreview3() {
    Box(Modifier.padding(16.dp)) {
        SampleLayout("heloooooooooooooooooooooooooooooooooooooooo")
    }
}

I've tried setting the minimum width of the icon 48dp, but the text then still fills until the end of the row.

How can I make sure the the Text width does not go further than the icon button?

Maarten
  • 6,894
  • 7
  • 55
  • 90

2 Answers2

68

Row measures its children one after an other, and if some item needs to take all space available(e.g. if you use fillMaxWidth, or in this case text that has more than one line), next items won't have any space left for them.

If it's layout logic supports compression up to zero size, you won't see it at all (like in case with the Icon), otherwise it can be seen at the end of the row, actually taking zero size but being drawn outsize of the bounds.

To change order in which children are measured, you can use weight modifier: in this case the size of Icon will be calculated before Text:

The parent will divide the vertical space remaining after measuring unweighted child elements

Also weight has a fill parameter, which is set to true by default. This is equivalent to fillMaxWidth (when weight is used inside a Row), so you can skip the fillMaxWidth modifier in your parent. When you don't need this behavior, pass false to this parameter.

Row{
    Text(text, modifier = Modifier.weight(1f))
    IconButton(
        onClick = { }
    ) {
        Icon(
            imageVector = Icons.Default.StarBorder,
            null
        )
    }
}
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
  • Thanks for the answer. Can you please share the source for "By default Text has a higher layout priority than Icon in order to fill the necessary space"? This is very unintuitive. I would like to read a rationale for this decision. – Alex Misiulia Jul 28 '23 at 06:20
  • @AlexMisiulia thanks for pointing that out, it was actually a wrong assumption. see the updated answer. – Phil Dukhov Jul 28 '23 at 08:39
12

Try this

@Composable
fun SampleLayout(text: String) {
                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.SpaceBetween,
                    ) {
                        Text(text = text)
                        IconButton(
                            modifier = Modifier.weight(1f, fill = false), //You must add the false fill here to keep it from occupying all the available space
                            onClick = {  },
                        ) {
                            Icon(
                                imageVector = androidx.compose.material.icons.Icons.Default.StarBorder,
                                null
                            )
                        }
                    }
}
Richard Onslow Roper
  • 5,477
  • 2
  • 11
  • 42