Collapsible Extended Floating Action Buttons with a LazyColumn in Jetpack Compose

Patrick Millais
3 min readJul 1, 2022

Android users with a keen eye will have spotted small behavioural changes across apps made by Google in recent months.

Extended Floating Action Buttons (FABs) are wide buttons that typically appear in the bottom right of an app. These buttons enable users to perform a primary app function. A great example is the Compose button in Gmail:

Image from m3.material.io

With the arrival of Material Design 3, there are new recommendations on how these Extended Floating Action Buttons should behave when a user scrolls the content behind the button. In short: extended FABs should now collapse into a regular FAB on scroll.

This scrolling behaviour is already present in the latest Gmail app. The video below shows the transitions from extended to regular FABs when the app content is vertically scrolled.

Video from m3.material.io

Prior to Jetpack Compose, developers could achieve this behaviour with extendedFab.extend() and extendedFab.shrink() in combination with a RecyclerView scroll listener.

However, in the new world of Jetpack Compose the approach changes.

At the point of migrating my app to Jetpack Compose, there was little documentation online describing how to approach this with Android’s new view paradigm. Below I will share the approach I took so you can build this behaviour into your app.

There are three main building blocks to this approach:

  • Add a basic extension function to LazyListState
  • Prepare theScaffold and FloatingActionButton slot
  • Connect everything up!

Extend LazyListState

We need a way to detect how the user interacts with your LazyColumn, similar to a scroll listener on a RecyclerView. Specifically, we are interested in whether the user scrolls vertically upwards so that we can extend or collapse the FAB accordingly.

Enter LazyListState. LazyListState is used across Jetpack Compose to control and observe scrolling. It comes with some useful properties — such as isScrollInProgress — but none that will immediately tell us whether the user is scrolling upwards.

To detect whether the user scrolls upwards, the easiest thing to do is to extend LazyListState and add our own extension function isScrollingUp(). In fact, this is exactly what Google do in one of their Codelabs, and this is where this code snippet is taken from:

isScrollingUp() function from Google Code Labs (line 337)

Prepare The Scaffold

Next, we need to update the scaffold in your screen’s composable to take advantage of the new isScrollingUp() extension function.

The changes below are simple. First, declare a new listState variable on Line 1 with rememberLazyListState(). Second, connect this up with the expanded parameter of the ExtendedFloatingActionButton composable. This takes advantage of the extension function we created in the first step.

Joining Everything Together

Finally, we provide the LazyListState variable (listState) to LazyColumn so that we can observe and react to changes in its scroll position.

In Line 2 we provide the state object from the previous steps

And you are done!

Note that in many cases the listState will be hoisted across multiple composables. In my app’s case that looks something like this:

In this post we:

  • Created an extension function on LazyListState to detect the user’s vertical scroll
  • Connected LazyListState with an ExtendedFloatingActionButton
  • Observed the state on LazyColumn

And that’s it! The Extended FAB will now collapse into a regular FAB when the user scrolls down, and expand to an extended FAB when the user scrolls up.

--

--

Patrick Millais

Building things @paypal + @livefootyontv android app 📱 More at: milla.is