One of the most powerful pieces of Jetpack Compose is the concept of Modifiers. These little pieces of code are some of the basic building blocks of compose and you cannot go far without using them. They are how you apply padding, set backgrounds, set borders, make things clickable, and so much more. For being at the foundation of your composable functions and for being used as much as they are; there is still a lot of confusion that can come because of these little bits of code.
That confusion stems from primarily one thing: order. In Compose, you can and should be chaining the modifiers for a function together. The order they get chained can greatly affect things. One such example is applying padding on something that is clickable, whether you apply the padding before or after the clickable can affect how far the ripple extends.
So lets dive into demystifying the order of modifiers and play around with some examples to solidify what we learn.
Our starting point:
Box(
modifier = Modifier
.size(400.dp)
.background(Color.White)
) {
Box(modifier = Modifier
.background(Color.Red)
.size(64.dp)
)
}
And here is what that looks like:
If we wanted to add some padding to the red square we have a few options but the two that may come to mind the most are:
a) at the beginning of the modifier chain
Modifier
.padding(8.dp)
.background(Color.Red)
.size(64.dp)
b) at the end of the modifier chain.
Modifier
.background(Color.Red)
.size(64.dp)
.padding(8.dp)
From the above we can see that option a) is most likely the option we are wanting in this scenario. The red square has been moved away from the edges like we would expect padding to do. Some of you may be wondering what happened to option b). Why didn't the padding work there and what exactly did applying the padding at the end do? Well we can see what happened by adding some more color.
Lets add another background to the red square to show whats going on.
Modifier
.background(Color.Red)
.size(64.dp)
.padding(8.dp)
.background(Color.Black)
Which now makes our result look like this:
Here we can see that when we put padding at the end of the modifier chain it applied that padding "inside" the box rather than to the box as a whole. So when it comes to spacing and placing things we want to apply the padding first.
This is actually a beneficial pattern though. As we build out screens and components as composables it is very common to pass in a modifier as a parameter. Since we want to apply padding first this makes it incredibly easy to let a component worry about itself, while the parent composable that calls the component can apply padding to place the component appropriately. This makes for very reusable components.
There are times though that we may want to apply padding not at the start of the chain. Say we want to apply a border to the red square, but we want that border spaced slightly out from the square. To get such affect we can do something like this to the modifier:
Modifier
.padding(8.dp)
.border(1.dp, Color.Black)
.padding(2.dp)
.background(Color.Red)
.size(64.dp)
Hopefully this help demystify what order you should be calling your modifiers in. When in doubt, try it out. I encourage you to explore and try different modifier orders to help better understand what is going on. But also don't be hard on yourself if its not clicking or you still fail to put something in the right order. I've been working with Compose for years now and still get it wrong.
PS. If you are still wondering if you should apply the padding before or after the clickable. It really just depends on where you want the ripple. You have a Box that should contain its ripple, then apply the padding before clickable. If you have a list item or text, you may want to apply some padding after clickable so the ripple goes past the text.
Top comments (0)