Match Width of Parent in Column (Jetpack Compose)
Asked Answered
R

7

81

By default, Column {} has the width of it's largest child element. How can I make all other elements to fit the width of the parent Column? If I use Modifier.fillMaxWidth() the elements will take up the entire available space and make the parent Column larger. How can I achieve the same behavior like a Modifier.matchParentWidth() Modifier would provide?

Redness answered 28/1, 2021 at 17:58 Comment(0)
R
66

The solution is to leverage the power of intrinsic measurements.

Instead of using Modifier.fillMaxWidth() we use width(IntrinsicSize.Min) to match the width to the minimum width of the largest element

Redness answered 27/2, 2021 at 13:28 Comment(1)
Note: Trying this got me an error, so know that IntrinsicSize can't be used in LazyColumns and the like: Asking for intrinsic measurements of SubcomposeLayout layouts is not supported. This includes components that are built on top of SubcomposeLayout, such as lazy lists, BoxWithConstraints, TabRow, etc.Hypso
Z
109

You can use the Modifier .width(IntrinsicSize.Max)

 Column(Modifier.width(IntrinsicSize.Max)) {
        Box(Modifier.fillMaxWidth().background(Color.Gray)) {
            Text("Short text")
        }
        Box(Modifier.fillMaxWidth().background(Color.Yellow)) {
            Text("Extremely long text giving the width of its siblings")
        }
        Box(Modifier.fillMaxWidth().background(Color.Green)) {
            Text("Medium length text")
        }
    }

enter image description here

Zanthoxylum answered 20/3, 2021 at 11:48 Comment(2)
any idea when to use .Min vs .Max? the accepted answer uses .Min while yours is .Max.Vientiane
@AngelKoh It doesn't make a difference if all children can only be rendered in a specific width. Here however, when using IntrinsicSize.Min, the minimum width is chosen so the children can still be rendered "properly". Since maxLines of Text is not restricted by default it can happen that the text is wrapped into multiple lines when using IntrinsicSize.Min - so IntrinsicSize.Min can lead to a Column width smaller than wrapContentWidth() - That's what I observed. So IntrinsicSize.Max is preferrable here. I could also imagine a difference if a child uses Modifier.widthIn().Dareen
R
66

The solution is to leverage the power of intrinsic measurements.

Instead of using Modifier.fillMaxWidth() we use width(IntrinsicSize.Min) to match the width to the minimum width of the largest element

Redness answered 27/2, 2021 at 13:28 Comment(1)
Note: Trying this got me an error, so know that IntrinsicSize can't be used in LazyColumns and the like: Asking for intrinsic measurements of SubcomposeLayout layouts is not supported. This includes components that are built on top of SubcomposeLayout, such as lazy lists, BoxWithConstraints, TabRow, etc.Hypso
E
6

Here I'm using Modifier.fillMaxWidth and the items doesn't make parent column larger :

@Composable
fun Demo() {
Column(modifier = Modifier.width(300
    .dp)) {
    Text(text = "with fillMaxWidth modifier",modifier = Modifier.fillMaxWidth().background(Color.Red))
    Text(text = "without fillMaxWidth modifier",modifier = Modifier.background(Color.Gray))
 }

}

What I usually do to achieve the matchParentWidth is something like this (It's dirty but gets the job done):

val context = AmbientContext.current.resources
val displayMetrics = context.displayMetrics
val scrWidth = displayMetrics.widthPixels / displayMetrics.density

Column(modifier = Modifier.width(300
    .dp)) {
    Text(text = "with fillMaxWidth modifier",modifier = Modifier.fillMaxWidth().background(Color.Red))
    Text(text = "without fillMaxWidth modifier",modifier = Modifier
        .preferredWidth(scrWidth.dp)
        .background(Color.Gray))
}
Estimative answered 30/1, 2021 at 5:48 Comment(3)
Hello, I'm looking for dynamic sizing here. I can't specify a fixed size for the parent column. The column should be as wide as its widest childRedness
This is the best solution if you want to have pixel perfect proportional geometry. Thanks. I was missing the division by density! Big thanks.Hawfinch
Also what I achieved with it: gist.github.com/cicerohellmann/20f276f04a694e7bb588d84162169a73Hawfinch
H
5

you can simply use fillMaxWidth()

 Row(modifier = Modifier
        .fillMaxWidth()
        .background(Color.Cyan),) {
        Spacer(modifier = Modifier.padding(start = 4.dp))
        Image(
            painter = painterResource(R.drawable.tom_jerry),
            contentDescription ="this is image",

            modifier = Modifier
                .size(40.dp)
                .clip(shape = CircleShape).align(CenterVertically),
            alignment = Alignment.Center,
            contentScale = ContentScale.FillHeight


        )
        Column(modifier = Modifier.padding(start = 10.dp)) {
            Text(text = "Name : ${msg.name}")
            Text(text = "age : ${msg.age.toString()}")

        }

enter image description here

Hyperon answered 3/10, 2021 at 9:2 Comment(0)
B
4
Surface(
    modifier = Modifier
        .fillMaxSize()
        .wrapContentWidth(Alignment.CenterHorizontally)
        .wrapContentHeight(Alignment.CenterVertically)
) {
    Column(
        modifier = Modifier.width(300.dp)
    ) {
        // ...
        Button(
            { /*TODO*/ },
            modifier = Modifier
                .fillMaxWidth()
                .height(60.dp)
        ) {
            Text(stringResource(R.string.log_in))
        }
    }
}

enter image description here

Benzoyl answered 14/1, 2023 at 14:51 Comment(0)
B
0

Since this question is not only about matching Text Composables which return intrinsic sizes via Modifier's

@Stable
fun Modifier.width(intrinsicSize: IntrinsicSize) = when (intrinsicSize) {
    IntrinsicSize.Min -> this.then(MinIntrinsicWidthModifier)
    IntrinsicSize.Max -> this.then(MaxIntrinsicWidthModifier)
}

Then inside MaxIntrinsicWidthModifier with

override fun IntrinsicMeasureScope.maxIntrinsicWidth(
    measurable: IntrinsicMeasurable,
    height: Int
) = measurable.minIntrinsicWidth(height)

However, Intrinsic measurements don’t really measure the children twice. Instead, they do a different kind of calculation — you can think of it as a pre-measure step without requiring exponential measurement time, as it is cheaper and easier. So while this doesn’t exactly break the single measurement rule, it does bend it a little bit and shows a Compose requirement that falls outside of the usual ones.

When creating a custom layout, Intrinsics provide a default implementation based on approximations. However, in some cases, the default calculation might not work for you as intended, so the API provides a way of overriding these defaults.

To specify the Intrinsic measurements of your custom layout, you can override the minIntrinsicWidth, minIntrinsicHeight, maxIntrinsicWidth, and maxIntrinsicHeight of the MeasurePolicy interface during the measurement pass:

If you encounter such cases

https://medium.com/@lepicekmichal/compose-intrinsic-redraw-bug-2cdecef0f96c

Row IntrinsicSize.Min not working when the children are async loading images

Android Jetpack Compose Row's height (IntrinsicSize.Min) is not stretched when children column generate more composables

Or If you use a parent with IntrinsicSize while any of content Composables are BoxWithConstratins, lazy lists, TabRow or etc. you get exception.

Asking for intrinsic measurements of SubcomposeLayout layouts is not supported. This includes components that are built on top of SubcomposeLayout, such as lazy lists, BoxWithConstraints, TabRow, etc.

In that case if you know which child will be bigger you can measure it inside a Layout first then measure rest using its width.

If any of these child composable can be widest you need to use SubcomposeLayout as in this question.

Jetpack Compose set sibling Composables' width to longest one dynamically with SubcomposeLayout

Bathsheb answered 18/8, 2023 at 18:50 Comment(0)
T
-2

You are applyingModifier.width(300.dp) onto the parent column, the maximum width a child item in that column can occupy is 300.dp.

Using Modifier.fillMaxWidth() on your Text composable in this context is synonymous to using Modifier.preferredWidth(300.dp) because it can only get as wide as it's parent composable.

Twirl answered 31/1, 2021 at 8:44 Comment(1)
Hello, I'm looking for dynamic sizing here. I can't specify a fixed size for the parent column. The column should be as wide as its widest childRedness

© 2022 - 2024 — McMap. All rights reserved.