Custom Composable with Jetpack Compose
Tags: android, compose, custom, canvas
Published: 2022-07-05 09:47:00

Jetpack Compose already offers us a huge library of components to create our UI. But in order to create a more delight and custom experience to the user, we sometimes need to create our own.

In my first article series I want to talk about how to create custom views in Jetpack Compose.

On the example of a Labeled Ranged Slider we go though the steps needed to create a fully custom Composable. I chose this example because the current version of the Range Slider in Compose only offers a Float Range and no easy way to display step aligned labels, changing its appears in accordance to the selected range.

The result of this series will look like this
![Labeled Range Slider in motion](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/juu2sa2uyqkoobdsyu3r.gif)

We will look at three different topics used to create the Composable and in the end we put it all together.

- How to draw on a Canvas (This part)
- [Handling touch interaction](https://dev.to/lex_fury/touch-interactions-in-jetpack-compose-5be9)
- [Kotlin Context Receivers](https://dev.to/lex_fury/context-receivers-39p5) (because it’s new and fun :-D)
- [Draw the Labeled Range Slider](https://dev.to/lex_fury/make-the-labeled-range-slider-interactive-5gf)
- [Interact with the Labeled Range slider](https://dev.to/lex_fury/make-the-labeled-range-slider-interactive-5gf)

All the code and projects will be published on GitHub as we go along.

## How to draw on a Canvas

Let’s start with looking at how to draw something on a Canvas within a Composable.

As a first simple step we draw a basic shape, like a circle. To do this we

Create our Composable

- Give it a minimum size
- Add a Canvas
- Draw the circle

```
@Composable
fun DrawSimpleCircle(
   color: Color,
   modifier: Modifier = Modifier,
   minSize: DpSize = DpSize(32.dp, 32.dp)
) {
   Canvas(modifier = modifier.size(minSize)) {
      drawCircle(color)
   }
}
```

The result of this Composable will be a circle in the given color, filling the size of the whole Composable, e.g.


![A simple green circle](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xijaymo7r0t7wm8ybx3u.png)

Additionally this API allows us to draw our shapes not only with a single color, but also offers a more flexible Brush variant. With a Brush we are able to achieve gradients as well. The example shows a gradient with two colors, but the API allows for a variable list of colors, if desired.

```
...
drawCircle(
   brush = Brush.radialGradient(
      colors = listOf(
         centerColor, outerColor
      )
   )
)
```

![A circle with radial gradient](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3zk67oiic4e34muscrai.png)

A Canvas in Compose can of course not only draw circles. It offers a multitude of different drawing methods. Here are examples of the offers methods and a what it might look like.

**drawRect**

![A simple rect](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/308x62i716xnqdou9m1a.png)


**drawRoundRect**


![Rect with rounded corners](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p1xr1b72tbtflx5ewwwr.png)


**drawLine**


![A simple line](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wnooa0wca4oafphm5xao.png)

**drawOval**


![A simple oval](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qkgb2v9migvzyskmhjbs.png)


**drawArc**


![An 180° arc](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q1i38piyt1iv8v9644gk.png)

```
...
drawArc(
   color = color,
   startAngle = 180f,  // 0f is at 3 o'clock of the circle
   sweepAngle = 180f,  // go 180° from startAngle
   useCenter = false,  // whether to fill the arc or not
   style = Stroke(width = 2.dp.toPx())
)
```

**drawPath**


![An hour glass drawn with a Path](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pfqfqdfsgald929a2l4e.png)

```
...
val path = Path().apply {
   lineTo(size.width, 0f)   // line from start to end
   quadraticBezierTo(       // arc from the top to  bottom
      size.width / 2, size.height / 2,
      size.width, size.height
   )
   lineTo(0f, size.height)  // line from end to start
   quadraticBezierTo(       // arc from bottom to top 
      size.width / 2, size.height / 2,
      0f, 0f
   )
   close()                   // link end to start
}

drawPath(
   color = color,
   path = path,
   style = Stroke(width = 2.dp.toPx())
)
```

**drawPoints**


![Five dots](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8hyzszkb87noohfcbjo3.png)

**drawOutline**


![An outlined rect](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gaax8dodtys6o0sc971l.png)

**drawImage**

![Showing an image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/682qh9ajr9uhl7slr2zz.png)

```
...
drawImage(
   image = image,
   dstSize = IntSize(size.width.toInt(), size.width.toInt()),
   colorFilter = ColorFilter.lighting(Color.Yellow, Color.Red)
)
```

## Native Canvas

One useful draw function that is not offered directly within the Canvas Composable is the ability to draw a text. But we can still achieve this by using the drawIntoCanvas method. It gives us access to the native Canvas, which offers a drawText method.

Important to note: When using the native Canvas you have to resort to the android.graphics package, using its Paint or Color classes instead of the Compose variants.

```
Canvas(modifier = modifier.size(minSize)) {
   val paint = android.graphics.Paint()
   paint.textSize = textSize.toPx()
   paint.color = android.graphics.Color.BLACK
   drawIntoCanvas {
      it.nativeCanvas.drawText(text, 0f, 0f, paint)
   }
}
```

## Conclusion
With the Canvas and the native Canvas we are able to draw everything we need for our Labeled Range Slider.

![How our final result will look like](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/welrivcekwkivryynr0c.png)

In the next part we will take a look at how we can make our custom Composable interactive, by reacting on touch events. [Go directly to part 2](https://dev.to/lex_fury/touch-interactions-in-jetpack-compose-5be9).

The code for creating the images in this post can be found on [GitHub](https://github.com/a-frank/draw-canvas-composable).