A Floating Action Button Speed Dial implementation for Android that follows the Material Design specification (https://material.io/components/buttons-floating-action-button#types-of-transitions)
Java
1,475
263 commits
updated Apr 4, 2024
Android library providing an implementation of the Material Design Floating Action Button Speed Dial for both classic View and Compose.
RecyclerView and NestedScrollView behaviorThe library is available on Jcenter so no additional repository is required.
Dependencies entry (latest version on Maven Central
):
// Compose only
implementation "com.leinardi.android:speed-dial.compose:1.0.0-alpha04"
// Classic View only
implementation "com.leinardi.android:speed-dial:3.3.0"
You can use JitPack to test the latest master (remember that master is the development branch and can be unstable or completely broken).
Add the JitPack repository to your build file:
maven { url 'https://jitpack.io' }
Add the dependency
implementation 'com.github.leinardi:FloatingActionButtonSpeedDial:master-SNAPSHOT'
SpeedDialAdd the SpeedDial() Composable to the floatingActionButton of your Scaffold:
var speedDialState by rememberSaveable { mutableStateOf(SpeedDialState.Collapsed) }
var overlayVisible: Boolean by rememberSaveable { mutableStateOf(speedDialState.isExpanded()) }
Scaffold(
floatingActionButton = {
SpeedDial(
state = speedDialState,
onFabClick = { expanded ->
overlayVisible = !expanded
speedDialState = SpeedDialState(!expanded)
},
) {
}
}
) {
SpeedDialOverlay(
visible = overlayVisible,
onClick = {
overlayVisible = false
speedDialState = speedDialState.toggle()
},
)
}
Add the FabWithLabel items to the SpeedDial:
SpeedDial(
state = speedDialState,
onFabClick = { expanded ->
overlayVisible = !expanded
speedDialState = SpeedDialState(!expanded)
},
) {
item {
FabWithLabel(
onClick = { showToast(context, "Item 1 clicked!") },
labelContent = { Text(text = "Item 1") },
) {
Icon(Icons.Default.Share, null)
}
}
item {
FabWithLabel(
onClick = { showToast(context, "Item 2 clicked!") },
labelContent = { Text(text = "Item 2") },
) {
Icon(Icons.Default.Share, null)
}
}
}
SpeedDialViewAdd the SpeedDialView to your layout:
<com.leinardi.android.speeddial.SpeedDialView android:id="@+id/speedDial" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" app:sdMainFabClosedSrc="@drawable/ic_add_white_24dp" />
Add the items to the SpeedDialView:
val speedDialView = findViewById<SpeedDialView>(R.id.speedDial)
speedDialView.addActionItem(
SpeedDialActionItem.Builder(R.id.fab_no_label, R.drawable.ic_link_white_24dp)
.create()
)
If the color customization is not requested, it is also possible to inflate the Action items form a Menu Resource:
speedDialView.inflate(R.menu.menu_speed_dial)
Only the attributes android:id, android:icon and android:title are supported.
Add the click listeners:
speedDialView.setOnActionSelectedListener(SpeedDialView.OnActionSelectedListener { actionItem ->
when (actionItem.id) {
R.id.fab_no_label -> {
showToast("No label action clicked!\nClosing with animation")
speedDialView.close() // To close the Speed Dial with animation
return@OnActionSelectedListener true // false will close it without animation
}
}
false
})
speedDialView.setOnChangeListener(object : SpeedDialView.OnChangeListener {
override fun onMainActionSelected(): Boolean {
showToast("Main action clicked!")
return false // True to keep the Speed Dial open
}
override fun onToggleChanged(isOpen: Boolean) {
Log.d(TAG, "Speed dial toggle state changed. Open = $isOpen")
}
})
The SpeedDialActionItem.Builder provides several setters to customize the aspect of one item:
speedDialView.addActionItem(
SpeedDialActionItem.Builder(R.id.fab_custom_color, drawable)
.setFabBackgroundColor(ResourcesCompat.getColor(resources, R.color.material_white_1000, getTheme()))
.setFabImageTintColor(ResourcesCompat.getColor(resources, R.color.inbox_primary, getTheme()))
.setLabel(getString(R.string.label_custom_color))
.setLabelColor(Color.WHITE)
.setLabelBackgroundColor(ResourcesCompat.getColor(resources, R.color.inbox_primary, getTheme()))
.setLabelClickable(false)
.create()
)
It is also possible to specify a theme to easily change the FAB background and ripple effect color:
speedDialView.addActionItem(
SpeedDialActionItem.Builder(R.id.fab_custom_theme, R.drawable.ic_theme_white_24dp)
.setLabel(getString(R.string.label_custom_theme))
.setTheme(R.style.AppTheme_Purple)
.create()
)
<style name="AppTheme.Purple" parent="AppTheme">
<item name="colorPrimary">@color/material_purple_500</item>
<item name="colorPrimaryDark">@color/material_purple_700</item>
<item name="colorAccent">@color/material_purple_a700</item>
<item name="colorControlHighlight">@color/material_purple_200</item>
</style>
You simply need to add the SpeedDialOverlayLayout to your layout:
<com.leinardi.android.speeddial.SpeedDialOverlayLayout android:id="@+id/overlay" android:layout_width="match_parent" android:layout_height="match_parent" />
and then provide the instance of that layout to the SpeedDialView:
<com.leinardi.android.speeddial.SpeedDialView android:id="@+id/speedDial" android:layout_width="wrap_content" android:layout_height="wrap_content" app:sdMainFabClosedSrc="@drawable/ic_add_white_24dp" app:sdOverlayLayout="@id/overlay" />
or
val overlayLayout = findViewById<SpeedDialOverlayLayout>(R.id.overlay)
speedDialView.setSpeedDialOverlayLayout(overlayLayout)
RecyclerView or a NestedScrollViewJust apply the ScrollingViewSnackbarBehavior to the SpeedDialView. This can be done via XML using the convenience string
resource @string/speeddial_scrolling_view_snackbar_behavior:
<com.leinardi.android.speeddial.SpeedDialView android:id="@+id/speedDial" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_behavior="@string/speeddial_scrolling_view_snackbar_behavior" />
Or programmatically:
val params = speedDialView.layoutParams as CoordinatorLayout.LayoutParams
params.behavior = SpeedDialView.ScrollingViewSnackbarBehavior()
speedDialView.requestLayout()
NB: for the behaviors to work, SpeedDialView needs to be a direct child of CoordinatorLayout
SnackbarBehaviorSince the SnackbarBehavior is enabled by default and, afaik, it is not possible to remove a Behavior, simply use apply
the SpeedDialView.NoBehavior instead:
val params = speedDialView.layoutParams as CoordinatorLayout.LayoutParams
params.behavior = SpeedDialView.NoBehavior()
speedDialView.requestLayout()
A fully working example is available here.
https://www.youtube.com/watch?v=tWowiF5ElAg

SpeedDialActionItem.Builder?It can be done in XML using the <item type="id" />:
<resources>
<item name="fab_action1" type="id" />
<item name="fab_action2" type="id" />
<item name="fab_action3" type="id" />
<item name="fab_action4" type="id" />
</resources>
You can set a different value for the max length of the label overriding sd_label_max_width:
<dimen name="sd_label_max_width">240dp</dimen>
More info here.
The color of the SpeedDialOverlayLayout can be changed simply using the android:background attribute or, programmatically, using the equivalent
setter like any other view.
Toolbar inside a CoordinatorLayout?It can be done using the attribute app:layout_behavior="@string/appbar_scrolling_view_behavior":
<com.leinardi.android.speeddial.SpeedDialOverlayLayout android:id="@+id/overlay" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" />
See the CHANGELOG.md
This project is based on floating-action-menu by ArthurGhazaryan.
Copyright 2021 Roberto Leinardi.
Licensed to the Apache Software Foundation (ASF) under one or more contributor
license agreements. See the NOTICE file distributed with this work for
additional information regarding copyright ownership. The ASF licenses this
file to you under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy of
the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.
13,002 followers · starred Apr 2018
4,112 followers · starred Apr 2018
663 followers · starred Dec 2021
A Floating Action Button Speed Dial implementation for Android that follows the Material Design specification (https://material.io/components/buttons-floating-action-button#types-of-transitions)
Java
1,475
263 commits
updated Apr 4, 2024
Android library providing an implementation of the Material Design Floating Action Button Speed Dial for both classic View and Compose.
RecyclerView and NestedScrollView behaviorThe library is available on Jcenter so no additional repository is required.
Dependencies entry (latest version on Maven Central
):
// Compose only
implementation "com.leinardi.android:speed-dial.compose:1.0.0-alpha04"
// Classic View only
implementation "com.leinardi.android:speed-dial:3.3.0"
You can use JitPack to test the latest master (remember that master is the development branch and can be unstable or completely broken).
Add the JitPack repository to your build file:
maven { url 'https://jitpack.io' }
Add the dependency
implementation 'com.github.leinardi:FloatingActionButtonSpeedDial:master-SNAPSHOT'
SpeedDialAdd the SpeedDial() Composable to the floatingActionButton of your Scaffold:
var speedDialState by rememberSaveable { mutableStateOf(SpeedDialState.Collapsed) }
var overlayVisible: Boolean by rememberSaveable { mutableStateOf(speedDialState.isExpanded()) }
Scaffold(
floatingActionButton = {
SpeedDial(
state = speedDialState,
onFabClick = { expanded ->
overlayVisible = !expanded
speedDialState = SpeedDialState(!expanded)
},
) {
}
}
) {
SpeedDialOverlay(
visible = overlayVisible,
onClick = {
overlayVisible = false
speedDialState = speedDialState.toggle()
},
)
}
Add the FabWithLabel items to the SpeedDial:
SpeedDial(
state = speedDialState,
onFabClick = { expanded ->
overlayVisible = !expanded
speedDialState = SpeedDialState(!expanded)
},
) {
item {
FabWithLabel(
onClick = { showToast(context, "Item 1 clicked!") },
labelContent = { Text(text = "Item 1") },
) {
Icon(Icons.Default.Share, null)
}
}
item {
FabWithLabel(
onClick = { showToast(context, "Item 2 clicked!") },
labelContent = { Text(text = "Item 2") },
) {
Icon(Icons.Default.Share, null)
}
}
}
SpeedDialViewAdd the SpeedDialView to your layout:
<com.leinardi.android.speeddial.SpeedDialView android:id="@+id/speedDial" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" app:sdMainFabClosedSrc="@drawable/ic_add_white_24dp" />
Add the items to the SpeedDialView:
val speedDialView = findViewById<SpeedDialView>(R.id.speedDial)
speedDialView.addActionItem(
SpeedDialActionItem.Builder(R.id.fab_no_label, R.drawable.ic_link_white_24dp)
.create()
)
If the color customization is not requested, it is also possible to inflate the Action items form a Menu Resource:
speedDialView.inflate(R.menu.menu_speed_dial)
Only the attributes android:id, android:icon and android:title are supported.
Add the click listeners:
speedDialView.setOnActionSelectedListener(SpeedDialView.OnActionSelectedListener { actionItem ->
when (actionItem.id) {
R.id.fab_no_label -> {
showToast("No label action clicked!\nClosing with animation")
speedDialView.close() // To close the Speed Dial with animation
return@OnActionSelectedListener true // false will close it without animation
}
}
false
})
speedDialView.setOnChangeListener(object : SpeedDialView.OnChangeListener {
override fun onMainActionSelected(): Boolean {
showToast("Main action clicked!")
return false // True to keep the Speed Dial open
}
override fun onToggleChanged(isOpen: Boolean) {
Log.d(TAG, "Speed dial toggle state changed. Open = $isOpen")
}
})
The SpeedDialActionItem.Builder provides several setters to customize the aspect of one item:
speedDialView.addActionItem(
SpeedDialActionItem.Builder(R.id.fab_custom_color, drawable)
.setFabBackgroundColor(ResourcesCompat.getColor(resources, R.color.material_white_1000, getTheme()))
.setFabImageTintColor(ResourcesCompat.getColor(resources, R.color.inbox_primary, getTheme()))
.setLabel(getString(R.string.label_custom_color))
.setLabelColor(Color.WHITE)
.setLabelBackgroundColor(ResourcesCompat.getColor(resources, R.color.inbox_primary, getTheme()))
.setLabelClickable(false)
.create()
)
It is also possible to specify a theme to easily change the FAB background and ripple effect color:
speedDialView.addActionItem(
SpeedDialActionItem.Builder(R.id.fab_custom_theme, R.drawable.ic_theme_white_24dp)
.setLabel(getString(R.string.label_custom_theme))
.setTheme(R.style.AppTheme_Purple)
.create()
)
<style name="AppTheme.Purple" parent="AppTheme">
<item name="colorPrimary">@color/material_purple_500</item>
<item name="colorPrimaryDark">@color/material_purple_700</item>
<item name="colorAccent">@color/material_purple_a700</item>
<item name="colorControlHighlight">@color/material_purple_200</item>
</style>
You simply need to add the SpeedDialOverlayLayout to your layout:
<com.leinardi.android.speeddial.SpeedDialOverlayLayout android:id="@+id/overlay" android:layout_width="match_parent" android:layout_height="match_parent" />
and then provide the instance of that layout to the SpeedDialView:
<com.leinardi.android.speeddial.SpeedDialView android:id="@+id/speedDial" android:layout_width="wrap_content" android:layout_height="wrap_content" app:sdMainFabClosedSrc="@drawable/ic_add_white_24dp" app:sdOverlayLayout="@id/overlay" />
or
val overlayLayout = findViewById<SpeedDialOverlayLayout>(R.id.overlay)
speedDialView.setSpeedDialOverlayLayout(overlayLayout)
RecyclerView or a NestedScrollViewJust apply the ScrollingViewSnackbarBehavior to the SpeedDialView. This can be done via XML using the convenience string
resource @string/speeddial_scrolling_view_snackbar_behavior:
<com.leinardi.android.speeddial.SpeedDialView android:id="@+id/speedDial" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_behavior="@string/speeddial_scrolling_view_snackbar_behavior" />
Or programmatically:
val params = speedDialView.layoutParams as CoordinatorLayout.LayoutParams
params.behavior = SpeedDialView.ScrollingViewSnackbarBehavior()
speedDialView.requestLayout()
NB: for the behaviors to work, SpeedDialView needs to be a direct child of CoordinatorLayout
SnackbarBehaviorSince the SnackbarBehavior is enabled by default and, afaik, it is not possible to remove a Behavior, simply use apply
the SpeedDialView.NoBehavior instead:
val params = speedDialView.layoutParams as CoordinatorLayout.LayoutParams
params.behavior = SpeedDialView.NoBehavior()
speedDialView.requestLayout()
A fully working example is available here.
https://www.youtube.com/watch?v=tWowiF5ElAg

SpeedDialActionItem.Builder?It can be done in XML using the <item type="id" />:
<resources>
<item name="fab_action1" type="id" />
<item name="fab_action2" type="id" />
<item name="fab_action3" type="id" />
<item name="fab_action4" type="id" />
</resources>
You can set a different value for the max length of the label overriding sd_label_max_width:
<dimen name="sd_label_max_width">240dp</dimen>
More info here.
The color of the SpeedDialOverlayLayout can be changed simply using the android:background attribute or, programmatically, using the equivalent
setter like any other view.
Toolbar inside a CoordinatorLayout?It can be done using the attribute app:layout_behavior="@string/appbar_scrolling_view_behavior":
<com.leinardi.android.speeddial.SpeedDialOverlayLayout android:id="@+id/overlay" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" />
See the CHANGELOG.md
This project is based on floating-action-menu by ArthurGhazaryan.
Copyright 2021 Roberto Leinardi.
Licensed to the Apache Software Foundation (ASF) under one or more contributor
license agreements. See the NOTICE file distributed with this work for
additional information regarding copyright ownership. The ASF licenses this
file to you under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy of
the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.
13,002 followers · starred Apr 2018
4,112 followers · starred Apr 2018
663 followers · starred Dec 2021