Cartographing Jetpack Compose: foundation
Tags: android, jetpackcompose, kotlin
Published: 2021-05-29 12:43:55

Welcome to the second installment of *Cartographing Jetpack Compose*. In the first part we looked at `androidx.compose.compiler` and `androidx.compose.runtime`. Both provide essential groundwork and keep the compose machinery working, for example by modifying or amending composable functions during compile time, and by bringing on screen what has been emitted by a composable during runtime.

This time we will be looking at `androidx.compose.foundation`. The docs describe is like this:

> Write Jetpack Compose applications with ready to use building
> blocks and extend foundation to build your own design system
> pieces.

As of May 2021 we see quite a few subpackages: *layout*, *shape*, *gestures*, *selection*, *lazy*, *interaction* and *text*. Before we turn to them in the next episode, let's look at the base package, `androidx.compose.foundation`. We have a few interfaces, `Indication` and `IndicationInstance`. They deal with visual effects that occur when certain interactions happens. Both sort of accompany a Top-level property called `LocalIndication`, a `CompositionLocal` that provides an Indication through the hierarchy.

We also see a few classes, for example `BorderStroke` (to specify the stroke to draw borders with) and `ScrollState` (state of the scroll). Here's how to use them.

```kotlin
@Composable
@Preview
fun Sandbox() {
  Text(
    modifier = Modifier
      .border(
        BorderStroke(4.dp, Color.Red)
      )
      .padding(8.dp),
    text = "Hello Compose"
  )
}
```

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

As you can see I use `BorderStroke` to create a simple border around a `Text()`. Please be aware that `BorderStroke` is both a class and a Top-level function. 

Next: `ScrollState`. The docs say:

> Create and `remember` the `ScrollState` based on the currently
> appropriate scroll configuration to allow changing scroll
> position or observing scroll behavior.

```kotlin
@Composable
@Preview
fun Sandbox() {
  val state = rememberScrollState()
  val text = "${(1..100).joinToString("") { "${it}\n" }}- end -"
  Text(
    text = text,
    textAlign = TextAlign.Center,
    modifier = Modifier
      .fillMaxSize()
      .verticalScroll(state)
  )
}
```

![App showing a scrollable text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qifqtdy74f67ixxuwjzd.png)

My composable first creates a string consisting of 100 lines with ascending numbers followed by a final line `- end -`. This string is passed to `Text()`. Spot the `modifier`. The text wants all available size (`fillMaxSize()`). Also, `verticalScroll()` makes it scrollable vertically. The docs say:

> Modify element to allow to scroll vertically when height of the
> content is bigger than max constraints allow.

Naturally, `verticalScroll()` is an extension function to `Modifier`. It receives the state of the scroll, which is created using `rememberScrollState()`.

Now let's look at the Top-level functions. They include very important basic composables, for example `Image()` and `Canvas()`.

### Displaying images

Displaying static images is super easy:

```kotlin
@Composable
fun Sandbox() {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(8.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Image(
            painter = painterResource(R.drawable.ic_launcher_foreground),
            "An image",
            modifier = Modifier.requiredSize(96.dp)
        )
        Text("Hello Image")
    }
}
```

As you can see I obtain a `painter` through `painterResource()`. The docs say:

> Create a `Painter` from an Android resource id. This can load
> either an instance of `BitmapPainter` or `VectorPainter` for
> `ImageBitmap` based assets or vector based assets respectively.
> The resources with the given id must point to either fully
> rasterized images (ex. PNG or JPG files) or VectorDrawable xml
> assets. API based xml Drawables are not supported here.

There is one caveat, though.

If you try this:

```kotlin
Image(
  painter = painterResource(R.mipmap.ic_launcher),
  "An image",
  modifier = Modifier.requiredSize(96.dp)
)
```

you will get a `IllegalArgumentException: Only VectorDrawables and rasterized asset types are supported ex. PNG, JPG` at runtime if your app icon is an adaptive icon. This is the case if your have a file *ic_launcher.xml* in *mipmap-anydpi-v26*. Yet, it's easy to load adaptive icons:

```kotlin
@Composable
fun Sandbox() {
  ResourcesCompat.getDrawable(
    LocalContext.current.resources,
    R.mipmap.ic_launcher, LocalContext.current.theme
  )?.let { drawable ->
    val bitmap = Bitmap.createBitmap(
      drawable.intrinsicWidth, drawable.intrinsicHeight,
      Bitmap.Config.ARGB_8888
    )
    val canvas = Canvas(bitmap)
    drawable.setBounds(0, 0, canvas.width, canvas.height)
    drawable.draw(canvas)
    Column(
      modifier = Modifier
        .fillMaxSize()
        .padding(8.dp),
      verticalArrangement = Arrangement.Center,
      horizontalAlignment = Alignment.CenterHorizontally
    ) {
      Image(
        // painter = painterResource(R.mipmap.ic_launcher),
        bitmap = bitmap.asImageBitmap(),
        "An image",
        modifier = Modifier.requiredSize(96.dp)
      )
      Text("Hello Image")
    }
  }
}
```

So, we just need to...

- get a `Drawable` using `ResourcesCompat.getDrawable()`
- create an empty `android.graphics.Bitmap`
- create an `android.graphics.Canvas` that operates on this bitmap
- draw the drawable on the canvas (into our bitmap)
- get an `androidx.compose.ui.graphics.ImageBitmap` instance using the extension function `asImageBitmap()`

Quite easy, isn't it? 😎

### Drawing with `Canvas()`

Just like `Image()`, `Canvas()` is a Top-level function in `androidx.compose.foundation`. Please do not confuse it with the class we saw moments ago, `android.graphics.Canvas` (which belongs to the Android framework). I have written a complete series called [Drawing and painting in Jetpack Compose](https://dev.to/tkuenneth/series/10525), so here I will show you just one example.

```kotlin
@Composable
@Preview
fun Sandbox() {
  Canvas(modifier = Modifier.fillMaxSize(),
    onDraw = {
      drawLine(
        Color.Black, Offset(0f, 0f),
        Offset(size.width - 1, size.height - 1)
      )
      drawLine(
        Color.Black, Offset(0f, size.height - 1),
        Offset(size.width - 1, 0f)
      )
      drawCircle(
        Color.Red, 64f,
        Offset(size.width / 2, size.height / 2)
      )
    })
}
```

![Drawing on a canvas](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iqvwgu4ii7div6xy328x.png)

You specify an area on the screen and perform drawing operations on it. These instructions are given inside `onDraw()`. My example produces two lines and a filled circle.

### Wrap up

I hope you like this series. Please share your thoughts in the comments. In the next installment we will turn to the subpackages of `androidx.compose.foundation`. So stay tuned.