ButtonGroup

Functions summary

Unit
@Composable
@ExperimentalMaterial3ExpressiveApi
ButtonGroup(
    modifier: Modifier,
    expandedRatio: @FloatRange(from = 0.0) Float,
    horizontalArrangement: Arrangement.Horizontal,
    content: @Composable ButtonGroupScope.() -> Unit
)

This function is deprecated. Please use the overload with overflowIndicator parameter.

Cmn
Unit
@Composable
@ExperimentalMaterial3ExpressiveApi
ButtonGroup(
    overflowIndicator: @Composable (ButtonGroupMenuState) -> Unit,
    modifier: Modifier,
    expandedRatio: @FloatRange(from = 0.0) Float,
    horizontalArrangement: Arrangement.Horizontal,
    verticalAlignment: Alignment.Vertical,
    content: ButtonGroupScope.() -> Unit
)

A layout composable that places its children in a horizontal sequence.

Cmn

Functions

@Composable
@ExperimentalMaterial3ExpressiveApi
fun ButtonGroup(
    modifier: Modifier = Modifier,
    expandedRatio: @FloatRange(from = 0.0) Float = ButtonGroupDefaults.ExpandedRatio,
    horizontalArrangement: Arrangement.Horizontal = ButtonGroupDefaults.HorizontalArrangement,
    content: @Composable ButtonGroupScope.() -> Unit
): Unit

A layout composable that places its children in a horizontal sequence. When a child uses ButtonGroupScope.animateWidth with a relevant MutableInteractionSource, this button group can listen to the interactions and expand the width of the pressed child element as well as compress the neighboring child elements.

import androidx.compose.material3.ButtonGroup
import androidx.compose.material3.ButtonGroupDefaults
import androidx.compose.ui.Alignment

val numButtons = 10
ButtonGroup(
    overflowIndicator = { menuState ->
        ButtonGroupDefaults.OverflowIndicator(menuState = menuState)
    },
    verticalAlignment = Alignment.Top,
) {
    for (i in 0 until numButtons) {
        clickableItem(onClick = {}, label = "$i")
    }
}

A connected button group is a variant of a button group that have leading and trailing buttons that are asymmetric in shape and are used to make a selection.

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Coffee
import androidx.compose.material.icons.filled.Restaurant
import androidx.compose.material.icons.filled.Work
import androidx.compose.material.icons.outlined.Coffee
import androidx.compose.material.icons.outlined.Restaurant
import androidx.compose.material.icons.outlined.Work
import androidx.compose.material3.ButtonGroup
import androidx.compose.material3.ButtonGroupDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.ToggleButton
import androidx.compose.material3.ToggleButtonDefaults
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp

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)
        }
    }
}
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Coffee
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Restaurant
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Work
import androidx.compose.material.icons.outlined.Coffee
import androidx.compose.material.icons.outlined.Home
import androidx.compose.material.icons.outlined.Restaurant
import androidx.compose.material.icons.outlined.Search
import androidx.compose.material.icons.outlined.Work
import androidx.compose.material3.ButtonGroup
import androidx.compose.material3.ButtonGroupDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.ToggleButton
import androidx.compose.material3.ToggleButtonDefaults
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp

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)
        }
    }
}
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Coffee
import androidx.compose.material.icons.filled.Restaurant
import androidx.compose.material.icons.filled.Work
import androidx.compose.material.icons.outlined.Coffee
import androidx.compose.material.icons.outlined.Restaurant
import androidx.compose.material.icons.outlined.Work
import androidx.compose.material3.ButtonGroup
import androidx.compose.material3.ButtonGroupDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.ToggleButton
import androidx.compose.material3.ToggleButtonDefaults
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

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)
        }
    }
}
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Coffee
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Restaurant
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Work
import androidx.compose.material.icons.outlined.Coffee
import androidx.compose.material.icons.outlined.Home
import androidx.compose.material.icons.outlined.Restaurant
import androidx.compose.material.icons.outlined.Search
import androidx.compose.material.icons.outlined.Work
import androidx.compose.material3.ButtonGroup
import androidx.compose.material3.ButtonGroupDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.ToggleButton
import androidx.compose.material3.ToggleButtonDefaults
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

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)
        }
    }
}
Parameters
modifier: Modifier = Modifier

the Modifier to be applied to the button group.

expandedRatio: @FloatRange(from = 0.0) Float = ButtonGroupDefaults.ExpandedRatio

the percentage, represented by a float, of the width of the interacted child element that will be used to expand the interacted child element as well as compress the neighboring children. By Default, standard button group will expand the interacted child element by ButtonGroupDefaults.ExpandedRatio of its width and this will be propagated to its neighbors. If 0f is passed into this slot, then the interacted child element will not expand at all and the neighboring elements will not compress. If 1f is passed into this slot, then the interacted child element will expand to 200% of its default width when pressed.

horizontalArrangement: Arrangement.Horizontal = ButtonGroupDefaults.HorizontalArrangement

The horizontal arrangement of the button group's children.

content: @Composable ButtonGroupScope.() -> Unit

the content displayed in the button group, expected to use a Material3 component or a composable that is tagged with Modifier.interactionSourceData.

@Composable
@ExperimentalMaterial3ExpressiveApi
fun ButtonGroup(
    overflowIndicator: @Composable (ButtonGroupMenuState) -> Unit,
    modifier: Modifier = Modifier,
    expandedRatio: @FloatRange(from = 0.0) Float = ButtonGroupDefaults.ExpandedRatio,
    horizontalArrangement: Arrangement.Horizontal = ButtonGroupDefaults.HorizontalArrangement,
    verticalAlignment: Alignment.Vertical = Alignment.Top,
    content: ButtonGroupScope.() -> Unit
): Unit

A layout composable that places its children in a horizontal sequence. When a child uses ButtonGroupScope.animateWidth with a relevant MutableInteractionSource, this button group can listen to the interactions and expand the width of the pressed child element as well as compress the neighboring child elements. Additionally, items will overflow into a dropdown menu if there are too many items or the items are too wide to all fit onto the screen.

import androidx.compose.material3.ButtonGroup
import androidx.compose.material3.ButtonGroupDefaults
import androidx.compose.ui.Alignment

val numButtons = 10
ButtonGroup(
    overflowIndicator = { menuState ->
        ButtonGroupDefaults.OverflowIndicator(menuState = menuState)
    },
    verticalAlignment = Alignment.Top,
) {
    for (i in 0 until numButtons) {
        clickableItem(onClick = {}, label = "$i")
    }
}

A connected button group is a variant of a button group that have leading and trailing buttons that are asymmetric in shape and are used to make a selection.

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Coffee
import androidx.compose.material.icons.filled.Restaurant
import androidx.compose.material.icons.filled.Work
import androidx.compose.material.icons.outlined.Coffee
import androidx.compose.material.icons.outlined.Restaurant
import androidx.compose.material.icons.outlined.Work
import androidx.compose.material3.ButtonGroup
import androidx.compose.material3.ButtonGroupDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.ToggleButton
import androidx.compose.material3.ToggleButtonDefaults
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp

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)
        }
    }
}
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Coffee
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Restaurant
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Work
import androidx.compose.material.icons.outlined.Coffee
import androidx.compose.material.icons.outlined.Home
import androidx.compose.material.icons.outlined.Restaurant
import androidx.compose.material.icons.outlined.Search
import androidx.compose.material.icons.outlined.Work
import androidx.compose.material3.ButtonGroup
import androidx.compose.material3.ButtonGroupDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.ToggleButton
import androidx.compose.material3.ToggleButtonDefaults
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp

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)
        }
    }
}
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Coffee
import androidx.compose.material.icons.filled.Restaurant
import androidx.compose.material.icons.filled.Work
import androidx.compose.material.icons.outlined.Coffee
import androidx.compose.material.icons.outlined.Restaurant
import androidx.compose.material.icons.outlined.Work
import androidx.compose.material3.ButtonGroup
import androidx.compose.material3.ButtonGroupDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.ToggleButton
import androidx.compose.material3.ToggleButtonDefaults
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

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)
        }
    }
}
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Coffee
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Restaurant
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Work
import androidx.compose.material.icons.outlined.Coffee
import androidx.compose.material.icons.outlined.Home
import androidx.compose.material.icons.outlined.Restaurant
import androidx.compose.material.icons.outlined.Search
import androidx.compose.material.icons.outlined.Work
import androidx.compose.material3.ButtonGroup
import androidx.compose.material3.ButtonGroupDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.ToggleButton
import androidx.compose.material3.ToggleButtonDefaults
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

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)
        }
    }
}
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ButtonGroup
import androidx.compose.material3.ButtonGroupDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.ToggleButton
import androidx.compose.material3.ToggleButtonDefaults
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp

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)
        }
    }
}
Parameters
overflowIndicator: @Composable (ButtonGroupMenuState) -> Unit

composable that is displayed at the end of the button group if it needs to overflow. It receives a ButtonGroupMenuState.

modifier: Modifier = Modifier

the Modifier to be applied to the button group.

expandedRatio: @FloatRange(from = 0.0) Float = ButtonGroupDefaults.ExpandedRatio

the percentage, represented by a float, of the width of the interacted child element that will be used to expand the interacted child element as well as compress the neighboring children. By Default, standard button group will expand the interacted child element by ButtonGroupDefaults.ExpandedRatio of its width and this will be propagated to its neighbors. If 0f is passed into this slot, then the interacted child element will not expand at all and the neighboring elements will not compress. If 1f is passed into this slot, then the interacted child element will expand to 200% of its default width when pressed.

horizontalArrangement: Arrangement.Horizontal = ButtonGroupDefaults.HorizontalArrangement

The horizontal arrangement of the button group's children.

verticalAlignment: Alignment.Vertical = Alignment.Top

The vertical alignment of the button group's children.

content: ButtonGroupScope.() -> Unit

the content displayed in the button group, expected to use a composable that i s tagged with ButtonGroupScope.animateWidth.