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.
- 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.