Skip to content

FAB menu and Medium FAB in Compose

Tap the FAB: it morphs into a round close button and the actions rise out of it

The FAB menu replaces the speed dial. The FAB morphs into a close button, and 2 to 6 labelled actions open above it. When it closes, the items fold back into the FAB.

data class FabAction(val icon: ImageVector, val label: String, val onClick: () -> Unit)
@Composable
fun ComposeFabMenu(
actions: List<FabAction>,
addIcon: ImageVector,
closeIcon: ImageVector,
listState: LazyListState,
modifier: Modifier = Modifier,
) {
var expanded by rememberSaveable { mutableStateOf(false) }
BackHandler(expanded) { expanded = false }
val fabVisible by remember {
derivedStateOf { listState.firstVisibleItemIndex == 0 || !listState.canScrollForward }
}
Box(modifier.fillMaxSize()) {
FloatingActionButtonMenu(
modifier = Modifier.align(Alignment.BottomEnd),
expanded = expanded,
button = {
ToggleFloatingActionButton(
checked = expanded,
onCheckedChange = { expanded = it },
modifier = Modifier
.semantics {
traversalIndex = -1f
stateDescription = if (expanded) "Expanded" else "Collapsed"
contentDescription = "Toggle menu"
}
.animateFloatingActionButton(
visible = fabVisible || expanded,
alignment = Alignment.BottomEnd,
),
) {
// checkedProgress goes 0 -> 1 as the FAB morphs into the close button.
val icon by remember { derivedStateOf { if (checkedProgress > 0.5f) closeIcon else addIcon } }
Icon(
painter = rememberVectorPainter(icon),
contentDescription = null,
modifier = Modifier.animateIcon({ checkedProgress }),
)
}
},
) {
actions.forEach { action ->
FloatingActionButtonMenuItem(
onClick = { expanded = false; action.onClick() },
icon = { Icon(action.icon, contentDescription = null) },
text = { Text(action.label) },
)
}
}
}
}
Composable Size Corner
FloatingActionButton 56dp 16dp (large)
MediumFloatingActionButton (new) 80dp 20dp (largeIncreased)
LargeFloatingActionButton 96dp 28dp (extraLarge)
@Composable
fun NewItemFab(icon: ImageVector, onClick: () -> Unit) {
// FAB sizes: FloatingActionButton 56dp, MediumFloatingActionButton 80dp (new in 1.5.0),
// LargeFloatingActionButton 96dp. M3 Expressive guidance steers away from the 40dp small FAB.
MediumFloatingActionButton(onClick = onClick) { Icon(icon, contentDescription = "New item") }
}
  • The toggle FAB comes first in traversal order (traversalIndex = -1f).
  • It announces Expanded / Collapsed.
  • Back closes the menu.

Google’s sample also wraps the FAB in a TooltipBox and adds a “Close menu” custom action on the last item.

  • Putting more than 6 actions in the menu. Move extra actions to a sheet.
  • Leaving the FAB visible while the list scrolls. Use Modifier.animateFloatingActionButton(visible = …).

Related: floating toolbar · design rules: morph, don’t swap

Want your agent to get this right every time? Install the skill. If this page helped,star the repo on GitHub.

Built by Sanikant. Set in Google Sans Flex (SIL Open Font License), icons from Material Symbols (Apache 2.0). Material Design is a Google trademark; this project is not affiliated with Google.