Skip to content

Floating toolbar in Jetpack Compose

A pill toolbar with a selected tool, beside a contrasting FAB. Collapse it to the selected tool.

Floating toolbars replace the bottom app bar for contextual actions. In Google’s before/after, a docked bar with a FAB becomes a floating pill toolbar with a separate FAB of a different shape and color.

@Composable
fun EditorScreen(
lines: List<String>,
toolIcons: List<Pair<ImageVector, String>>,
addIcon: ImageVector,
onAdd: () -> Unit,
) {
// Floating toolbars replace the bottom app bar in M3 Expressive. They hide on scroll down.
val exitAlways = FloatingToolbarDefaults.exitAlwaysScrollBehavior(
exitDirection = FloatingToolbarExitDirection.Bottom,
)
Scaffold(modifier = Modifier.nestedScroll(exitAlways)) { padding ->
Box(Modifier.padding(padding)) {
LazyColumn {
items(lines) { ListItem { Text(it) } }
}
HorizontalFloatingToolbar(
expanded = true,
floatingActionButton = {
FloatingToolbarDefaults.VibrantFloatingActionButton(onClick = onAdd) {
Icon(addIcon, contentDescription = "Add")
}
},
colors = FloatingToolbarDefaults.vibrantFloatingToolbarColors(),
scrollBehavior = exitAlways,
modifier = Modifier
.align(Alignment.BottomCenter)
.offset(y = -FloatingToolbarDefaults.ScreenOffset)
.zIndex(1f),
) {
toolIcons.forEach { (icon, label) ->
IconButton(onClick = {}) { Icon(icon, contentDescription = label) }
}
}
}
}
}

Colors: vibrantFloatingToolbarColors() (primary container) or standardFloatingToolbarColors() (surface container). To collapse a toolbar without a FAB to its main content, use the overload with expanded, leadingContent and trailingContent.

/**
* Google's before/after: a docked bottom bar with a FAB becomes a floating toolbar plus a
* separate FAB in a contrasting shape and color. The FAB is the primary action, so it gets the
* vibrant container and the square-ish shape; the toolbar stays a pill.
*/
@Composable
fun ComposeActions(actions: List<Pair<ImageVector, String>>, primary: ImageVector, onPrimary: () -> Unit) {
HorizontalFloatingToolbar(
expanded = true,
floatingActionButton = {
FloatingToolbarDefaults.VibrantFloatingActionButton(onClick = onPrimary) {
Icon(primary, contentDescription = "Compose")
}
},
) {
actions.forEach { (icon, label) ->
IconButton(onClick = {}) { Icon(icon, contentDescription = label) }
}
}
}
  • Placing it inside Scaffold(bottomBar = …). It floats over content: align it bottom-centre in a Box, offset by FloatingToolbarDefaults.ScreenOffset.
  • Forgetting Modifier.nestedScroll(exitAlways) on the scrolling parent, which stops it hiding on scroll.

Related: FAB menu · flexible top app bar

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.