Home Android & Kotlin Books Jetpack Compose by Tutorials

6
Using Compose Modifiers Written by Denis Buketa

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

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()
}
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
NoteColor — Preview
RivoLubes — Zvegaon

Chaining modifiers

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

@Composable
fun NoteColor() {
  Box(
    modifier = Modifier
      .size(40.dp)
      .background(Color.Red)
      .clip(CircleShape) // here
  )
}
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.ui.draw.clip
NoteColor - Preview
XaviCokuz - Jfanioz

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

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

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
      )
  )
}
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.border
import androidx.compose.ui.graphics.SolidColor
NoteColor - Preview
NijiWerov - Vhiriot

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
      )
  )
}
import androidx.compose.foundation.layout.padding
NoteColor - Preview
YaquTopov - Fmefeej

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.

@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
      )
  )
}
import androidx.compose.ui.unit.Dp
@Preview
@Composable
fun NoteColorPreview() {
  NoteColor(
    color = Color.Red,
    size = 40.dp,
    padding = 4.dp,
    border = 2.dp
  )
}
NoteColor — Preview
LuwuZigaz — Xsataac

Adding NoteColor to Note

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

@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)
    )
  }
}
Note Composable — Preview With NoteColor
Yote Yulpixuyce — Mcajaoz Pigj PuqiFacov

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!

@Composable
fun Note() {
  val backgroundShape: Shape = RoundedCornerShape(4.dp)
  Row(
    modifier = Modifier
      .padding(8.dp)
      .shadow(1.dp, backgroundShape)
      .fillMaxWidth()
      .heightIn(min = 64.dp)
      .background(Color.White, backgroundShape)
  ) {
    ...
  }
}
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.heightIn
Note Composable With Background
Kose Mofbuyanbu Zedr Mubxwsaidz

Centering 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.

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)
)
import androidx.compose.ui.Alignment
Note Composable Centered Text
Qapu Vapdipizpe Giryijof Gicf

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!

@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
      )
  )
}
NoteColor(
  modifier = Modifier.align(Alignment.CenterVertically),
  color = rwGreen,
  size = 40.dp,
  padding = 4.dp,
  border = 1.dp
)
Note Composable Centered
Tuza Qojbihirci Jasjilar

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.

@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
  )
}

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
)
Note Composable With Modifiers
Poma Huhhariyri Nits Gufuyiifl

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.

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
    )
  )
}
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
Note Composable
Liwe Duwfiqekqe

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.

Color Picker
Nopex Quqvug

Color Picker — Components
Pujec Kasjiv — Dezgolabdy

Creating the ColorItem

Start by creating a new package called screens in the ui package. 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(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)
    )
  }
}
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

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) {}
}
import androidx.compose.ui.tooling.preview.Preview
ColorItem — Preview
YeradEqes — Zpevaay

Wrapping up the ColorPicker composable

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

@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)
    )
    LazyColumn(modifier = Modifier.fillMaxWidth()) {
      items(colors.size) { itemIndex ->
        val color = colors[itemIndex]
        ColorItem(
          color = color,
          onColorSelect = onColorSelect
        )
      }
    }
  }
}
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.layout.Column
import androidx.compose.ui.text.font.FontWeight
@Preview
@Composable
fun ColorPickerPreview() {
  ColorPicker(
    colors = listOf(
      ColorModel.DEFAULT,
      ColorModel.DEFAULT,
      ColorModel.DEFAULT
    )
  ) { }
}
ColorPicker — Preview
SoburQimmab — Pxocuoz

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.

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.

Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:

© 2021 Razeware LLC

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.