9

I have the following composable.

@Composable
fun Temp() {
    Row(
        modifier = Modifier
            .background(Color.Red)
            .height(IntrinsicSize.Min)
            .fillMaxWidth()
    ) {
        Text(text = "Hello", fontSize = 10.sp)
        Icon(
            imageVector = Icons.Default.Star,
            contentDescription = "Star",
            modifier = Modifier.fillMaxHeight()
        )
    }
}

The height of icon is not decreasing from 24.dp. Is there any way I can achieve this behavior. I want the icon size to just be the height of the parent row. If the text is large. The icons size is increased. I think it has to be with icon minimum size being 24.dp. How can I make icon smaller?

Diken Mhrz
  • 327
  • 2
  • 14
  • I don't know how, but I look at the code. Icon use `DefaultIconSizeModifier = Modifier.size(24.dp)` if imageVector passed. otherwise uses painter size. So try add vector asset with the required size and then use like `Icon(painter = painterResource(R.drawable.ic_baseline_star_10)` – vitidev May 29 '22 at 19:04

2 Answers2

11

Your code actually works as expected - that's how intrinsic calculations work.

Compose checks the min height of each view and chooses the maximum of those values. In your case, the min height of the image is related to the intrinsic size of the image, which you cannot control in the case of Icons.Default.

A possible solution is to use Modifier.layout. When Compose calculates the intrinsic height, the height constraint will be infinite, in which case you can layout it as a zero size view, so that your text will be the highest. When the intrinsic height is determined, you can measure and position the icon:

Row(
    modifier = Modifier
        .background(Color.Red)
        .height(IntrinsicSize.Min)
        .fillMaxWidth()
) {
    Text(text = "Hello", fontSize = 10.sp)
    Icon(
        imageVector = Icons.Default.Star,
        contentDescription = null,
        modifier = Modifier
            .layout { measurable, constraints ->
                if (constraints.maxHeight == Constraints.Infinity) {
                    layout(0, 0) {}
                } else {
                    val placeable = measurable.measure(constraints)
                    layout(placeable.width, placeable.height) {
                        placeable.place(0, 0)
                    }
                }
            }
    )
}

Using Modifier.layout you can change size of view and its position. Usually you use it like this:

  1. First parameter, measurable is an object on which you can call measure with constraints - the second layout parameter. measure is gonna calculate the size your view will take, taking constraints in count.
  2. in layout you need to pass the desired view size - usually it can be taken from placeable from the previous step.
  3. inside layout you need to call place on the placeable with the desired offset.

With height(IntrinsicSize.Min) layout content is getting called multiple times:

  1. during the first call(s) max height constraint is equal to Infinity, so intrinsic calculations can select the correct size ignoring the parent size.
  2. In the last call max height constraint is equal to the calculated parent intrinsic height.

In my code during first calls, when height constraint is equal to Infinity, I say that this view has zero size, so it's not counted in intrinsic measurements. When intrinsic height is defined, I can layout it with the final constraints.

Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
  • 2
    This worked.. thanks.. could you please explain me what are we doing actually? – Diken Mhrz May 30 '22 at 06:48
  • 3
    @DikenMhrz I added more detailed explanation – Phil Dukhov May 30 '22 at 08:05
  • 1
    I have been looking at many a Stack Overflow post, but finally this question and answer are exactly what I needed. Thank you Diken for posting the question and thank you @PhilDukhov for the great answer and thorough explanation! – Clark Sandholtz Aug 30 '22 at 22:52
0

I took @Phil's answer and turned it into an extension which in turn can be reused.

fun Modifier.matchRowSize() : Modifier {
    return layout { measurable, constraints ->
        if (constraints.maxHeight == Constraints.Infinity) {
            layout(0, 0) {}
        } else {
            val placeable = measurable.measure(constraints)
            layout(placeable.width, placeable.height) {
                placeable.place(0, 0)
            }
        }
    }
}

which can be used in your icon like so:

Row(
    modifier = Modifier
        .height(IntrinsicSize.Min)
        .padding(8.dp)
) {
    Icon(
        imageVector = Icons.Default.Star,
        contentDescription = null,
        modifier = Modifier
            .matchRowSize() // <-- here
    )
    Spacer(modifier = Modifier.size(10.dp))
    Text(text = "Hello", fontSize = 17.sp)
}

Robin
  • 704
  • 8
  • 24