FloatingActionButton in Compose: All FAB Variants & Expandable Menu
Floating Action Buttons (FABs) are a signature Material Design component for primary actions. Jetpack Compose provides four FAB variants, each suited for different use cases.
The Four FAB Variants
1. FloatingActionButton (Standard FAB)
The classic circular FAB, typically 56dp:
FloatingActionButton(
onClick = { /* Handle click */ },
modifier = Modifier.padding(16.dp),
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary
) {
Icon(Icons.Default.Add, contentDescription = "Add")
}
2. SmallFloatingActionButton
Compact 40dp variant for secondary actions:
SmallFloatingActionButton(
onClick = { /* Handle click */ },
containerColor = MaterialTheme.colorScheme.primaryContainer
) {
Icon(Icons.Default.Edit, contentDescription = "Edit")
}
3. LargeFloatingActionButton
Spacious 96dp variant for prominent actions:
LargeFloatingActionButton(
onClick = { /* Handle click */ },
modifier = Modifier.padding(32.dp)
) {
Icon(
imageVector = Icons.Default.Create,
contentDescription = "Create",
modifier = Modifier.size(36.dp)
)
}
4. ExtendedFloatingActionButton
FAB with text label, ideal for clarity:
ExtendedFloatingActionButton(
onClick = { /* Handle click */ },
icon = { Icon(Icons.Default.Send, contentDescription = null) },
text = { Text("Send Message") },
modifier = Modifier.padding(16.dp)
)
Scroll-Responsive ExtendedFAB
Hide the FAB on scroll down, show on scroll up using derivedStateOf:
val listState = rememberLazyListState()
val isFabVisible by remember {
derivedStateOf {
listState.firstVisibleItemIndex == 0
}
}
Scaffold(
floatingActionButton = {
AnimatedVisibility(
visible = isFabVisible,
enter = slideInVertically { it },
exit = slideOutVertically { it }
) {
ExtendedFloatingActionButton(
onClick = { /* Scroll to top */ },
icon = { Icon(Icons.Default.KeyboardArrowUp, null) },
text = { Text("Back to Top") }
)
}
}
) {
LazyColumn(state = listState) {
items(100) { index ->
ListItem(
headlineContent = { Text("Item $index") },
modifier = Modifier.fillMaxWidth()
)
}
}
}
Expandable FAB Menu
Create a multi-action FAB menu with AnimatedVisibility:
@Composable
fun ExpandableActionMenu() {
var expanded by remember { mutableStateOf(false) }
Box(modifier = Modifier.fillMaxSize()) {
// Main FAB
FloatingActionButton(
onClick = { expanded = !expanded },
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(16.dp),
containerColor = MaterialTheme.colorScheme.primary
) {
Icon(
imageVector = if (expanded) Icons.Default.Close else Icons.Default.Add,
contentDescription = if (expanded) "Close" else "Add"
)
}
// Expanded menu items
AnimatedVisibility(
visible = expanded,
enter = fadeIn() + scaleIn(),
exit = fadeOut() + scaleOut()
) {
Column(
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(bottom = 80.dp, end = 16.dp),
horizontalAlignment = Alignment.End,
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
FloatingActionButtonMenuItem(
icon = Icons.Default.Edit,
label = "Edit",
onClick = { expanded = false }
)
FloatingActionButtonMenuItem(
icon = Icons.Default.Share,
label = "Share",
onClick = { expanded = false }
)
FloatingActionButtonMenuItem(
icon = Icons.Default.Delete,
label = "Delete",
onClick = { expanded = false }
)
}
}
}
}
@Composable
fun FloatingActionButtonMenuItem(
icon: androidx.compose.material.icons.Icons,
label: String,
onClick: () -> Unit
) {
Row(
modifier = Modifier
.clip(RoundedCornerShape(8.dp))
.background(MaterialTheme.colorScheme.secondary)
.clickable(onClick = onClick)
.padding(8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(label, style = MaterialTheme.typography.labelSmall)
Icon(icon, contentDescription = label, Modifier.size(20.dp))
}
}
Custom FAB Colors & Shape
Customize FAB appearance with Material 3 color tokens:
FloatingActionButton(
onClick = { /* Handle click */ },
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
shape = RoundedCornerShape(12.dp), // Custom shape
modifier = Modifier.size(64.dp)
) {
Icon(Icons.Default.Favorite, contentDescription = "Favorite")
}
Key Takeaways
- FAB is the go-to for primary actions; ExtendedFAB adds textual clarity
- SmallFAB (40dp) and LargeFAB (96dp) suit secondary and prominent actions respectively
- derivedStateOf enables scroll-responsive FAB visibility without recomposition overhead
- AnimatedVisibility creates polished expandable FAB menus
- Customize colors and shapes to match your brand design system
${ CTA }
Top comments (0)