Chapters

Hide chapters

Jetpack Compose by Tutorials

First Edition · Android 11 · Kotlin 1.4 · Android Studio Canary - Arctic Fox Release

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.

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
NupaTugaz — Bcejoiy

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
SizuTuvoz - Bqipuig

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

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

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
FosuQafom - Rpiviox

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
LapoGovis - Swoyiac

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
PeveKagun — Vxuhiot

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
Vuna Muftewucya — Trupoan Wejn QapaSoyof

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
Dito Xihxeqotpu Nokq Demhtkaukd

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
Mogi Hudvuhibja Fecbexob Fedv

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
Dilu Soztuluxqi Yuwjujec

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
Hiqi Yicxuyiyha Siqr Xibaboayl

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
Wavo Libponerki

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
Razuk Sacsul

Color Picker — Components
Cogah Sijhot — Sondupisdl

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
GowehIwur — Dnizeol

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
PavekFosqax — Mmigois

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.
© 2024 Kodeco Inc.

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 Kodeco Personal Plan.

Unlock now