Chapters

Hide chapters

Jetpack Compose by Tutorials

First Edition · Android 11 · Kotlin 1.4 · Android Studio Canary

Before You Begin

Section 0: 3 chapters
Show chapters Hide chapters

6. Using Compose Modifiers
Written by Denis Buketa

A beautiful UI is essential for every app. It doesn’t just look nice, it also makes your app more fun to use. In the previous chapter, you learned how to create complex composables using basic ones. You also started working on the Note and AppDrawer composables. Now, you’ll learn how to make your composables look as beautiful as they are in your ideal design.

In this chapter, you’ll:

  • Learn how to style your composables using modifiers.
  • Style Note to make it look like it should in the final design.
  • Add more composables to Jet Notes.

From this point on, every composable you complete will be as beautiful as in your design, by adding those modifiers you’ve been hearing about for the past few chapters. :]

Modifiers

Modifiers tell a UI element how to lay out, display or behave within its parent layout. You can also say that they decorate or add behavior to UI elements.

In the previous chapter, you started working on Note().

Note composable - current and final state
Note composable - current and final state

In the figure above, you can compare where you left off (above) with how it’ll look like by the end of this chapter (below).

To follow along with the code examples, open this chapter’s starter project in Android Studio and select Open an existing project.

Next, navigate to 06-using-compose-modifiers/projects and select the starter folder as the project root. Once the project opens, let it build and sync and you’re ready to go!

Note that if you skip ahead to the final project, you’ll be able to see the completed Note() and some other composables that you’ll implement during this chapter.

Whatever you choose, we’ll start off by building the NoteColor widget.

Adding NoteColor

The first thing you’ll improve in your Note() is the NoteColor. In the ui.components package, create a new Kotlin file named NoteColor.kt, then add the following code to it:

@Composable
fun NoteColor() {
  Box(
    modifier = Modifier
      .size(40.dp)
      .background(Color.Red)
  )
}

@Preview
@Composable
fun NoteColorPreview() {
  NoteColor()
}

To make this work, add the following imports:

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp

Look at the code now and you can see that you created a Box and passed a modifier to it. In this example, you used two modifier functions: Modifier.size() and Modifier.background().

Modifier.size() declares the size of the content. You pass the value in density-independent pixels (dp) and set the element width and height to the same value.

Modifier.background() draws a shape with a solid color behind the content. In this case, you passed Color.Red.

As you can see, you can easily chain several modifiers, one after the other, to combine them. In this example, you started the modifier chain with Modifier, which represents an empty modifier object.

Finally, you used NoteColorPreview() to preview your composable in the preview panel.

Build your project and check the preview and you’ll see something like this:

NoteColor — Preview
NoteColor — Preview

Congratulations, you just created a very simple composable that is 40dp in size and has a red background. Let’s see if we can make it even nicer!

Chaining modifiers

Now you have the basic NoteColor(), but you still have to add a couple of modifiers to make it match the design.

The next thing you’ll do is to make your composable’s content round. In the previous example, you saw how to chain multiple modifiers. Here, you’ll apply the same principle and change the code so it includes one additional modifier:

@Composable
fun NoteColor() {
  Box(
    modifier = Modifier
      .size(40.dp)
      .background(Color.Red)
      .clip(CircleShape) // here
  )
}

Don’t forget to include these imports as well:

import androidx.compose.foundation.shape.CircleShape
import androidx.compose.ui.draw.clip

Now, build your project and check the preview. You’ll see something like this:

NoteColor - Preview
NoteColor - Preview

Surprised? Don’t blame yourself if you tried to refresh the preview panel, expecting a different result. :]

The order of modifiers in the chain matters. Each modifier not only prepares the composable for the next modifier in the chain, but it also modifies the composable at the same time.

With this in mind, try to break down the code you wrote. With Modifier.size(), you defined the width and the height of the composable.

After that, you have Modifier.background(Color.Red). Since UI elements are represented by rectangular blocks, you end up with a red square.

Then you added Modifier.clip(), which clips the content to a specific shape. Since the two modifiers before already modified the composable, your composable didn’t change. The content remained the same.

To make this clearer, try adding another Modifier.background composable to the chain:

@Composable
fun NoteColor() {
  Box(
    modifier = Modifier
      .size(40.dp)
      .background(Color.Red)
      .clip(CircleShape)
      .background(Color.Yellow) // here
  )
}

Build the project and check the preview. You’ll see something like this:

NoteColor - Preview
NoteColor - Preview

Now, you can visualize the effect that Modifier.clip() has on your composable. It clipped the future content into a circle shape, so when you applied the Modifier.background(Color.Yellow), you ended up with a yellow circle in the red square.

Taking this behavior into consideration, you can now continue working on NoteColor() to make it look like the design. Reorder the modifiers to get a circular shape with a specific color:

@Composable
fun NoteColor() {
  Box(
    modifier = Modifier
      .size(40.dp)
      .clip(CircleShape)
      .background(Color.Red)
  )
}

Here, you moved Modifier.clip() to come before the point where you specify the composable background. With that, you clipped the content of your composable to a circle whose width and height are set to the value you specify with Modifier.size().

Build the project and check the preview:

NoteColor - Preview
NoteColor - Preview

Excellent! You just created a composable that emits a colored circle of the size you specified.

Rounding out the NoteColor

There are a few more things you need to add before you wrap up this composable. One thing that’s missing is the border. To add that, update the code in NoteColor() like so:

@Composable
fun NoteColor() {
  Box(
    modifier = Modifier
      .size(40.dp)
      .clip(CircleShape)
      .background(Color.Red)
      .border( // new code
        BorderStroke(
          2.dp,
          SolidColor(Color.Black)
        ),
        CircleShape
      )
  )
}

To make Android Studio happy, add the following imports as well:

import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.border
import androidx.compose.ui.graphics.SolidColor

Here, you added Modifier.border(), which gives you a border with the appearance you specified using the border and shape.

For the border, you passed BorderStroke(), which defined the width of the border and its color. For the shape, you used the same shape as you did when clipping the content.

Build the project and you’ll see something like this in the preview panel:

NoteColor - Preview
NoteColor - Preview

Adding some padding

If you check the design you’ll notice that there should be some padding around NoteColor(). To fix that, update the code like this:

@Composable
fun NoteColor() {
  Box(
    modifier = Modifier
      .padding(4.dp) // here
      .size(40.dp)
      .clip(CircleShape)
      .background(Color.Red)
      .border( 
        BorderStroke(
          2.dp,
          SolidColor(Color.Black)
        ),
        CircleShape
      )
  )
}

Don’t forget to add the necessary import:

import androidx.compose.foundation.layout.padding

Modifier.padding() applies additional space to each edge around the content. In the code above, you used 4.dp. Note that when you don’t specify which edge to pad, the padding will be applied to all of them. You can also specify to which edge you want to apply the padding with the following named arguments: start, top, end and bottom.

It’s important to pay attention to the order in the chain where you added the modifier. You want your circle to be the size you specify with the Modifier.size. You also want your padding to be applied around that circle. So, the best place to put Modifier.padding is just before Modifier.size.

By doing that, you’ll first apply the padding to your composable, then you’ll reserve the space of the specified size for your content.

Build the project and check the preview. It will now look like this:

NoteColor - Preview
NoteColor - Preview

Improving NoteColor’s usability

Regarding NoteColor(), you have all the necessary code to fulfill the design. However, a substantial improvement to making the composable reusable is to allow users to specify different arguments.

Right now, you’ve hard-coded the values for the size, background color, padding and border width, but your users should be able to change them.

To implement this, just expose those values as parameters by replacing NoteColor() with the following code:

@Composable
fun NoteColor(
  color: Color,
  size: Dp,
  padding: Dp = 0.dp,
  border: Dp
) {
  Box(
    modifier = Modifier
      .padding(padding)
      .size(size)
      .clip(CircleShape)
      .background(color)
      .border(
        BorderStroke(
          border,
          SolidColor(Color.Black)
        ),
        CircleShape
      )
  )
}

Don’t forget this import:

import androidx.compose.ui.unit.Dp

Here, you changed the signature of NoteColor() to accept a color, size, padding and border. You then replaced the hard-coded values with the new parameters.

Now, you need to adapt NoteColorPreview() and specify the right parameters. Replace NoteColorPreview() with the following code:

@Preview
@Composable
fun NoteColorPreview() {
  NoteColor(
    color = Color.Red,
    size = 40.dp,
    padding = 4.dp,
    border = 2.dp
  )
}

Here, you’ve used the same values as before, so your preview should remain the same.

Build the project to make sure everything works as expected.

NoteColor — Preview
NoteColor — Preview

The next step is to add your new component to the Note.

Adding NoteColor to Note

Great work on completing NoteColor()! You can now use it in your Note to make it match the design.

In Note.kt, replace the Note() implementation with the following code:

@Composable
fun Note() {
  Row(modifier = Modifier.fillMaxWidth()) {
    NoteColor( // NoteColor instead of Box
      color = rwGreen,
      size = 40.dp,
      padding = 4.dp,
      border = 1.dp
    )
    Column(modifier = Modifier.weight(1f)) {
      Text(text = "Title", maxLines = 1)
      Text(text = "Content", maxLines = 1)
    }
    Checkbox(
      checked = false,
      onCheckedChange = { },
      modifier = Modifier.padding(start = 8.dp)
    )
  }
}

In the code above, you removed the Box that you used as a placeholder, then added your beautiful NoteColor.

Build the project now and you’ll see something like this in the preview panel:

Note Composable — Preview With NoteColor
Note Composable — Preview With NoteColor

While you’re here, check out the modifiers that you added in the previous chapter, when you were working on Note.

For Row(), you used Modifier.fillMaxWidth(). This modifier allows you to specify the fraction of the available width that the composable should use. By default, the fraction is 1f. So in this case, you specified that the Row should take the maximum available width.

For Column(), you used Modifier.weight(). If you’re familiar with the weight property in XML layouts, then you already know what it does. With weight, you size the element’s width proportional to its weight relative to other weighted sibling elements.

Check the definition of this modifier. It’s defined in RowScope, which means you can use it on elements in a Row. In this case, you used it to make the Column take the available width between NoteColor and Checkbox.

Adding a background to Note

Look at Note’s design and notice that it has a white background, its corners are rounded and there’s a small shadow around it. Luckily, you can easily use modifiers to add those features!

Update Note() code to add the necessary modifiers to Row(), as shown below:

@Composable
fun Note() {
  val backgroundShape: Shape = RoundedCornerShape(4.dp)
  Row(
    modifier = Modifier
      .padding(8.dp)
      .shadow(1.dp, backgroundShape)
      .fillMaxWidth()
      .preferredHeightIn(min = 64.dp)
      .background(Color.White, backgroundShape)
  ) {
    ...
  }
}

As usual, don’t forget to add the necessary imports:

import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.foundation.layout.preferredHeightIn

With this code, you introduced four modifiers, two of which are completely new to you:

  • Modifier.padding: Adds some space between the note and screen edges.
  • Modifier.shadow: Creates DrawLayerModifier, which draws the shadow. The elevation defines the visual depth of a physical object. Furthermore, the physical object has a shape. In this case, you defined that the elevation should be 1.dp and, for the shape, you used RoundedCornerShape() to define that the corners should be rounded with a radius of 4dp.
  • Modifier.background: This is a simple one. As you already learned, it draws a shape with a solid color behind the content.
  • Modifier.preferredHeightIn: Constrains the height of the content between min and max values. In this case, you don’t need a max value, but you do need a min because you want your composable to be at least 64dp in height.

Now, build your project and run the app to check how Note looks.

Note Composable With Background
Note Composable With Background

Great! You’ve successfully added a background to your note. Pay attention to the corners: They’re rounded now, and there’s a shadow around the note. Also, see how the height of the note matches what you specified.

At this point, however, you’ve probably noticed that the overall composable looks awkward. That’s because its content isn’t centered. Fixing the alignment will be your next task.

Centering the Text & Checkbox composables

You’re getting closer and closer to completing your Note(). It now has the correct shape and the right elements, but they aren’t positioned properly yet.

Your first step is to fix the position of the Text and Checkbox. To do this, update the code for Column and Checkbox in Note() so it looks like this:

Column(
  modifier = Modifier
    .weight(1f)
    .align(Alignment.CenterVertically)
) {
  Text(text = "Title", maxLines = 1)
  Text(text = "Content", maxLines = 1)
}
Checkbox(
  checked = false,
  onCheckedChange = { },
  modifier = Modifier
    .padding(16.dp)
    .align(Alignment.CenterVertically)
)

For this to work, add this import:

import androidx.compose.ui.Alignment

The key to aligning your composables in this code is Modifier.align(alignment: Alignment.Vertical). This modifier allows you to align elements vertically within the Row.

Just like Modifier.weight(), this modifier is defined in a RowScope. That means you can only use it in a Row.

Notice that you also added Modifier.padding() to the checkbox to make it look nicer. This doesn’t affect the composable alignment, but it’s a good practice to pay attention to the details.

Build and run the app and your note will look like this:

Note Composable Centered Text
Note Composable Centered Text

Now, both the text and the checkbox are nicely centered in the note.

Centering NoteColor

When you look at NoteColor, you realize that you can’t apply a modifier to it like you did for the Column and Checkbox. The NoteColor doesn’t expose a modifier as its parameter. It’s time to fix that!

In NoteColor.kt, update NoteColor() so it looks like this:

@Composable
fun NoteColor(
  modifier: Modifier = Modifier, // 1
  color: Color,
  size: Dp,
  padding: Dp = 0.dp,
  border: Dp
) {
  Box(
    modifier = modifier // 2
      .padding(padding)
      .size(size)
      .clip(CircleShape)
      .background(color)
      .border(
        BorderStroke(
          border,
          SolidColor(Color.Black)
        ),
        CircleShape
      )
  )
}

There are two things to notice here:

  1. You added a modifier as a parameter to your custom composable and you initialized it with an empty Modifier.
  2. You used that modifier as the first in your chain of modifiers in Box(). Remember that, earlier, you used an empty Modifier here instead.

Note: Take the time to digest the difference between using modifier vs Modifier. Only one single character separates the two, but the meaning is completely different. Understanding this can help you avoid quite a few bugs in the future.

What you just did is considered a good practice when creating custom composables with Jetpack Compose. It’s always useful to expose the modifier as a parameter and to allow users of that composable to add other modifiers, as needed.

Now, go back to Note.kt and align the NoteColor as well:

NoteColor(
  modifier = Modifier.align(Alignment.CenterVertically),
  color = rwGreen,
  size = 40.dp,
  padding = 4.dp,
  border = 1.dp
)

As you see, you can now apply the same logic as you did for the Text and Checkbox, so you add Modifier.align() to NoteColor.

Build and run the app.

Note Composable Centered
Note Composable Centered

Nice! Every component is now nicely centered in the note. However, the NoteColor and the Text are a bit cramped on the left side of the note. You’ll work on that next.

Taking advantage of the modifier parameter

As mentioned before, when working on custom composables it’s a good practice to think about how someone might use that composable.

For NoteColor you exposed the color, size, padding, border and modifier to make it more flexible. However, you have to be careful not to overdo it. Having a lot of parameters can introduce more complexities than you need.

By exposing the modifier as a parameter, you suddenly allow a lot of customization for your composable. That means that you might be able to remove some parameters because the behavior they provided can now be taken over by the modifier.

For NoteColor, notice that you’re passing the padding as a parameter. That was useful when you didn’t have the modifier as a parameter, but now, you don’t need it. You’ll do something about that next. :]

Open NoteColor.kt and update the code to look like this:

@Composable
fun NoteColor(
  modifier: Modifier = Modifier,
  color: Color,
  size: Dp,
  border: Dp
) {
  Box(
    modifier = modifier
      .size(size)
      .clip(CircleShape)
      .background(color)
      .border(
        BorderStroke(
          border,
          SolidColor(Color.Black)
        ),
        CircleShape
      )
  )
}

@Preview
@Composable
fun NoteColorPreview() {
  NoteColor(
    color = Color.Red,
    size = 40.dp,
    border = 2.dp
  )
}

In the code above, you removed padding from the composable’s parameters. You also removed Modifier.padding() from the chain of modifiers for the Box. You also updated NoteColorPreview so it doesn’t include padding in the parameters.

Now, you’ll get the padding by calling the composable through the modifier.

Applying the padding

In Note.kt, update NoteColor like this:

NoteColor(
  modifier = Modifier
    .align(Alignment.CenterVertically)
    .padding(start = 16.dp, end = 16.dp), // here
  color = rwGreen,
  size = 40.dp,
  border = 1.dp
)

To add horizontal padding to NoteColor, you added Modifier.padding() to the Modifier that you pass as one of its parameters.

Build and run the app, and you’ll see the following result:

Note Composable With Modifiers
Note Composable With Modifiers

Great job, your note almost matches the design now!

However, the devil is in the details, and there’s one thing still missing: the text style. Right now, both the title and the content have the same text style. You’ll work on this next.

Styling title and content

Look at the note design once again and you’ll see that the title and content have specific text styles. The content text is smaller and has a different color. You won’t use modifiers in this case, but it’s as good a place as any to wrap up the UI of your Note.

In Note.kt, edit the code for Column() so it looks like this:

Column(
  modifier = Modifier
    .weight(1f)
    .align(Alignment.CenterVertically)
) {
  Text(
    text = "Title",
    color = Color.Black,
    maxLines = 1,
    style = TextStyle(
      fontWeight = FontWeight.Normal,
      fontSize = 16.sp,
      letterSpacing = 0.15.sp
    )
  )
  Text(
    text = "Content",
    color = Color.Black.copy(alpha = 0.75f),
    maxLines = 1,
    style = TextStyle( // here
      fontWeight = FontWeight.Normal,
      fontSize = 14.sp,
      letterSpacing = 0.25.sp
    )
  )
}

To avoid complaints from Android Studio, add these imports:

import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp

Here, you used the style and color in your Texts to apply Material Design to your note.

TextStyle() is a styling configuration for the Text. It exposes different parameters like fontWeight, fontSize, letterSpacing and more that let you style the text.

In Chapter 8, “Applying Material Design To Compose”, you’ll see how you can use Material components provided in Jetpack Compose to easily accomplish the same result. But for now, it’s good to notice that you can accomplish the same thing using basic components.

Build and run the app. The note now looks like this:

Note Composable
Note Composable

Well done! Your note composable is now as beautiful as it is in the design. :]

Now that you’ve completed the Note, it would be nice to add some new composables to Jet Notes.

Adding the Color composable

No, you’re not experiencing deja vu. This will be a different composable from the previous NoteColor. :]

Since you’ve completed NoteColor, it makes sense to add a composable that relies on it to build extra functionality.

So now, you’ll start working on a color picker composable, like the one shown below:

Color Picker
Color Picker

The color picker allows the user to color code their notes by assigning specific colors to them. The user can open the color picker by clicking on the color palette icon in the app bar or by pulling from the bottom edge of the screen.

Color Picker — Components
Color Picker — Components

You can break this composable down into smaller ones, as shown in the figure above. By following the bottom-up approach, you’ll work on ColorItem first.

You’ll use this composable in the Save Note screen.

Creating the ColorItem

Start by creating a new package called screens. Then, in this package, create a new Kotlin file named SaveNoteScreen.kt. Finally, add the following code to SaveNoteScreen.kt:

@Composable
fun ColorItem(
  color: ColorModel, 
  onColorSelect: (ColorModel) -> Unit
) {
  Row(
    modifier = Modifier
      .fillMaxWidth()
      .clickable(
        onClick = {
          onColorSelect.invoke(color)
        }
      )
  ) {
    NoteColor(
      modifier = Modifier.padding(10.dp),
      color = Color.fromHex(color.hex),
      size = 80.dp,
      border = 2.dp
    )
    Text(
      text = color.name,
      fontSize = 22.sp,
      modifier = Modifier
        .padding(horizontal = 16.dp)
        .align(Alignment.CenterVertically)
    )
  }
}

For this to work, you need to add the necessary imports:

import androidx.compose.material.Text
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.raywenderlich.android.jetnotes.domain.model.ColorModel
import com.raywenderlich.android.jetnotes.ui.components.NoteColor
import androidx.compose.ui.graphics.Color
import com.raywenderlich.android.jetnotes.util.fromHex

OK, it’s time to break down the code. In the design, two components work together to make the ColorItem composable: NoteColor and Text. They’re aligned next to each other, so you use a Row to position them.

There’s one new modifier here that you haven’t used so far: Modifier.clickable() in Row. With that modifier, you made the whole ColorItem clickable. As mentioned before, it’s a good practice to expose click events to parent composables.

To accomplish that, you passed the onColorSelect.invoke(color) call for the onClick. onColorSelect is of type (ColorModel) -> Unit, which is known as a function type. This specific function type says that the function that will be passed to it should take ColorModel as an argument.

To execute it, you used the invoke(color) operator. This means that when the user clicks on a note, the function that was passed for the onColorSelect parameter will execute.

ColorItem has two parameters:

  • A color parameter of type ColorModel, which represents a model class for the color.
  • The onColorSelect parameter of type (ColorModel) -> Unit. This is a lambda that takes ColorModel as an argument. That way, you allow the parent composable to know which color the user selected.

Previewing the ColorItem

Finally, add the following preview function to the bottom of SaveNoteScreen.kt so you can preview your ColorItem:

@Preview
@Composable
fun ColorItemPreview() {
  ColorItem(ColorModel.DEFAULT) {}
}

Don’t forget to add the Preview import:

import androidx.compose.ui.tooling.preview.Preview

Here, you just invoked Color with the default color defined in ColorModel.kt. For onColorSelect, you passed an empty lambda since you don’t need it for the preview to work. Thanks to Kotlin, you’re able to pass the second argument as a trailing lambda.

Now, build the project. In the preview panel, you’ll see this:

ColorItem — Preview
ColorItem — Preview

Great work! You’ve completed another composable! :]

Now, you can use this composable to complete the color picker.

Wrapping up the ColorPicker composable

With ColorItem in place, it’s a piece of cake to build ColorPicker().

Add the following code to the top of SaveNoteScreen.kt, just above ColorItem:

@Composable
private fun ColorPicker(
  colors: List<ColorModel>,
  onColorSelect: (ColorModel) -> Unit
) {
  Column(modifier = Modifier.fillMaxWidth()) {
    Text(
      text = "Color picker",
      fontSize = 18.sp,
      fontWeight = FontWeight.Bold,
      modifier = Modifier.padding(8.dp)
    )
    ScrollableColumn(modifier = Modifier.fillMaxWidth()) {
      for (color in colors) {
        ColorItem(color, onColorSelect)
      }
    }
  }
}

As usual, there are a few imports that you need to add as well:

import androidx.compose.foundation.ScrollableColumn
import androidx.compose.foundation.layout.Column
import androidx.compose.ui.text.font.FontWeight

To create the ColorPicker in the code above, you used a Column to align its title and list of colors. You want the user to be able to scroll through the colors, so you used a ScrollableColumn to wrap them.

ColorPicker has two parameters: It takes the list of ColorModels and, like the ColorItem, it exposes the click event parameter.

To visualize what you’ve built so far, add the preview composable to the bottom of SaveNoteScreen.kt:

@Preview
@Composable
fun ColorPickerPreview() {
  ColorPicker(
    colors = listOf(
      ColorModel.DEFAULT,
      ColorModel.DEFAULT,
      ColorModel.DEFAULT
    )
  ) { }
}

Here, you invoked ColorPicker() and passed it a list of default colors. For onColorSelect, you passed an empty lambda, since you’re not interested in interacting with the composable at this stage.

Build the project and check the preview panel to see this:

ColorPicker — Preview
ColorPicker — Preview

Well done! Yet another composable under your belt. :]

You’ll see the color picker in action in Chapter 7, “Managing State in Compose”.

This is where this chapter ends. Hopefully, you now have a feeling for how powerful modifiers are. You can find the final code for this chapter by navigating to 06-using-compose-modifiers/projects/final.

Key points

  • Modifiers tell a UI element how to lay out, display or behave within its parent layout. You can also say that they decorate or add behavior to UI elements.
  • You can chain several modifiers, one after the other, to compose them.
  • The order of modifiers in the chain matters. Each modifier prepares the composable for the next modifier in the chain, but it also modifies the composable at the same time.
  • Avoid hard-coding the values in your composables. Instead, expose those values as properties of the composable function.
  • When creating custom composables, it’s a good practice to expose the modifier as a parameter to allow the users of that composable to add other modifiers, as necessary.

Where to go from here?

Modifiers are a great tool to use when you style your composables. By this point, you should have a sense of what you can accomplish with them.

This chapter didn’t cover all the modifiers that Compose offers since there are a lot of them. The good news is that the principles are the same so you should feel safe using them with the knowledge you’ve gained.

When you play with composables, don’t be afraid to dive deep and research which modifiers you can use on which components. You might be pleasantly surprised. :]

In the next chapter, you’ll learn one of the most important things about Jetpack Compose: how to manage states. When you complete that chapter, Jet Notes will be one step closer to being a fully functional app.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.