DEV Community

myougaTheAxo
myougaTheAxo

Posted on

FloatingActionButton in Compose: All FAB Variants & Expandable Menu

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")
}
Enter fullscreen mode Exit fullscreen mode

2. SmallFloatingActionButton

Compact 40dp variant for secondary actions:

SmallFloatingActionButton(
    onClick = { /* Handle click */ },
    containerColor = MaterialTheme.colorScheme.primaryContainer
) {
    Icon(Icons.Default.Edit, contentDescription = "Edit")
}
Enter fullscreen mode Exit fullscreen mode

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)
    )
}
Enter fullscreen mode Exit fullscreen mode

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)
)
Enter fullscreen mode Exit fullscreen mode

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()
            )
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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))
    }
}
Enter fullscreen mode Exit fullscreen mode

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")
}
Enter fullscreen mode Exit fullscreen mode

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)