Jetpack Compose — Drawing custom shapes (Circle, Rect, Paths)

Using Jetpack Compose alpha08 build. This post will cover how to draw shapes in Jetpack Compose.

Photo by Tetiana SHYSHKINA on Unsplash

Before starting, it’s good to have a grid notebook, something you can just use pen and paper and draw what you want to draw there first. The 0,0 is always in the top left corner. It’s basicly the reverse coordinate system. In a grid of 100x100, the bottom right corner is 100,100. All these examples will use a canvas of 100x100. For each of the example I’m gonna use a little different approach. End result still remains the same.

Circle

The most basic way to draw the circle

@Composable
fun MyCircle(){
Canvas(modifier = Modifier.size(100.dp), onDraw = {
drawCircle(color = Color.Red)
})
}

Drawing Micky Mouse (kinda). May seem a lot, but its just basicly 3 circles. One for head and two for ears. Head at center, left ear at 25x25 and right ear at 75x25.

@Composable
fun MyMouse(){
Canvas(modifier = Modifier.size(100.dp), onDraw = {
drawCircle(Color.Black, radius = 70f, center = Offset(size.width / 2f, size.height / 2f))
drawCircle(Color.Black, radius = 50f, center = Offset(size.width * 0.25f, size.height * 0.25f))
drawCircle(Color.Black, radius = 50f, center = Offset(size.width * 0.75f, size.height * 0.25f))
})
}

Rectangle

In the circle example above you see I use size width/height and calculate based on that. You can use float values directly, but you need to multiply by density. Each phone may have a different pixel density. So 100f x 100f may not be on where you expected it to be once its drawn.

@Composable
fun MyRect(){
val density = AmbientDensity.current.density

val w = remember { 100f * density }
val h = remember { 33f * density }

Canvas(modifier = Modifier.size(100.dp), onDraw = {
drawRect(color = Color.Black, size = Size(w, h), topLeft = Offset.Zero)
drawRect(color = Color.Red, size = Size(w, h), topLeft = Offset(0f, h))
drawRect(color = Color.Yellow, size = Size(w, h), topLeft = Offset(0f, h * 2))
})
}

Paths

Paths allows you to make any custom shape. You can make circles or rectangles if you like. But most of the time you will use Paths to draw triangles, stars, …

fun MyTriangle(){
val density = AmbientDensity.current.density

val path = Path().apply {
moveTo(50f * density, 0f)
lineTo(100f * density, 100f * density)
lineTo(0f, 100f * density)
close()
}

Canvas(modifier = Modifier.size(100.dp), onDraw = {
drawPath(path, Color.Blue)
})
}

Star trek logo

This one has a little curve at the bottom and a border. To achive this I used Surface composable and applied the shape on it. The result is pretty nice.

@Composable
fun MyStarTrek(){
val myStarTrekShape = GenericShape { size ->
moveTo(size.width / 2f, 0f)
lineTo(size.width, size.height)
quadraticBezierTo(
size.width * 0.6f,
size.height * 0.4f,
0f,
size.height
)
close()
}

Surface(
shape = myStarTrekShape,
color = Color.Yellow,
border = BorderStroke(3.dp, Color.Black),
modifier = Modifier.size(100.dp)
) { }
}

developer