Adaptive LinearProgressIndicator Height in Compose
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 LinearProgressIndicator
.
The LinearProgressIndicator
Use Case
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 4.dp
).
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:
- a
Button
- a
LinearProgressIndicator
- a
Box
that encapsulates both theButton
and theLinearProgressIndicator
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.dp
height 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)
Box(
modifier = modifier
.background(
color = MaterialTheme.colors.primary,
shape = shape,
)
.clip(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.
Resizing The LinearProgressIndicator
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 Modifier
called 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 Modifier
properties.