Split Button
Overview
The SplitButtonLayout composable in Jetpack Compose creates a button group with a primary action button and a trailing button that toggles a dropdown menu for related actions. It’s ideal for scenarios like saving a file with options such as "Save," "Save As," or "Save to Cloud." This component is part of Material 3, introduced in version 1.5.0-alpha03.
Explore two split button styles for implementing flexible button groups.
Explore Split Button Styles
Learn to implement two split button types for toggling related actions.
Basic Split Button
Creates a split button with a primary action.
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.SplitButtonDefaults
import androidx.compose.material3.SplitButtonLayout
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.stateDescription
@Composable
fun BasicSplitButtonExample() {
var checked by remember { mutableStateOf(false) }
SplitButtonLayout(
leadingButton = {
SplitButtonDefaults.LeadingButton(onClick = { /* Do Nothing */ }) {
Icon(
Icons.Filled.Edit,
modifier = Modifier.size(SplitButtonDefaults.LeadingIconSize),
contentDescription = "Localized description"
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text("My Button")
}
},
trailingButton = {
SplitButtonDefaults.TrailingButton(
checked = checked,
onCheckedChange = { checked = it },
modifier = Modifier.semantics {
stateDescription = if (checked) "Expanded" else "Collapsed"
contentDescription = "Toggle Button"
}
) {
val rotation: Float by animateFloatAsState(
targetValue = if (checked) 180f else 0f,
label = "Trailing Icon Rotation"
)
Icon(
Icons.Filled.KeyboardArrowDown,
modifier = Modifier.size(SplitButtonDefaults.TrailingIconSize).graphicsLayer { rotationZ = rotation },
contentDescription = "Localized description"
)
}
}
)
}
Use a Basic Split Button when you want to provide a primary action with a secondary toggle.
Examples:
- Trigger a save action with a toggle for related options
- Enable editing with a secondary action toggle
- Select a primary action in a toolbar with a related sub-action
Split Button with Dropdown Menu
Implements a split button with a rotating trailing icon for state feedback. Expands into a dropdown menu offering multiple user actions.
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material.icons.outlined.Edit
import androidx.compose.material.icons.outlined.Email
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.PlainTooltip
import androidx.compose.material3.SplitButtonDefaults
import androidx.compose.material3.SplitButtonLayout
import androidx.compose.material3.Text
import androidx.compose.material3.TooltipAnchorPosition
import androidx.compose.material3.TooltipBox
import androidx.compose.material3.TooltipDefaults
import androidx.compose.material3.rememberTooltipState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.stateDescription
import androidx.compose.ui.text.style.TextAlign
@Composable
fun SplitButtonWithIconExample() {
var checked by remember { mutableStateOf(false) }
Box(modifier = Modifier.fillMaxSize().wrapContentSize()) {
SplitButtonLayout(
leadingButton = {
SplitButtonDefaults.LeadingButton(onClick = { /* Do Nothing */ }) {
Icon(
Icons.Filled.Edit,
modifier = Modifier.size(SplitButtonDefaults.LeadingIconSize),
contentDescription = "Localized description"
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text("My Button")
}
},
trailingButton = {
val description = "Toggle Button"
TooltipBox(
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(TooltipAnchorPosition.Above),
tooltip = { PlainTooltip { Text(description) } },
state = rememberTooltipState()
) {
SplitButtonDefaults.TrailingButton(
checked = checked,
onCheckedChange = { checked = it },
modifier = Modifier.semantics {
stateDescription = if (checked) "Expanded" else "Collapsed"
contentDescription = description
}
) {
val rotation: Float by animateFloatAsState(
targetValue = if (checked) 180f else 0f,
label = "Trailing Icon Rotation"
)
Icon(
Icons.Filled.KeyboardArrowDown,
modifier = Modifier.size(SplitButtonDefaults.TrailingIconSize).graphicsLayer { rotationZ = rotation },
contentDescription = "Localized description"
)
}
}
}
)
DropdownMenu(expanded = checked, onDismissRequest = { checked = false }) {
DropdownMenuItem(
text = { Text("Edit") },
onClick = { /* Handle edit! */ },
leadingIcon = { Icon(Icons.Outlined.Edit, contentDescription = null) }
)
DropdownMenuItem(
text = { Text("Settings") },
onClick = { /* Handle settings! */ },
leadingIcon = { Icon(Icons.Outlined.Settings, contentDescription = null) }
)
HorizontalDivider()
DropdownMenuItem(
text = { Text("Send Feedback") },
onClick = { /* Handle send feedback! */ },
leadingIcon = { Icon(Icons.Outlined.Email, contentDescription = null) },
trailingIcon = { Text("F11", textAlign = TextAlign.Center) }
)
}
}
}
Use a Split Button with Custom Icon when you want to enhance visual feedback for the dropdown toggle.
Examples:
- Indicate dropdown state in a toolbar
- Highlight toggle action in a form
- Customize button appearance in a settings menu
Split Button Properties
Understand the key properties used in the split button examples.
| Property | Usage |
|---|---|
| leadingButton* | Required: Composable for the primary action button |
| trailingButton* | Required: Composable for the toggle button |
| checked | Boolean for dropdown menu state (expanded/collapsed) |
| modifier | Applies semantics or styling to buttons |


