1

I define a val modifier with height(30.dp), then I pass it with modifier.height(5.dp) to Spacer function.

I think the final height of Spacer should be 5.dp because I have overwritten the height of val modifier.

It seems that the height of Spacer isn't 5.dp, what's wrong with my code?

Code A

val modifier = Modifier.fillMaxWidth().height(30.dp)
    
Spacer(modifier = modifier.height(5.dp))
HelloCW
  • 843
  • 22
  • 125
  • 310

2 Answers2

3

Order of Modifiers in Composable functions is important.

Refer to this solution and other solutions in the question for more info on this.


If you want the height inside the Spacer to be the final one, you have to use Modifier.then().

The given code

val modifier = Modifier.fillMaxWidth().height(30.dp)
Spacer(modifier = modifier.height(5.dp))

is same as

Spacer(modifier = Modifier.fillMaxWidth().height(30.dp).height(5.dp))

But, if you change it using then() like this

val modifier = Modifier.fillMaxWidth().height(30.dp)
Spacer(modifier = Modifier.height(5.dp).then(modifier))

It would become

Spacer(modifier = Modifier.height(5.dp).fillMaxWidth().height(30.dp))

Sample code

@Composable
fun OrderOfModifiers() {
    val modifier = Modifier
        .fillMaxWidth()
        .height(30.dp)
    Column(
        modifier = Modifier
            .fillMaxSize(),
    ) {
        Spacer(modifier = modifier
            .height(5.dp)
            .background(DarkGray),
        )
        Spacer(modifier = Modifier
            .height(5.dp)
            .then(modifier)
            .background(Cyan),
        )
    }
}

Sample screenshot

screenshot

Abhimanyu
  • 11,351
  • 7
  • 51
  • 121
2

When chaining size modifiers such as Modifier.height(height1).height(height2) first one is used by design. As in Abhimanyu's answer using Modifier.then() doesn't change this either, first one is used. Then changes in which order modifiers are applied.

This approach creates opportunity for developers to assign default size when no modifier with size is set by devs use this Composable. Slider is built in similar fashion, it covers full width by default.

When you create a Composable such as

@Composable
private fun MyComposable(modifier: Modifier=Modifier){
    Box(modifier = modifier
        .fillMaxWidth()
        .size(48.dp))
}

And use it as

MyComposable(modifier = Modifier.border(3.dp, Color.Green))

Will result having a Composable with full screen width and 48.dp height. If you set a Modifier with a size default one gets overridden

MyComposable(modifier = Modifier.border(3.dp, Color.Red).size(50.dp))

enter image description here

Thracian
  • 43,021
  • 16
  • 133
  • 222
  • Thanks! I have tested your code and get the same result, but I'm so strange why `MyComposable(modifier = Modifier.border(3.dp, Color.Red).size(50.dp))` make `fillMaxWidth()` disabled ? – HelloCW Aug 26 '22 at 01:36
  • It's not disabled. When you use `Modifier.width(w1).width(w2).width(w3)`first width Modifier is used. The ones that follow are not taken into consideration unless you use `Modifier.required`. This design approach lets you assign default width if any other size Modifier is assigned. So you can create a Composable with dimensions instead of 0 size when there is no modifier. `Slider` is an example for this. It fills width by default. – Thracian Aug 26 '22 at 03:18
  • If i don't assign `modifier.fillMaxWidth()` to MyComposable if anyone uses this Composable forgets to assign a Modifier its size would be zero but with a default modifier even if you don't assign a size modifier it would still have a default size. That's a good approach to have default dimensions when there is no other size modifier but when there is you use the one comes from user or developer – Thracian Aug 26 '22 at 03:20
  • With Modifier.then you might change order from `Modifier.width(w1).width(w2)`to `Modifier.width(w2).width(w1)` but same thing applies first one is used nonetheless. But you change which modifier will be first – Thracian Aug 26 '22 at 03:20