Jetpack Compose Canvas (with Examples)

Jetpack Compose Canvas

Jetpack Compose offers several APIs like Button, Switch, etc. But what if you want to create a custom UI? That’s where Canvas comes in handy. In this article, we’ll learn how to draw custom graphics in Jetpack Compose using the Canvas API.

Prerequisites:

What is Canvas?

Canvas is used to create custom UI components. You can design any kind of UI you want. Look at these awesome progress bars. Most of them are made with Canvas API.

Before jumping into Canvas, we need to understand the coordinate system in Android.

Canvas Coordinate system

The coordinate system starts at the top left corner. The corner represents the (0,0) point. The X values increase towards the right and Y values increase downwards.

Everything we draw on the Canvas is relative to (0,0) point.

For this article, create an empty Jetpack Compose project and open MainActivity.kt. Create a composable called MyUI() and call it from the onCreate(). We’ll write our code in it.

// add the following packages
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.unit.dp

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            YourProjectNameTheme(darkTheme = false) {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    Column(
                        modifier = Modifier.fillMaxSize(),
                        verticalArrangement = Arrangement.Center,
                        horizontalAlignment = Alignment.CenterHorizontally
                    ) {
                        MyUI()
                    }
                }
            }
        }
    }
}

@Composable
fun MyUI() {
   
}

How to Use Canvas in Jetpack Compose?

Jetpack Compose offers Canvas API. It takes two parameters.

@Composable
fun Canvas(modifier: Modifier, onDraw: DrawScope.() -> Unit)

The modifier is used for adding styles (like border) and onDraw is a lambda that will be called when we are drawing. Let’s create a Canvas of size 300.dp.

@Composable
fun MyUI() {
    Canvas(
        modifier = Modifier
            .size(size = 300.dp)
            .border(width = 2.dp, color = Color.Magenta, shape = RectangleShape)
    ) {

    }
}

It looks like this:

Graphics

Related: Shape APIs in Jetpack Compose

Let’s specify the coordinates at the corners.

Coordinates corners

Let’s draw a line vertically. To draw lines, there is a drawLine() function.

@Composable
fun MyUI() {
    Canvas(
        modifier = Modifier
            .size(size = 300.dp)
            .border(width = 2.dp, color = Color.Magenta, shape = RectangleShape)
    ) {
        drawLine(
            color = Color.Blue,
            strokeWidth = 2.dp.toPx(),
            start = Offset(x = size.width/2, y = 0f),
            end = Offset(x = size.width/2, y = size.height)
        )
    }
}

Output:

Canvas draw line

Let’s understand the code.

drawLine() takes 4 parameters:

color – line color.

strokeWidth – width of the line.

start – first point of the line.

end – last point of the line.

We have specified the first and last points using the Offset() function. The function takes two parameters – x and y. x represents the x coordinate and y represents the y coordinate.

Our line starts at the top center and ends at the bottom center. The coordinates look like this:

line start end positions

We can get the Canvas size from the size property. size.height returns the height and size.width returns the width.

The start position is at the center of the x – axis. So, the x should be 150.dp (size.width/2), and the y should be 0.dp.

The end position is at the bottom center. So, the x should be 150.dp (size.width/2), and the y should be 300.dp (size.height).

Similar to drawLine(), there are other drawing functions:

Note: You can also draw some basic shapes using the box with round corner shape API.

Jetpack Compose Draw Behind:

Jetpack Compose offers drawBehind modifier. It helps us to draw shapes behind other composables.

Example:

@Composable
fun MyUI() {
    Box(modifier = Modifier
        .size(size = 300.dp)
        .border(width = 2.dp, color = Color.Magenta, shape = RectangleShape)
        .drawBehind {
            // draw here
            drawLine(
                color = Color.Blue,
                strokeWidth = 2.dp.toPx(),
                start = Offset(x = size.width / 2, y = 0f),
                end = Offset(x = size.width / 2, y = size.height)
            )
        }
    )
}

Output:

Canvas draw line

When working with Canvas, you need to remember 2 things:

1. If the element’s size is bigger than the canvas’s, it goes outside the canvas.

@Composable
fun MyUI() {
    Canvas(
        modifier = Modifier
            .size(size = 100.dp)
            .border(width = 2.dp, color = Color.Magenta)
    ) {
        drawCircle(
            color = Color.Green.copy(alpha = 0.1f),
            radius = 110.dp.toPx() // bigger than Canvas
        )
    }
}

Output:

Jetpack Compose Canvas boundaries

The circle is crossing the Canvas.

2. Whenever you want to set the size, use dp.toPx() instead of float values. It ensures that your UI is consistent across all the screen densities.

This is all about Canvas in Jetpack Compose. I hope you have learned something new. If you have any doubts, comment below.

For more information, look at the Android Docs.

Related Articles:

Leave a Comment