CollapsingToolbar with Jetpack Compose

Minami Munakata
3 min readJan 31, 2023

--

Photo by Grabster on Unsplash

I have implemented CollapsingToolbar which has an enterAlways-like behavior with Jetpack Compose. Unfortunately, there is not enough information to create TopAppBar which expands and collapses when scrolling like a CoordinatorLayout of Android View, so, I would like to share how I have achieved it with Jetpack Compose.

Beforehand, if your project works with Material Design 3, it would be nice to try to play around with TopAppBarScrollBehavior.

This article is for those who don’t introduce Material Design 3 into their project. Let’s get started!

First of all, if you know the height of your TopAppBar or your custom component which contains TopAppBar and other layouts to be collapsed, you can just copy and paste sample codes from Android Developers.

However, there is a problem. Let’s say you want a collapsible component that has not only TopAppBar but also an image, but you don’t know the exact size of the image until you fetch the data from a server. You have to get the height of that collapsible component at some point to calculate the portion of the offset.

In that case, you can get the layout information like its size by Modifier.onGloballyPositioned.

To make it reusable, let’s create your custom state to manage variables. I would name it CollapsibleState.

Let’s create a function that returns a default CollapsibleState and remembers it.

Wait a minute. Why don’t you use rememberSaveable instead of the remember function? Using a rememberSaveable function makes it a lifecycle-aware UI. The stored value will survive when a user rotates a screen. But if you just replace a rememberSaveable function here instead of the remember function, it will occur an error which says:

java.lang.IllegalArgumentException: CollapsibleState cannot be saved using the current SaveableStateRegistry. The default implementation only supports types which can be stored inside the Bundle. Please consider implementing a custom Saver for this class and pass it to rememberSaveable().

As it claims, the state should be a type that can be stored inside the Bundle. To do so, we have to create a custom saver object. You can create it by using mapSaver or listSaver. You can find details here.

I created a companion object function inside the CollapsibleState class that provides a custom Saver object.

Now, you can remember the state by using rememberSaveable.

You are almost there! Now we have everything we need to create a custom Android-View-CollapsingToolbar-like layout.

I named it CollapsibleScaffold which is a container that has a CollapsingToolbar. You can also set whatever you want to collapse along with CollapsingToolbar. CollapsibleScaffold holds contents as LazyColumn.

See how I have implemented CollapsibleScaffold. (Actually, it might not be the best name since it is not the scaffold. Who cares. It’s just a demo.)

As you see, it takes collapsibleAppBar composable lambda (collapsibleAppBar: @Composable CollapsibleScope.() -> Unit).

CollapsibleScope is a custom scope and this is how it works.

Now you can hoist collapsibleAppBar , and you can pass your custom modifier extension by taking CollapsibleScope so that you can decorate collapsibleAppBar from outside of CollapsibleScaffold.

Voila! See it shows you expanding-collapsing behavior like enterAlways.

Demo

You can check the whole code on GitHub.

As a newbie to Jetpack Compose, I have faced many problems to implement this CollapsingToolbar. However, I also have found a lot of tips from android developers. I would like to say thank you to them.

Also, I hope my sample code would help someone.

Have a happy coding day!

--

--