Skip to content

Intermediate: Android basics · Jetpack Compose UI · topic 22 of 50 · 30 min

Composables and modifiers

Explanation

Screens are built from small composable functions such as Text, Button, Image and Icon. A Modifier is a chain of instructions that changes how a composable looks or behaves: size, padding, background, click handling. The order of modifiers matters, because each one wraps the next. Use dp for sizes and sp for text.

Building blocks

A Compose screen is a tree of composables. You combine small built-in ones into your own, the same way you combine words into sentences. Here are the ones you will use every day:

kotlin
@Composable
fun ProductRow() {
    Row(verticalAlignment = Alignment.CenterVertically) {
        Image(
            painter = painterResource(R.drawable.tiffin),
            contentDescription = "Steel tiffin box",
            modifier = Modifier.size(64.dp),
        )
        Column(Modifier.padding(start = 12.dp)) {
            Text("Steel tiffin box", style = MaterialTheme.typography.titleMedium)
            Text("Rs 349", color = MaterialTheme.colorScheme.primary)
        }
        Spacer(Modifier.weight(1f))
        IconButton(onClick = { /* add to cart */ }) {
            Icon(Icons.Default.Add, contentDescription = "Add to cart")
        }
    }
}
Text, Image, Icon and IconButton inside a Row and Column. Layouts are covered in topic 3.
ComposableShows
TextText, with a style from the theme
Button / OutlinedButton / TextButtonClickable buttons with an onClick lambda
ImageA picture from resources (painterResource) or loaded from the internet with a library such as Coil
Icon / IconButtonMaterial icons, optionally clickable
SpacerEmpty space

Modifiers

Almost every composable has a modifier parameter. A Modifier is built by chaining functions: Modifier.fillMaxWidth().padding(16.dp).background(Color.White). Each function adds one decoration or behaviour. Common ones: size, width, height, fillMaxWidth, fillMaxSize, padding, background, border, clip (round the corners), clickable, and weight (inside Row/Column).

Order matters

kotlin
// Padding OUTSIDE the background: the coloured box is smaller, with a gap around it
Text("A", Modifier.padding(16.dp).background(Color.Yellow))

// Padding INSIDE the background: the coloured box is bigger, with space around the text
Text("B", Modifier.background(Color.Yellow).padding(16.dp))

// Clickable area: only the inner area responds here
Text("C", Modifier.padding(16.dp).clickable { })
Read a modifier chain from left to right, as layers wrapped around the content from the outside in.
Think of it like this: Modifiers are like wrapping a gift. If you first add a gap (padding) and then wrapping paper (background), the paper is tight around the box and the gap is outside. If you wrap first and then add the gap, the gap is inside the paper. Same steps, different order, different result.

By convention, every reusable composable you write takes modifier: Modifier = Modifier as a parameter and passes it to its outermost element. The caller can then add padding or size from outside without you changing the composable, exactly like the built-in ones.

dp and sp

Phones have very different pixel densities. dp (density-independent pixels) are a unit that looks about the same physical size on every screen; use it for sizes, padding and spacing. sp (scale-independent pixels) are like dp but also grow when the user increases the font size in system settings; use it for text sizes. Never use raw pixels for layout.

Content descriptions

contentDescription describes an image or icon for screen readers such as TalkBack, which blind and low-vision users rely on. Describe meaningful images ("Add to cart"); pass null for purely decorative ones. This is also checked by Play's pre-launch accessibility report.

Common mistakes

  • Getting modifier order wrong, for example clickable before padding when you wanted the whole padded area clickable. Put clickable first in that case.
  • Hard-coding colours like Color.Red everywhere. Use MaterialTheme.colorScheme so dark mode and branding work (module 9).
  • Using dp for text size. Use sp so text follows the user's font setting.
  • Leaving out contentDescription on icon buttons, which makes them unusable with a screen reader.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Composables and modifiers · 1/6

Text | Button | Image | Icon

Screens are built from small composables: Text, Button, Image and Icon.

Prefer a video by a teacher? Find free videos on “jetpack compose modifiers order text image button tutorial” on YouTube ↗

In real life: A product card on a shopping app

An online saree shop shows each product as a card: image, name, price and a heart button. The card is one composable, ProductCard, used in the home feed, search results and wishlist. A change to the card (say, showing a discount badge) appears everywhere at once, and modifiers give it consistent padding and rounded corners.

kotlin
@Composable
fun PriceTag(price: Int, modifier: Modifier = Modifier) {
    Text(
        text = "Rs $price",
        fontSize = 18.sp,
        modifier = modifier
            .clip(RoundedCornerShape(8.dp))
            .background(MaterialTheme.colorScheme.primaryContainer)
            .padding(horizontal = 12.dp, vertical = 4.dp),
    )
}
Clip, then background, then padding: a rounded coloured pill with space around the text.

Practice (0/3 done)

Task 1 · Question

Which modifier chain makes the whole padded area clickable?

Task 2 · Question

Which unit should a text size use?

Task 3 · Practice

Build a ContactRow composable: a round avatar (an Icon in a Box clipped with CircleShape and a background), the name and phone number in a Column, and a call IconButton at the end. Make two previews and try swapping the order of padding and background to see the difference.

Quick quiz

Question 1

1.What is a Modifier?

Question 2

2.What does dp stand for and why use it?

Question 3

3.What should contentDescription be for a purely decorative image?

Question 4

4.How do you read a modifier chain?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What is a Modifier?

Answer: A chain of instructions that changes a composable's look or behaviour

Modifiers decorate and add behaviour.

What does dp stand for and why use it?

Answer: Density-independent pixels; sizes look similar on all screens

dp scales with screen density.

What should contentDescription be for a purely decorative image?

Answer: null

null tells screen readers to skip it.

How do you read a modifier chain?

Answer: Left to right, outside in

Each modifier wraps what follows it.

Which window width class do most phones in portrait have?Related

Answer: Compact

Phones in portrait are Compact.

From Accessibility and adaptive layouts

Can you call a @Composable function inside a click handler?Related

Answer: No; change state and let the UI recompose

Composables can only be called from other composables.

From Hello Compose: your first screen

Why test stateless "Content" composables directly?Related

Answer: You can show any state without a real ViewModel, server or database

Easy setup of exact states.

From Compose UI tests

Where can Modifier.weight be used?Related

Answer: Inside Row and Column

weight belongs to RowScope and ColumnScope.

From Layouts: Column, Row, Box and Scaffold

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 4 more topics in “Jetpack Compose UI” before its exam: State and recomposition, Layouts: Column, Row, Box and Scaffold, Lists and grids, Text input and forms.
  2. Module 6 exam: Jetpack Compose UI · 13 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Navigation and app architecture. 50 topics left in the course before the final exam.

← Previous: Hello Compose: your first screen