ButtonGroup and connected button groups in Compose
There are two kinds of button group.
Connected: pick one (or several)
Section titled “Connected: pick one (or several)”Connected groups look like a segmented control. Build one from ToggleButtons with the
connected shapes for the first, middle and last items:
@Composablefun PeriodPicker(options: List<String>) { var selected by rememberSaveable { mutableIntStateOf(0) } // Connected button group = single-select segmented control with expressive shapes. // Replaces SingleChoiceSegmentedButtonRow when you want the M3 Expressive look. FlowRow(horizontalArrangement = Arrangement.spacedBy(ButtonGroupDefaults.ConnectedSpaceBetween)) { options.forEachIndexed { index, label -> ToggleButton( checked = selected == index, onCheckedChange = { selected = index }, shapes = when (index) { 0 -> ButtonGroupDefaults.connectedLeadingButtonShapes() options.lastIndex -> ButtonGroupDefaults.connectedTrailingButtonShapes() else -> ButtonGroupDefaults.connectedMiddleButtonShapes() }, modifier = Modifier.weight(1f).semantics { role = Role.RadioButton }, ) { Text(label) } } }}In Google’s examples the selected segment turns dark, becomes a full pill and gets a little wider
while the others shrink. The connected shapes give you the shape change. The width change comes
from weight and the group’s layout.
Standard: a row of actions
Section titled “Standard: a row of actions”ButtonGroup lays out actions where the pressed one grows and pushes its neighbours. Items
that don’t fit move into an overflow menu automatically.
@Composablefun EditorActions(onAction: (String) -> Unit) { // Standard button group: neighbours squeeze when one is pressed; extra items go to overflow. ButtonGroup( overflowIndicator = { menuState -> ButtonGroupDefaults.OverflowIndicator(menuState = menuState) }, ) { listOf("Bold", "Italic", "Underline", "Strike", "Code", "Link").forEach { label -> clickableItem(onClick = { onAction(label) }, label = label) } }}Inside ButtonGroup, add items with clickableItem, toggleableItem or customItem.
ButtonGroupScope is not a RowScope, so plain composables don’t go there.
Accessibility
Section titled “Accessibility”- Single-select: put
Role.RadioButtonon each item (as in the snippet). - Multi-select: use
Role.Checkbox. - Label every icon-only item.
Common mistakes
Section titled “Common mistakes”- Using the same
ButtonGroupDefaults.connectedMiddleButtonShapes()for every item. The first and last items need their own shapes. - Expecting
SingleChoiceSegmentedButtonRowto look expressive. It still works, but the connected group is the M3 Expressive look.
Related: toggle buttons · split button · design rules
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.