Skip to content

SplitButton in Jetpack Compose

Tap the arrow: the trailing button turns round, the arrow flips, the menu opens

A split button pairs a main action (“Send”, “Save”) with a menu of related actions. The trailing half is a toggle. While it’s checked, rotate the arrow and show your DropdownMenu.

@Composable
fun SaveSplitButton(arrowDown: ImageVector, onSave: () -> Unit, onMenu: (Boolean) -> Unit) {
var menuOpen by rememberSaveable { mutableStateOf(false) }
SplitButtonLayout(
leadingButton = {
SplitButtonDefaults.LeadingButton(onClick = onSave) { Text("Save") }
},
trailingButton = {
SplitButtonDefaults.TrailingButton(
checked = menuOpen,
onCheckedChange = { menuOpen = it; onMenu(it) },
modifier = Modifier.semantics {
stateDescription = if (menuOpen) "Expanded" else "Collapsed"
},
) {
val rotation by animateFloatAsState(if (menuOpen) 180f else 0f, label = "arrow")
Icon(
arrowDown,
contentDescription = "More save options",
modifier = Modifier
.size(SplitButtonDefaults.TrailingIconSize)
.graphicsLayer { rotationZ = rotation },
)
}
},
)
}

Variants come in matching pairs: LeadingButton/TrailingButton (filled), TonalLeadingButton/TonalTrailingButton, ElevatedLeadingButton/ElevatedTrailingButton and OutlinedLeadingButton/OutlinedTrailingButton. Don’t mix them.

The trailing button is icon-only. Give it a content description (“More save options”) and a stateDescription of Expanded/Collapsed.

Related: buttons · floating toolbar

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.