Jetpack Compose Trick— The hidden secret of the weight modifier 💎

Know the nuances & improve your skills!

The Android Developer

--

Introduction ✍️

Hello everyone 👋 Hope you’re doing well! In this article, I will talk about an important characteristic of the weight modifier in Jetpack compose. I will also show you how we can leverage that to our advantage.

Explanation with an example 🙇‍♂️

I always like to explain things with examples. I believe learning this way would really cement the concept being learned. So, I’m gonna do the same thing in this article.

Let’s say that we are working on a weather app and we want to create a composable like this.

The composable that we want to create

If you have some experience using Jetpack compose, you’d realize that it’s pretty straightforward to code it up. The code for the above composable might look something like this

@ExperimentalMaterial3Api
@Composable
fun CompactWeatherCard(
nameOfLocation: String,
shortDescription: String,
@DrawableRes shortDescriptionIcon: Int,
weatherInDegrees: String,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
val weatherWithDegreesSuperscript = remember(weatherInDegrees) {
"$weatherInDegrees°"
}
OutlinedCard(modifier = modifier, onClick = onClick) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Column {
Text(
text = nameOfLocation,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Medium
)
ShortWeatherDescriptionWithIconRow(
shortDescription = shortDescription,
iconRes = shortDescriptionIcon
)
}
Text(
text = weatherWithDegreesSuperscript,
style = MaterialTheme.typography.displayMedium
)
}
}
}

@Composable
private fun ShortWeatherDescriptionWithIconRow(
shortDescription: String,
@DrawableRes iconRes: Int,
modifier: Modifier = Modifier
) {
Row(
modifier = modifier,
verticalAlignment = Alignment.CenterVertically
) {
Icon(
modifier = Modifier.size(24.dp),
imageVector = ImageVector.vectorResource(id = iconRes),
contentDescription = null,
tint = Color.Unspecified
)
Text(
text = shortDescription,
maxLines = 1,
style = MaterialTheme.typography.labelMedium,
fontWeight = FontWeight.Normal
)
}
}

This definitely works and looks great! But there’s a problem with the composable that might not be that immediately apparent. Let’s see how the composable looks when the location's name is very long.

How the composable looks like when the location’s name is too long

Huh! That doesn't look right. The current temperature doesn't even get displayed in the composable. Let’s look at an excerpt of the above code that defines how the location’s name and the temperature are displayed.

    Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Column {
// name of the location
Text(
text = nameOfLocation,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Medium
)
ShortWeatherDescriptionWithIconRow(
shortDescription = shortDescription,
iconRes = shortDescriptionIcon
)
}
// current weather in degrees
Text(
text = weatherWithDegreesSuperscript,
style = MaterialTheme.typography.displayMedium
)
}

As we can notice, the composable that displays the location’s name and the composable that displays the current weather in degrees are placed inside a Row composable with a specific horizontalArrangement and verticalAlignment.

It’s now pretty clear why the composable looks weird when the location's name is long. It’s simple, the composable doesn't have enough space to display the current weather when the name of the location is too long.

The fix 🔧

What can we do to fix this? Let’s first think of what we need the composable to do when the location's name is too long.

The desired result of what the composable must look like when the location’s name is too long

Now that looks way better! We want enough space for both the text composables to comfortably fit. If the name of the location is too long, then we want the composble that displays the name of the location to be as long as it can be while ensuring that there is sufficient space for the other text composable (the composable that displays the current weather). If there’s not enough space, we want the text composable to append an ellipsis near the end of the string.

If we notice, the text composable that displays the name of the location is already configured in a way that it appends ellipses to the string if it runs out of space.

fun CompactWeatherCard(...){
.
.
// name of the location
Text(
text = nameOfLocation,
maxLines = 1,
overflow = TextOverflow.Ellipsis, // Use an ellipsis to indicate that the text has overflowed.
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Medium
)
.
.
}

Now that we already took care of that. Let’s focus on figuring out how to ensure that both composables have sufficient space.

The hidden secret of the weight modifier 🤫

Here’s where we can take advantage of an interesting property of the weight modifier in compose. The reason why the Text composable spans the entire width is because of the fact that it is the first child of the parent composable. So the parent composable measures it first.

Knowing this fact reveals the real reason why the other text composable is not getting “presented” in the UI. I’ve mentioned the word “presented” in quotes because it is actually composed and rendered, but it’s just pushed out of the screen because the first Text composable uses up all the available space.

Wouldn’t it be better to “ask” the parent composable to measure and render the second composable (the composable responsible for displaying the current weather) first, and then let the first composable (the composable responsible for displaying the name of the location) fill up the remaining available space? Yes! And this is possible in compose. Here’s how we can accomplish it.

Let’s have a look at the documentation of the weight modifier.

Here’s an extremely crucial piece of information mentioned in the documentation of the weight modifier.

The parent will divide the horizontal space remaining after measuring
unweighted child elements and distribute it according to this weight.

This implies that, all composables with the weight modifier added to them will only be measured after the composables that don’t use the weight modifier are measured.

Leveraging the weight modifier 💪

We can leverage this to fix the issue that we are facing with our composable. To fix this, we can add a weight modifier to the text composable that displays the name of the location. This way, the composable will only get measured after the composable that displays the current temperature is measured. This will ensure that the composable that displays the temperature will always have sufficient space for it to display its contents appropriately. Let’s make that change and see the result.

@ExperimentalMaterial3Api
@Composable
fun CompactWeatherCard(
nameOfLocation: String,
shortDescription: String,
@DrawableRes shortDescriptionIcon: Int,
weatherInDegrees: String,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
val weatherWithDegreesSuperscript = remember(weatherInDegrees) {
"$weatherInDegrees°"
}
OutlinedCard(modifier = modifier, onClick = onClick) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
// add weight modifier to the column composable to ensure
// that the composable is measured after the other
// composable is measured.
Column(modifier = Modifier.weight(1f)) {
Text(
text = nameOfLocation,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Medium
)
ShortWeatherDescriptionWithIconRow(
shortDescription = shortDescription,
iconRes = shortDescriptionIcon
)
}
Text(
text = weatherWithDegreesSuperscript,
style = MaterialTheme.typography.displayMedium
)
}
}
}
How the composable looks after using the weight modifier

And, Voila! We’ve fixed the issue 🎉 Now the composable looks great even if the name of the location is too long.

Conclusion ✌️

And that wraps up this short blog post! Hope you now know how to leverage the special property of the modifier composable that we learned about in this article.

If you liked this article, feel free to check out my other articles as well. I would like to thank you for taking the time to read this article😊. I wish you the best of luck! Happy coding 👨‍💻! Go create some awesome Android apps 👨‍💻! Cheers!

--

--

The Android Developer

| A very passionate Android Developer 💚 | An extreme Kotlin fanatic 💜 | A huge fan of Jetpack Compose 💙| Focused on making quality blog posts 📝 |