Drawing Basic Shapes in Jetpack Compose: A Comprehensive Guide

Jetpack Compose, Android’s modern UI toolkit, provides a powerful and intuitive way to create user interfaces. A key aspect of UI design is the ability to draw shapes directly within your app. Jetpack Compose offers a variety of tools and APIs for drawing basic shapes, allowing you to create custom visuals and enhance your app’s aesthetic appeal.

Why Draw Shapes in Jetpack Compose?

  • Custom UI Elements: Create unique buttons, backgrounds, and decorative elements.
  • Visual Appeal: Add visual interest and improve user engagement.
  • Flexibility: Design exactly what you need without relying on predefined components.

Drawing Basic Shapes

Jetpack Compose makes drawing shapes straightforward using the Canvas composable and the draw* functions provided by the DrawScope.

Setting up the Canvas

First, you need to set up a Canvas composable where you’ll perform the drawing operations. Here’s how you can do it:


import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp

@Composable
fun DrawingCanvas() {
    Canvas(modifier = Modifier.size(200.dp)) {
        // Drawing operations will go here
    }
}

@Preview(showBackground = true)
@Composable
fun PreviewDrawingCanvas() {
    DrawingCanvas()
}

In this example, we create a Canvas with a fixed size of 200×200 dp. Inside the lambda of the Canvas, we can use the DrawScope to draw shapes.

Drawing a Line

To draw a line, use the drawLine function:


import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp

@Composable
fun DrawingCanvas() {
    Canvas(modifier = Modifier.size(200.dp)) {
        drawLine(
            color = Color.Red,
            start = Offset(10f, 10f),
            end = Offset(190f, 190f),
            strokeWidth = 5f,
            cap = StrokeCap.Round
        )
    }
}

@Preview(showBackground = true)
@Composable
fun PreviewDrawingCanvas() {
    DrawingCanvas()
}

Explanation:

  • color: Specifies the color of the line (Color.Red in this case).
  • start: An Offset representing the starting point of the line.
  • end: An Offset representing the ending point of the line.
  • strokeWidth: The thickness of the line.
  • cap: The style of the line endings (StrokeCap.Round for rounded ends).

Drawing a Rectangle

To draw a rectangle, use the drawRect function:


import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.Fill
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp

@Composable
fun DrawingCanvas() {
    Canvas(modifier = Modifier.size(200.dp)) {
        drawRect(
            color = Color.Blue,
            topLeft = Offset(10f, 10f),
            size = Size(180f, 100f),
            style = Fill
        )
    }
}

@Preview(showBackground = true)
@Composable
fun PreviewDrawingCanvas() {
    DrawingCanvas()
}

Explanation:

  • color: Specifies the color of the rectangle (Color.Blue).
  • topLeft: An Offset representing the top-left corner of the rectangle.
  • size: A Size object representing the width and height of the rectangle.
  • style: Determines whether the rectangle is filled (Fill) or stroked (Stroke).

For a stroked rectangle:


import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp

@Composable
fun DrawingCanvas() {
    Canvas(modifier = Modifier.size(200.dp)) {
        drawRect(
            color = Color.Green,
            topLeft = Offset(10f, 10f),
            size = Size(180f, 100f),
            style = Stroke(width = 5f)
        )
    }
}

@Preview(showBackground = true)
@Composable
fun PreviewDrawingCanvas() {
    DrawingCanvas()
}

Drawing a Circle

To draw a circle, use the drawCircle function:


import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.Fill
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp

@Composable
fun DrawingCanvas() {
    Canvas(modifier = Modifier.size(200.dp)) {
        drawCircle(
            color = Color.Magenta,
            center = Offset(size.width / 2, size.height / 2),
            radius = 90f,
            style = Fill
        )
    }
}

@Preview(showBackground = true)
@Composable
fun PreviewDrawingCanvas() {
    DrawingCanvas()
}

Explanation:

  • color: Specifies the color of the circle (Color.Magenta).
  • center: An Offset representing the center point of the circle. We use size.width / 2 and size.height / 2 to center the circle within the canvas.
  • radius: The radius of the circle.
  • style: Determines whether the circle is filled (Fill) or stroked.

For a stroked circle:


import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp

@Composable
fun DrawingCanvas() {
    Canvas(modifier = Modifier.size(200.dp)) {
        drawCircle(
            color = Color.Cyan,
            center = Offset(size.width / 2, size.height / 2),
            radius = 90f,
            style = Stroke(width = 5f)
        )
    }
}

@Preview(showBackground = true)
@Composable
fun PreviewDrawingCanvas() {
    DrawingCanvas()
}

Drawing an Arc

To draw an arc, use the drawArc function:


import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp

@Composable
fun DrawingCanvas() {
    Canvas(modifier = Modifier.size(200.dp)) {
        drawArc(
            color = Color.Yellow,
            startAngle = 0f,
            sweepAngle = 270f,
            useCenter = false,
            topLeft = Offset(10f, 10f),
            size = Size(180f, 180f),
            style = Stroke(width = 5f)
        )
    }
}

@Preview(showBackground = true)
@Composable
fun PreviewDrawingCanvas() {
    DrawingCanvas()
}

Explanation:

  • color: Specifies the color of the arc (Color.Yellow).
  • startAngle: The angle (in degrees) at which the arc starts.
  • sweepAngle: The angle (in degrees) that the arc sweeps.
  • useCenter: If true, the arc is drawn as a pie slice; if false, it’s drawn as an open arc.
  • topLeft: The top-left corner of the bounding box for the oval in which the arc is inscribed.
  • size: The size of the bounding box.
  • style: Whether to fill or stroke the arc.

Customizing Shapes

You can customize the shapes further by using different colors, styles, stroke widths, and effects. For example, adding a shadow or gradient fill.

Conclusion

Drawing basic shapes in Jetpack Compose is a fundamental skill that unlocks a wide range of possibilities for creating custom UIs and visually appealing applications. With the Canvas composable and the draw* functions, you can easily draw lines, rectangles, circles, arcs, and more. Experiment with different colors, styles, and parameters to create unique and engaging user interfaces.