androidandroid-jetpack-compose

When is it best practice to instantiate a new `Modifier`?


I'm learning Jetpack Compose with an official tutorial that states:

It's a best practice to have your Composable accept a Modifier parameter, and pass that modifier to its first child.

And they provide examples where a @Composable function uses the modifier parameter given by its parent, chains new modifier elements to it, and passes that to its child components.

e.g:

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Hello $name!",
        modifier = modifier.padding(bottom = 16.dp)
    )
}

However, in a later exercise in the provided solution code, the modifier parameter (with a lowercase m) is mostly unused, and instead, a new instatiation of Modifier (with an uppercase M) is typically used.

What is actually the best practice? When do I use the modifier parameter, and when do I create a new instantiation of Modifier ?


Solution

  • All composable functions that emit UI1 should take a parameter of type Modifier as their first optional parameter. Optional means that it has a default value and therefore you do not need to pass such a parameter when you call the function.

    Your example function contains the word modifier three times, each time meaning something different:

    fun Greeting(name: String, modifier: Modifier = Modifier)
    
    1. The name of the parameter is modifier.
    2. The parameter type is Modifier.
    3. The default value is the object Modifier (which has, of course, also the type Modifier). You could use any other Modifier object, but for the default value you shouldn't, it should always be the object that is named Modifier, a global object that represents an empty modifier.

    Now, inside your composable you should use this modifier parameter and apply it to the first UI element you use2. In your example that is the Text composable. It has a modifier parameter of its own that is declared identical to how your Greeting function declares it.

    In your example, you not only pass your modifier parameter to Text, you add another modifier first, effectively creating a modifier chain:

    Text(
        text = "Hello $name!",
        modifier = modifier.padding(bottom = 16.dp)
    )
    

    But you could also simply pass the parameter through:

    Text(
        text = "Hello $name!",
        modifier = modifier
    )
    

    (where the first word modifier is the parameter name of Text and the second is the actual modifier object that was passed to Greeting)

    You could also use this (with a capital M):

    Text(
        text = "Hello $name!",
        modifier = Modifier
    )
    

    It compiles just fine, but now you do not use the modifier parameter that was passed to Greeting anymore, you use the global Modifier object. Since that is the same as the default value declared by Text you could omit it altogether:

    Text(
        text = "Hello $name!"
    )
    

    Conclusion: Use modifier when you want to use the actual object that was passed to your function (it may contain any modifier or modifier chain). Use Modifier when you want to access the global Modifier that is empty and doesn't actually modify anything. You mostly use it as a default value or as the starting point of a modifier chain.


    1 That are composables like Column, Text, or most of the custom composables you will create, but not remember and the likes; they are composable functions but they do not emit UI.

    2 Only use it on the first element, do not use it on any other elements. For more See the official API Guidelines for @Composable components in Jetpack Compose.