2

I am confused by what the modifiers fillMaxWidth() and wrapContentWidth() in Jetpack Compose do. I have read through the documentation and looked at their implementations, but I am still not exactly sure how they work (internally).

Here is my understanding of what they do:

  • fillMaxWidth() sets the width of the element to the maximum width of its parent container. My understanding is that it does so by setting the minimum width and and the maximum width to be equal to the maximum width of the parent.

  • wrapContentWidth() causes a layout element to be as wide as the element's content.

My questions are:

  1. In case of fillMaxWidth(), is my understanding of what it does internally correct (setting the minimum width and and the maximum width to be equal to the maximum width of the parent)?

  2. In case of wrapContentWidth(), does is causes a layout element to be as wide as the element's content by setting the min and max width of the element to be the width of the content?

  3. Also, what would the following modifier exactly do if I applied it to an element? modifier = Modifier.fillMaxWidth().wrapContentWidth() Wouldn't the wrapContentWidth() just undo what fillMaxWidth did? And what if I switched the order and used modifier = Modifier.wrapContentWidth().fillMaxWidth()?

Thanks for all help.

IT'S-ON
  • 83
  • 6
  • It was used in the official Google course about Android development with Jetpack Compose https://developer.android.com/codelabs/basic-android-kotlin-compose-add-images – IT'S-ON Jan 11 '23 at 18:16

2 Answers2

2
  • fillMaxWidth() will set the min and max width of the composble to the maximum allowed by the container, to fill all the space
  • wrapContentWidth() will set the min width to 0 (so ignores a previous min width), and you can do the same with the max width if you set unbounded to true

You can combine them to have some effects, for instance you could use a fillMaxWidth to set a certain background color, then have a child composable centered in the same container using wrapContentWidth and wrapContentHeight, as seen here (I used wrapContentSize but the same applies to both wrapContentWidth and wrapContentHeight)

@Preview(widthDp = 360, heightDp = 360)
@Composable
private fun PreviewMOdifier() {
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .background(Color.Red)
            .wrapContentSize(Alignment.Center)
            .background(Color.Blue)
    ) {
        Text(
            text = "This is the body",
            modifier = Modifier
                .padding(all = 16.dp)
                .background(Color.Yellow)
        )
    }
}

enter image description here

Edit: here's another example, try removing wrapContentHeight in the Box modifier so see how the preview changes

@Preview(heightDp = 360)
@Composable
private fun PreviewThreeBlock() {
    PlaygroundTheme {
        Surface(
            color = MaterialTheme.colorScheme.background
        ) {
            Box(
                modifier = Modifier.wrapContentHeight().background(Color.Red)
            ) {
                Text(text = "FooBar")
            }
        }
    }
}
Francesc
  • 25,014
  • 10
  • 66
  • 84
  • Thanks for your reply! I see what you did here, and I understand how using wrapContentSize makes sense in this example. But you mentioned that you can use it to position a child within its container. However, wouldn't it make more sense (not in this example, but in general) to just set Alignment and Arrangement individually without using wrapContentSize? Besides being able to position an element, what would be a common use case of wrapContentSize (without any positioning arguments)? – IT'S-ON Jan 11 '23 at 21:51
  • wrapContentWidth or wrapContentHeight will ignore the min width or height. If you have for instance a Box within a Surface, the Surface is enforcing the child to be full size, but you can override that if you set wrapContentWidth or wrapContentHeight in your Box. – Francesc Jan 11 '23 at 22:23
2

In Jetpack Compose setting size of child or/and container Composables are done using Constraints coming from size Modifiers, parent or scroll as they return Constraints.Infinity.

Measuring a Composable with parent or modified Constraints determines which size that child Composable will have

You can check out Constraints section of this answer.

Modifier.fillmaxWidth(fraction) sets min and max width of Constraints to be equal so when you call measurable.measure(constraints) your child Composable is measured with this fixed value.

Modifier.wrapWidth() is actually most of the time is not required when your parent Composable is Row, Column or other built-in Composables because when you don't assign a size Modifier to your Composable it's measured with min-max constraints range of parent which is a range between 0 and parent width.

However, Box has a parameter called propagateMinConstraints or Surface which is a Box with propagateMinConstraints = true forces minimum Constraints to direct descendant as explained in this answer or as @Francesc explained.

enter image description here

@Composable
private fun WrapWidthExample() {
    Row() {
        Surface(
            modifier = Modifier
                .size(200.dp)
                .border(2.dp, Color.Yellow),
            onClick = {}) {
            Column(
                modifier = Modifier
                    .size(50.dp)
                    .background(Color.Red, RoundedCornerShape(6.dp))
            ) {
                Box(
                    modifier = Modifier
                        .size(50.dp)
                        .background(Color.Green, RoundedCornerShape(6.dp))
                )

            }
        }
        Surface(
            modifier = Modifier
                .size(200.dp)
                .border(2.dp, Color.Yellow),
            onClick = {}) {
            Column(
                modifier = Modifier
                    .wrapContentWidth()
                    .background(Color.Red, RoundedCornerShape(6.dp))
            ) {
                Box(
                    modifier = Modifier
                        .size(50.dp)
                        .background(Color.Green, RoundedCornerShape(6.dp))
                )

            }
        }
    }
}

Without Modifier.wrapContentWidth() 200.dp(525px in my device) is forced to red Column for measuring Measurable, but with wrapContentWidth allows that Composable to use its own min Constraints which is 0 because of not assigning any Modifier.

  • Allow the content to measure at its desired width without regard for the incoming measurement * [minimum width constraint][Constraints.minWidth], and, if [unbounded] is true, also without * regard for the incoming measurement [maximum width constraint][Constraints.maxWidth]. If * the content's measured size is smaller than the minimum width constraint, [align] * it within that minimum width space. If the content's measured size is larger than the maximum * width constraint (only possible when [unbounded] is true), [align] over the maximum * width space.

And the Constraints what is returned from this Modifier is as

    val wrappedConstraints = Constraints(
        minWidth = if (direction != Direction.Vertical) 0 else constraints.minWidth,
        minHeight = if (direction != Direction.Horizontal) 0 else constraints.minHeight,
        maxWidth = if (direction != Direction.Vertical && unbounded) {
            Constraints.Infinity
        } else {
            constraints.maxWidth
        },
        maxHeight = if (direction != Direction.Horizontal && unbounded) {
            Constraints.Infinity
        } else {
            constraints.maxHeight
        }
    )

Basically, wrapContentX can be used when parent forces child Composables to use its Constraints, this can be done when you build your custom Layouts for instance or using Surface as above.

  1. In Jetpack Compose unless you chain Modifier.requiredX first size modifier is applied while the one after it unless there is no padding, size, etc between is ignored. You can check out requiredSize answer in the first link for more details.
Thracian
  • 43,021
  • 16
  • 133
  • 222
  • Sorry for the late reply. Thanks a lot for your detailed answer! Seeing and understanding the constraints made thing clearer. – IT'S-ON Jan 21 '23 at 16:05