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:
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)?
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?
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.
fillMaxWidth() will set the min and max width of the composble to the maximum allowed by the container, to fill all the spacewrapContentWidth() 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 trueYou 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)
)
}
}

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")
}
}
}
}
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.

@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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With