Render your Composable the size you want
When building components with Jetpack Compose, you can render them the way you want, thanks to the myriad of Modifier properties.
Most of the time, you can customize any components by overriding this parameter — you should always provide one, too, especially while building an SDK. Arguably, though, some Composables come with a stricter set of rules. That’s the case with the
The native SDK exposes a Composable called
LinearProgressIndicator that displays a horizontal progress bar. To match the Material guidelines, the height matches
LinearIndicatorHeight (which equals to
Here’s the Composable’s implementation (at the time I’m writing this, with Compose 1.2.1):
As you may notice, the Composable internally overrides its size with fixed values.
Of course, this doesn’t prevent you from changing it! Since the Composable naturally exposes a
Modifier parameter, you can provide your own size. But what about if your size should fit another Composable’s size?
It’s time to look at the desired output. We would like to draw a progress bar that covers the whole content of a button.
If we break this Composable down, we have:
Boxthat encapsulates both the
I won’t cover the text and icon within the
Button as they are irrelevant.
This brings us to this code:
If you give it a run, you will notice that the progress bar:
- still has a
4.dpheight hence doesn’t fill the button.
- doesn’t get clipped at the rounded corners. Try it with a red color, you’ll see it straight away.
We can address the second issue easily. One way would be to delegate the shaping to the
Box instead of the
Button then clip its content.
val shape = RoundedCornerShape(8.dp)
modifier = modifier
color = MaterialTheme.colors.primary,
shape = shape,
Let’s move on to the most interesting part. We can pass a hardcoded value to change the progress bar’s height. But we would like it to dynamically match the container height no matter what’s in it.
First, there are several ways to tackle this problem. You could dive into the art of making custom components by using a
Layout and sizing the way you desire. For most cases, that would be preferable as it brings the most flexible pattern to draw your component.
Also, you could decide to ditch the
LinearProgressIndicator over a
Canvas and draw it yourself! Since this progress bar is meant to match the Material guidelines, it might be best not to tweak it further. And this would be one of the simplest shapes to draw in the
Canvas. All you need to take care of is the animation part yourself.
Now, for the sake of the example, we will still use our
LinearProgressIndicator and achieve the desired result with a few additional lines of code. We need to retrieve the
Button height and pass it to the
LinearProgressIndicator. We can leverage another useful
onSizeChanged(). This lambda passes its size when it gets redrawn as an argument. When received, convert it in
Dp then remember it so that our
LinearProgressIndicator recomposes with its new height.
For completeness, here is the final code of the Composable:
That’s all! This simple technique allows you to resize your components based on other component sizes. Note that you could reuse this pattern for any kind of component, although most of the time, the system should infer their size as you want using