Earlier this year, Google announced changes to Material Design in the form of Material Theming; Material Design is now more malleable, allowing it to better represent your brand, and look less sterile and cookie cutter.

Among the slew of changes that Material Theming brought, was the reimagining of the Floating Action Button as it originally debuted with Material Design; the Floating Action Button (henceforth referred to as a FAB) is now a little less cryptic, and a little more dynamic. It can house text to better describe itself, and when it doesn’t need to, can revert back to its more discrete, unassuming, yet prominent self.

This is really clever as it addresses one of the largest criticisms of the FAB; it was a little unintuitive and had a bit of mystery UI to it. Therefore, I was really excited to implement it in my app Teammate, and in this post I’m going to describe how.

Compose, don’t inherit

My favorite software engineering prose is favoring composition over inheritance, and this was an opportune moment to adhere to it. When creating a custom View in Android, it’s easy and tempting to extend a View class and add the custom behavior to it, however it may be better to create a utility class and add the desired attributes to it instead. The latest version of Material Components on Android provide both MaterialButton and FloatingActionButton components; but if you squint really closely, and throw in a healthy dose of padding, a MaterialButton looks quite a bit like an expanded FAB, so let’s mark this as our starting point.

An Extended FAB (1) along with 2 different styles of buttons (2 & 3). If button 2 rounded it’s corners some, and had just a little more padding…

So our entire premise is to come up with a morphing animation that converts a MaterialButton to a FloatingActionButton seamlessly, with no jitters whatsoever. Whew, okay, we can do this.

My favorite Android ViewGroup of the past few years is easily the ConstraintLayout for a litany of reasons. However, it is pertinent in this case specifically for animations where child views move from one state to another, so it will be used as the foundation for our animation. We will define our two states in xml that describe the both the collapsed and expanded states of our expanding FAB:

Collapsed FAB:

<?xml version="1.0" encoding="utf-8"?>

<android.support.constraint.ConstraintLayout android:id="@+id/extend_fab_container"

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:background="@drawable/bg_fab"

android:elevation="8dp">



<android.support.design.button.MaterialButton

android:id="@+id/fab"

style="@style/Widget.MaterialComponents.Button.UnelevatedButton"

android:layout_width="56dp"

android:layout_height="56dp"

app:cornerRadius="56dp"

app:layout_constraintBottom_toBottomOf="parent"

app:layout_constraintLeft_toLeftOf="parent"

app:layout_constraintRight_toRightOf="parent"

app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

Expanded FAB:

<?xml version="1.0" encoding="utf-8"?>

<android.support.constraint.ConstraintLayout android:id="@+id/extend_fab_container"

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:elevation="8dp"

android:background="@drawable/bg_fab">



<android.support.design.button.MaterialButton

android:id="@+id/fab"

style="@style/Widget.MaterialComponents.Button.UnelevatedButton"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

app:cornerRadius="56dp"

app:layout_constraintBottom_toBottomOf="parent"

app:layout_constraintLeft_toLeftOf="parent"

app:layout_constraintRight_toRightOf="parent"

app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

And of course, our background drawable, bg_fab:

<?xml version="1.0" encoding="utf-8"?>

<shape xmlns:android="http://schemas.android.com/apk/res/android">

<solid android:color="@color/colorAccent" />

<corners android:radius="56dp" />

</shape>

With these states defined, we can now write the code that transitions the FAB from state to state:

Simple, concise and sweet 😊.

You may have noticed that in the snippet above there are references made to the text and icon. This is because sometimes, we don’t just want to extend and collapse the FAB, but rather, we want to change the text, and we want that to animate as well, i.e the FAB should animate itself to accommodate any text changes we make to it.

Something that annoys me is that the ConstraintLayout exists only to animate the FAB. I tried removing it and just changing the LayoutParams of the MaterialButton directly, but it got wonky, so it’ll have to remain for now.

So far so good! However, our Extendable FAB is actually a composite ViewGroup that has no relationship to the original FAB in any way. This means if it were housed within a CoordinatorLayout , we’d lose the niceties that come for free with the default FAB such as the animations we get should a SnackBar appear.

This can easily be mitigated by creating (duplicating) the Behavior we desire from the original FAB class and attaching it to our composite component in xml.

And viola! We now have an expandable FAB, by simply composing the views already provided to us with the Material Design Components for Android.

You can see the Expanding FAB in Action in the latest version of the Teammate Android App.

The full source of the FabIconAnimator: