David Suárez
David Suárez

Reputation: 23

Learning Kotlin problem. Cannot understand Modifier parameter

I'm learning programming for the first time in my life. I've spent around 2 weeks learning Kotlin with a free course, and a week learning Jetpack Compose in Android Studio specifically. I've been stuck two days with a practice question, but every answer I encounter on the internet assumes I know more than I know. The practice consists in making a Quadrant like this CorrectQuadrant but unless I make a change that doesn't make sense to me I'm only able to get it like this IncorrectQuadrant.

So the thing is I don't quite understand the modifier parameter, so trying and trying I finally downloaded the solution code and found the mistake, but I cannot understand why it is a mistake. In the code below, in the private function "ComposeInfoCard", you can see a modifier Column argument surrounded by (=====). If I write that argument in lowercase (modifier = modifier) I get the correct Quadrant. If I write it like it is (modifier = Modifier), and that's how I supossed it was correct, the modifier parameter in the private function says "Parameter modifier is never used", and that's not true because I'm using it four times for the weight modifier in the ComposeQuadrant function.

Can someone explain why it needs to be in lowercase, and, if you don't mind, explain the object modifier like I'm a completely new to this?

@Composable
fun ComposeQuadrant(){
    Column (modifier = Modifier.fillMaxWidth()){
        Row (modifier = Modifier.weight(1f)){
            ComposeInfoCard(
                title = stringResource(R.string.title1),
                description = stringResource(R.string.description1),
                backgroundColor = Color(0xFFEADDFF),
                modifier = Modifier.weight(1f)
            )
            ComposeInfoCard(
                title = stringResource(R.string.title2),
                description = stringResource(R.string.description2),
                backgroundColor = Color(0xFFD0BCFF),
                modifier = Modifier.weight(1f)
            )

        }
        Row (Modifier.weight(1f)){
            ComposeInfoCard(
                title = stringResource(R.string.title3),
                description = stringResource(R.string.description3),
                backgroundColor = Color(0xFFB69DF8),
                modifier = Modifier.weight(1f)
            )
            ComposeInfoCard(
                title = stringResource(R.string.title4),
                description = stringResource(R.string.description4),
                backgroundColor = Color(0xFFF6EDFF),
                modifier = Modifier.weight(1f)
            )

        }
    }

}
@Composable
private fun ComposeInfoCard(
    title: String,
    description: String,
    backgroundColor: Color,
    modifier: Modifier = Modifier) {
=========================================================
    Column (modifier = Modifier
=========================================================
        .padding(16.dp)
        .background(backgroundColor)
        .fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ){
        Text(
            text = title,
            fontWeight = FontWeight.Bold,
            modifier = Modifier.padding(bottom = 16.dp)
        )
        Text(
            text = description,
            textAlign = TextAlign.Justify
        )

    }

}

Upvotes: 2

Views: 671

Answers (3)

Pawan Harariya
Pawan Harariya

Reputation: 1404

Short-Explaination : modifier small-case is the existing modifier that was created in ComposeQuadrant with property weight of 1f. When you use Modifier upper case, you create a new modifier object, so it doesn't have this weight property from before.

Long-Explaination :

  1. Understand functions in Kotlin Consider a function that prints a greeting to someone. It takes two input arguments the greeting and the name of the person to greet to.

Arguments - These are the inputs that you provide when you call the function, the function can then use the input variables. However we need to define the data type parameter of these variable, i.e. whether its a String or an Int, etc.

fun greetMe(greeting : String, name : String) {
    print("$greeting to $name") 
    // the name used here is the one that I get from the code that calls this function
}

Now we, call the function

greetMe("Hi", "Pawan")

The output is

Hi to Pawan
  1. Understand Named Arguments in Kotlin

Now, suppose the same greetMe function is called in this way

greetMe("Pawan", "Hi")

The output is

Pawan to Hi

Is there anything wrong with the code implementation? No! Because both "Pawan" and "Hi" are Strings and that's what the function requires. But is this logically correct? No! because we have provided input in wrong order. To handle this we use named arguments, and we call the function in this way

greetMe(name = "Pawan", greeting = "Hi")

Although the sequence is which we provided the input is wrong, but the output is correct because we have used named arguments, so now they will be mapped to correct variables.

Hi to Pawan

Now coming to question...

ComposeInfoCard(modifier = Modifier.weight(1f))

Here you are calling the ComposeInfoCard and using named arguments. You create a new Modifier object change its property i.e. set weigh to 1f.

private fun ComposeInfoCard(modifier: Modifier = Modifier) {
}

Okay, so I hope that now you understand the first modifier, it is a named argument and not a variable. The above line says that you will get a modifier as an arugment and the data type of it will be Modifier. The part after = sign is what we call a default value, i.e. if a modifier is not provided by the calling function, you use this, which in this case is a new Modifier.

So now inside your ComposeInfoCard method, you have a modifier variable, which is provided by the calling function.

private fun ComposeInfoCard(
    // modifer that you get here is not used anywhere
    modifier: Modifier = Modifier) {
    Column (modifier = Modifier // because here you are creating a new modifier
        .background(backgroundColor)
        .fillMaxSize()
   )

Now here you call another function, which is Column it also needs a modifier, you have created a new Modifier object and changed is background color. What you don't want is to create a new Modifier object but to use the same modifier which is small-case modifier that you already have.

private fun ComposeInfoCard(
     // modifier that you get here
    modifier: Modifier = Modifier) {

    Column (modifier = modifier   // same modifier is passed here with previous properties
        .background(backgroundColor)
        .fillMaxSize()
   )

NOTE : Spend some more time with basics of Kotlin like functions, named arguments, defaults, scope of a variable, lambda functions, etc before moving to complex Library like Jetpack Compose. Maybe try creating some basic projects in Kotlin before moving to Android.

Upvotes: 6

Eselfar
Eselfar

Reputation: 3869

In your ComposeInfoCard function you have a modifier parameter of type Modifier.

Then when you do Column (modifier = modifier Here you want to use this modifier parameter. If you do Column (modifier = Modifier then, instead of using the Modifier you passed as parameter to the function, you create a new Modifier. That's why you get the message saying that the modifier parameter is not used, and that your ComposeInfoCard doesn't behave as espected.

Upvotes: 1

Giorgos Neokleous
Giorgos Neokleous

Reputation: 1767

It works with the lowercase modifier because you need to use the one passed from the ComposeQuadrant() to the ComposeInfoCard.

If you use the uppercase modifier, Modifier, then you are constructing a new modifier to use but what you need in your example is the modifier passed as an argument to the ComposeInfoCard as that has some important decoration such as the weight(1f).

What happens in your example is that you have the ComposeQuadrant function which determines through the weight(1f) where to place the ComposeInfoCard. Then the ComposeInfoCard only cares about its decorations/configurations that it needs such as padding, background and size. In summary, ComposeInfoCard only cares about it's appearance but not where it's placed in the container. The container will instruct where it's placed and that happens through the modifier: Modifier argument passed to the ComposeInfoCard function.

Upvotes: 0

Related Questions