Custom Shapes

Every shape we've used so far — a Card's rounded corners, a chip's pill outline — has come from a small, ready-made set. Compose exposes the underlying Shape interface directly too, so any composable's silhouette can be clipped to something Material doesn't ship out of the box. This closing lesson of the UI Components part covers the built-in shapes plus GenericShape for fully custom outlines, applied to a small category badge on the RecipeBox recipe card.

The Built-In Shapes

ShapeProduces
RectangleShapeSharp, unrounded rectangle — the default for most containers
RoundedCornerShape(radius)Rectangle with rounded corners; also accepts a different radius per corner
CircleShapeA perfect circle (or ellipse, on a non-square component)
CutCornerShape(size)Corners cut diagonally instead of rounded, for an angular look

Modifier.clip: Applying a Shape

Any composable can be clipped to a shape with Modifier.clip(shape) — this is what turned the square AsyncImage thumbnail in the Cards lesson into a rounded-corner image, and the same modifier makes a perfectly circular avatar just as easily:

Box(
    modifier = Modifier
        .size(40.dp)
        .clip(CircleShape)
        .background(MaterialTheme.colorScheme.primaryContainer)
)

clip only affects drawing and hit-testing at the shape's boundary — it doesn't change layout size, so combine it with Modifier.background(color, shape) (a single call that both fills and clips together) when a shape needs a solid fill, as shown next.

Worked Example: A Custom Category Badge Shape

GenericShape builds an arbitrary outline by drawing a Path yourself, given the composable's actual pixel Size at draw time. Here's a small pentagon-ish "ribbon" badge shape for a recipe card's category label:

val RibbonBadgeShape = GenericShape { size, _ ->
    val notch = size.height / 2f
    moveTo(0f, 0f)
    lineTo(size.width - notch, 0f)
    lineTo(size.width, notch)
    lineTo(size.width - notch, size.height)
    lineTo(0f, size.height)
    close()
}

The lambda's second parameter (ignored here with _) is a LayoutDirection, useful if a shape needs to mirror for right-to-left locales; the first is the composable's Size, letting the shape adapt to whatever width the badge ends up with rather than hardcoding pixel values.

Applying the Badge to a Recipe's Category

Dropped onto a small colored box, the shape becomes an arrow-notched category badge that can sit in the corner of the RecipeCard built two lessons ago:

@Composable
fun CategoryBadge(category: RecipeCategory) {
    Box(
        contentAlignment = Alignment.Center,
        modifier = Modifier
            .clip(RibbonBadgeShape)
            .background(category.color)
            .padding(horizontal = 12.dp, vertical = 4.dp)
    ) {
        Text(
            text = category.label,
            color = Color.White,
            style = MaterialTheme.typography.labelSmall
        )
    }
}

Because Modifier.clip and Modifier.background are ordinary modifiers, CategoryBadge composes into the corner of a RecipeCard's Row exactly like any other small composable — no special integration needed beyond picking where in the row to place it.

Note: This is correct, real Compose/Kotlin code — but this website's own project doesn't run a live Android Studio build, so you won't see it actually render on this page.
Next up: MaterialTheme & Color Schemes starts the Theming part of this track — where category.color above would actually come from in a real app, rather than a hardcoded value.