Solution 1:

There’s Layouts in Jetpack Compose codelab containing Layout modifiers under the hood step which explains the modifier order, see "Order matters" section.

order matters when chaining modifiers as they're applied to the composable they modify from earlier to later, meaning that the measurement and layout of the modifiers on the left will affect the modifier on the right. The final size of the composable depends on all modifiers passed as a parameter. First, modifiers will update the constraints from left to right, and then, they return back the size from right to left.

To understand it better I'd recommend to figure out how layouts work in Compose. In short, padding() is a LayoutModifer, it takes in some constraints, measures its child size based on a projection of that constraints and places the child at some coordinates.

Let’s see an example:

Box(
  modifier = Modifier
    .border(1.dp, Color.Red)
    .size(32.dp)
    .padding(8.dp)
    .border(1.dp, Color.Blue)
)

And the result:

enter image description here

But let's swap the .size() and the .padding()

Box(
  modifier = Modifier
    .border(1.dp, Color.Red)
    .padding(8.dp)
    .size(32.dp)
    .border(1.dp, Color.Blue)
)

Now we have a different result:

enter image description here

I hope this sample helps you to figure out how the modifiers are applied.

One can expect that the red border should be the closest to the box since it was added first, so the order might seem reversed, but such an order has pros too. Let’s take a look at this composable:

@Composable
fun MyFancyButton(modifier: Modifier = Modifier) {
  Text(
    text = "Ok",
    modifier = modifier
      .clickable(onClick = { /*do something*/ })
      .background(Color.Blue, RoundedCornerShape(4.dp))
      .padding(8.dp)
  )
}

Just by moving the modifier to the arguments the composable allows its parents to add additional modifiers such as extra margin. Because the lastly added modifiers are the closest to the button, the border and the inner padding won’t be affected.

Solution 2:

  • In Android Compose resulting Image is being constructed from the outside layer toward the Composable in the center. This means that first defined Green border is outer border and the last defined Red border is inner border . This is very confusing since Green Modifier that is closest to Text Composable in the Code is furthest from it in the result.
  • This is in contrast to SwiftUI where Modifiers appear in the same order both in the Code and in the resulting Image. Modifier that is closest to the Composable in the Code is also closest to it in the resulting Image.
  • If you want to imagine that resulting Image is being constructed from the center where your Composable is positioned (like in SwiftUI) then Modifiers are applied in the opposite order from which they are given (from the bottom upward).
  • So if you have Text Composable with two border Modifiers
    • border Modifier that is furthest away from the Text Composable in the Code (the bottom Red one)
    • will be closest to the Text Composable in the resulting Image
  • Modifiers are applied from outer toward inner layer
    • Applying .border(2.dp, Color.Green) to the outmost layer
    • Applying .padding(50.dp) going inward
    • Applying .border(2.dp, Color.Red) to the innermost layer
package com.example.myapplication

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.padding
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.setContent
import androidx.compose.ui.unit.dp

class MainActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
      Text("Hi there!",
        Modifier
          .border(2.dp, Color.Green)
          .padding(50.dp)
          .border(2.dp, Color.Red)
      )
    }
  }
}

enter image description here