More on Canvas
Tags: kotlin, ux, jetpackcompose, android
Published: 2021-01-15 15:50:24

In the previous posts of this series we have taken a closer look at *how* to use shapes and canvases. In this installment I will be focussing on *when* to draw something. Or *why*. 

Sounds weird? Hang on.

Composables are the building blocks of our user interface. We can either use them *as is* or amend them with prebuilt functionaly. First, *as is*:

```kotlin
Text("Hello Compose")
```

![A simple text](https://dev-to-uploads.s3.amazonaws.com/i/75vjsolirio3ipe89kve.png)

To customize both look and behavior of a composable we use `modifier`:

```kotlin
Text(
  modifier = Modifier.border(
    1.dp,
    MaterialTheme.colors.primary
  )
    .padding(8.dp)
    .shadow(4.dp, shape = CircleShape, clip = true),
  text = "Hello Compose"
)
```

![A modified composable](https://dev-to-uploads.s3.amazonaws.com/i/2gqw38h5mwunoak5252y.png)

As you can see, we have amended, modified, our composable quite a bit by adding a border, a shadow and a padding. There are a lot of prebuilt modifiers. But what if none of these suits your needs? Just roll your own. As you will see shortly, this is very easy with Compose. But let's take a look at the inner workings first.

A modifier is an ordered, immutable collection of **modifier elements**. `androidx.compose.ui.Modifier` is an interface, that helps you define such a collection. `then()` returns a `Modifier` representing the *current* modifier followed by another one in sequence. There is also a companion object that implements this interface. It may be used as the start of a modifier extension factory expression. We'll see soon what this means. For now, please take a look at `padding()`:

![The padding() modifier](https://dev-to-uploads.s3.amazonaws.com/i/17ekffavvvtcr4e1atl6.png)

As you can see, `then()` receives a `PaddingModifier` (a private class inside `androidx.compose.foundation.layout.Padding.kt`). Since this affects more layout than drawing I'll not elaborate on this. What's important: `this` is another implementation of the `Modifier` interface. Which one, depends on what other extension functions have been called before. In my example `border()` is invoked first. Now let's tackle this *modifier extension factory expression* thing. Here's how some lines of `border()` look like.

![The border() modifier (beginning)](https://dev-to-uploads.s3.amazonaws.com/i/kh7pr8zayfz9csswcmjg.png)

![The border() modifier (end)](https://dev-to-uploads.s3.amazonaws.com/i/p2ubpjy1yo6ayv7110ec.png)

It returns the result of the `composed()` modifier, which receives a `factory` and a `inspectorInfo`.

![The composed() modifier](https://dev-to-uploads.s3.amazonaws.com/i/90w3int1ms1bzc99idi1.png)

The factory is both a composable and an extension function of `Modifier`. It is wrapped in a `ComposedModifier` which in turn is passed to `then()`. `ComposedModifier` is a private class that extends abstract `InspectorValueInfo` and implements `Modifier.Element`, a single element contained within a `Modifier` chain. But when do you use `then()`? And when is a factory preferrable? Fortunately, most of the time you won't need to deal with these questions. That's because there are a few `Modifier` extension functions that allow us to draw: `drawWithContent()`, `drawBehind()` and `drawWithCache()`.

```kotlin
fun Modifier.drawOnYellow() = this.drawBehind {
  drawRect(Color.Yellow)
}
```

This modifier draws a yellow rectangle behind the composable that is amended with it.

```kotlin
Text(
  modifier = Modifier.drawOnYellow(),
  text = "Hello Compose"
)
```

![Text() with a yellow background](https://dev-to-uploads.s3.amazonaws.com/i/oh3q9ffoul5y424vni7z.png)

Your lambda receives an instance of `androidx.compose.ui.graphics.drawscope.DrawScope`. This interface defines

> a scoped drawing environment with the provided `Canvas`.
> This provides a declarative, stateless API to draw shapes
> and paths without requiring consumers to maintain 
> underlying `Canvas` state information.

Next.

`drawWithContent()` allows us to draw before or after the layout's contents.

```kotlin
fun Modifier.drawRedCross() = this.drawWithContent {
  drawContent()
  drawLine(
    Color.Red, Offset(0f, 0f),
    Offset(size.width - 1, size.height - 1),
    blendMode = BlendMode.SrcAtop,
    strokeWidth = 8f
  )
  drawLine(
    Color.Red, Offset(0f, size.height - 1),
    Offset(size.width - 1, 0f),
    blendMode = BlendMode.SrcAtop,
    strokeWidth = 8f
  )
}
```

```kotlin
Text(
  modifier = Modifier.drawRedCross(),
  text = "Imperative"
)
```

![Using drawWithContent()](https://dev-to-uploads.s3.amazonaws.com/i/5r371dma45fhxtrzqx2w.png)

The location of `drawContent()` in your lambda defines *when* the content is drawn. Finally, `drawWithCache()` draws into

> a `DrawScope` with content that is persisted across
> draw calls as long as the size of the drawing area is 
> the same or any state objects that are read have not
> changed. In the event that the drawing area changes, or
> the underlying state values that are being read
> change, this method is invoked again to recreate objects
> to be used during drawing

As you have seen in this post, it is very easy to write extension functions that can modify your composables by drawing on a canvas. Are there other topics related to `Canvas` you would like me to cover? Please share your thoughts in the comments.