A layout composable that places its children in a horizontal sequence.
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Preview
@Composable
fun ButtonGroupSample() {
val numButtons = 10
ButtonGroup(
overflowIndicator = { menuState ->
ButtonGroupDefaults.OverflowIndicator(menuState = menuState)
},
verticalAlignment = Alignment.Top,
) {
for (i in 0 until numButtons) {
clickableItem(onClick = {}, label = "$i")
}
}
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun MultiSelectConnectedButtonGroupSample() {
val options = listOf("Work", "Restaurant", "Coffee")
val unCheckedIcons =
listOf(Icons.Outlined.Work, Icons.Outlined.Restaurant, Icons.Outlined.Coffee)
val checkedIcons = listOf(Icons.Filled.Work, Icons.Filled.Restaurant, Icons.Filled.Coffee)
val checked = remember { mutableStateListOf(false, false, false) }
Row(
Modifier.padding(horizontal = 8.dp),
horizontalArrangement = Arrangement.spacedBy(ButtonGroupDefaults.ConnectedSpaceBetween),
) {
val modifiers = listOf(Modifier.weight(1f), Modifier.weight(1.5f), Modifier.weight(1f))
options.forEachIndexed { index, label ->
ToggleButton(
checked = checked[index],
onCheckedChange = { checked[index] = it },
modifier = modifiers[index],
shapes =
when (index) {
0 -> ButtonGroupDefaults.connectedLeadingButtonShapes()
options.lastIndex -> ButtonGroupDefaults.connectedTrailingButtonShapes()
else -> ButtonGroupDefaults.connectedMiddleButtonShapes()
},
) {
Icon(
if (checked[index]) checkedIcons[index] else unCheckedIcons[index],
contentDescription = "Localized description",
)
Spacer(Modifier.size(ToggleButtonDefaults.IconSpacing))
Text(label)
}
}
}
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun MultiSelectConnectedButtonGroupWithFlowLayoutSample() {
val options = listOf("Work", "Restaurant", "Coffee", "Search", "Home")
val unCheckedIcons =
listOf(
Icons.Outlined.Work,
Icons.Outlined.Restaurant,
Icons.Outlined.Coffee,
Icons.Outlined.Search,
Icons.Outlined.Home,
)
val checkedIcons =
listOf(
Icons.Filled.Work,
Icons.Filled.Restaurant,
Icons.Filled.Coffee,
Icons.Filled.Search,
Icons.Filled.Home,
)
val checked = remember { mutableStateListOf(false, false, false, false, false) }
FlowRow(
Modifier.padding(horizontal = 8.dp).fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(ButtonGroupDefaults.ConnectedSpaceBetween),
verticalArrangement = Arrangement.spacedBy(2.dp),
) {
options.forEachIndexed { index, label ->
ToggleButton(
checked = checked[index],
onCheckedChange = { checked[index] = it },
shapes =
when (index) {
0 -> ButtonGroupDefaults.connectedLeadingButtonShapes()
options.lastIndex -> ButtonGroupDefaults.connectedTrailingButtonShapes()
else -> ButtonGroupDefaults.connectedMiddleButtonShapes()
},
) {
Icon(
if (checked[index]) checkedIcons[index] else unCheckedIcons[index],
contentDescription = "Localized description",
)
Spacer(Modifier.size(ToggleButtonDefaults.IconSpacing))
Text(label)
}
}
}
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun SingleSelectConnectedButtonGroupSample() {
val options = listOf("Work", "Restaurant", "Coffee")
val unCheckedIcons =
listOf(Icons.Outlined.Work, Icons.Outlined.Restaurant, Icons.Outlined.Coffee)
val checkedIcons = listOf(Icons.Filled.Work, Icons.Filled.Restaurant, Icons.Filled.Coffee)
var selectedIndex by remember { mutableIntStateOf(0) }
Row(
Modifier.padding(horizontal = 8.dp),
horizontalArrangement = Arrangement.spacedBy(ButtonGroupDefaults.ConnectedSpaceBetween),
) {
val modifiers = listOf(Modifier.weight(1f), Modifier.weight(1.5f), Modifier.weight(1f))
options.forEachIndexed { index, label ->
ToggleButton(
checked = selectedIndex == index,
onCheckedChange = { selectedIndex = index },
modifier = modifiers[index].semantics { role = Role.RadioButton },
shapes =
when (index) {
0 -> ButtonGroupDefaults.connectedLeadingButtonShapes()
options.lastIndex -> ButtonGroupDefaults.connectedTrailingButtonShapes()
else -> ButtonGroupDefaults.connectedMiddleButtonShapes()
},
) {
Icon(
if (selectedIndex == index) checkedIcons[index] else unCheckedIcons[index],
contentDescription = "Localized description",
)
Spacer(Modifier.size(ToggleButtonDefaults.IconSpacing))
Text(label)
}
}
}
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun SingleSelectConnectedButtonGroupWithFlowLayoutSample() {
val options = listOf("Work", "Restaurant", "Coffee", "Search", "Home")
val unCheckedIcons =
listOf(
Icons.Outlined.Work,
Icons.Outlined.Restaurant,
Icons.Outlined.Coffee,
Icons.Outlined.Search,
Icons.Outlined.Home,
)
val checkedIcons =
listOf(
Icons.Filled.Work,
Icons.Filled.Restaurant,
Icons.Filled.Coffee,
Icons.Filled.Search,
Icons.Filled.Home,
)
var selectedIndex by remember { mutableIntStateOf(0) }
FlowRow(
Modifier.padding(horizontal = 8.dp).fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(ButtonGroupDefaults.ConnectedSpaceBetween),
verticalArrangement = Arrangement.spacedBy(2.dp),
) {
options.forEachIndexed { index, label ->
ToggleButton(
checked = selectedIndex == index,
onCheckedChange = { selectedIndex = index },
shapes =
when (index) {
0 -> ButtonGroupDefaults.connectedLeadingButtonShapes()
options.lastIndex -> ButtonGroupDefaults.connectedTrailingButtonShapes()
else -> ButtonGroupDefaults.connectedMiddleButtonShapes()
},
modifier = Modifier.semantics { role = Role.RadioButton },
) {
Icon(
if (selectedIndex == index) checkedIcons[index] else unCheckedIcons[index],
contentDescription = "Localized description",
)
Spacer(Modifier.size(ToggleButtonDefaults.IconSpacing))
Text(label)
}
}
}
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Preview
@Composable
fun VerticalButtonGroupSample() {
val options = listOf("Button 1", "Button 2", "Button 3", "Button 4", "Button 5")
var selectedIndex by remember { mutableIntStateOf(0) }
Column(verticalArrangement = Arrangement.spacedBy((-6).dp)) {
options.forEachIndexed { index, label ->
val shape =
when (index) {
0 ->
(ButtonGroupDefaults.connectedMiddleButtonShapes().shape
as RoundedCornerShape)
.copy(topStart = CornerSize(100), topEnd = CornerSize(100))
options.lastIndex ->
(ButtonGroupDefaults.connectedMiddleButtonShapes().shape
as RoundedCornerShape)
.copy(bottomStart = CornerSize(100), bottomEnd = CornerSize(100))
else -> ButtonGroupDefaults.connectedMiddleButtonShapes().shape
}
ToggleButton(
checked = selectedIndex == index,
onCheckedChange = { selectedIndex = index },
shapes =
ToggleButtonDefaults.shapes(
shape = shape,
checkedShape = ButtonGroupDefaults.connectedButtonCheckedShape,
),
modifier = Modifier.semantics { role = Role.RadioButton },
) {
Text(label)
}
}
}
}