# ButtonGroup

Common/AllAndroid/JVM

## Functions summary

|---|---|---|
| `https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | `@https://developer.android.com/reference/kotlin/androidx/compose/runtime/Composable https://developer.android.com/reference/kotlin/androidx/compose/material3/ButtonGroup.composable#ButtonGroup(kotlin.Function1,androidx.compose.ui.Modifier,kotlin.Float,androidx.compose.foundation.layout.Arrangement.Horizontal,androidx.compose.ui.Alignment.Vertical,kotlin.Function1)( overflowIndicator: @https://developer.android.com/reference/kotlin/androidx/compose/runtime/Composable (https://developer.android.com/reference/kotlin/androidx/compose/material3/ButtonGroupMenuState) -> https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html, modifier: https://developer.android.com/reference/kotlin/androidx/compose/ui/Modifier, expandedRatio: @https://developer.android.com/reference/kotlin/androidx/annotation/FloatRange(from = 0.0) https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-float/index.html, horizontalArrangement: https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/Arrangement.Horizontal, verticalAlignment: https://developer.android.com/reference/kotlin/androidx/compose/ui/Alignment.Vertical, content: https://developer.android.com/reference/kotlin/androidx/compose/material3/ButtonGroupScope.() -> https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html )` A layout composable that places its children in a horizontal sequence. | Cmn |

## Functions

### ButtonGroup

Cmn Artifact: [androidx.compose.material3:material3](https://developer.android.com/jetpack/androidx/releases/compose-material3) [View Source](https://cs.android.com/search?q=file:androidx/compose/material3/ButtonGroup.kt+function:ButtonGroup)

```
@Composable
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 `https://developer.android.com/reference/kotlin/androidx/compose/material3/ButtonGroupScope#(androidx.compose.ui.Modifier).animateWidth(androidx.compose.foundation.interaction.InteractionSource)` with a relevant `https://developer.android.com/reference/kotlin/androidx/compose/foundation/interaction/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. Please also pass in a relevant maximum compression limit to `https://developer.android.com/reference/kotlin/androidx/compose/material3/ButtonGroupScope#(androidx.compose.ui.Modifier).animateWidth(androidx.compose.foundation.interaction.InteractionSource)`, so button groups can correctly calculate the maximum compression that each item can compress by; this defaults to `https://developer.android.com/reference/kotlin/androidx/compose/material3/ButtonDefaults#ContentPadding()`. 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.

Standard button group using `https://developer.android.com/reference/kotlin/androidx/compose/material3/ButtonGroupScope#clickableItem(kotlin.Function0,kotlin.String,kotlin.Function0,kotlin.Float,kotlin.Boolean)`

```kotlin
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")
    }
}
```

standard button group using `https://developer.android.com/reference/kotlin/androidx/compose/material3/ButtonGroupScope#customItem(kotlin.Function0,kotlin.Function1)` with `https://developer.android.com/reference/kotlin/androidx/compose/material3/ButtonGroupScope#(androidx.compose.ui.Modifier).animateWidth(androidx.compose.foundation.interaction.InteractionSource)`

```kotlin
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Restaurant
import androidx.compose.material.icons.filled.Work
import androidx.compose.material.icons.outlined.Home
import androidx.compose.material.icons.outlined.Restaurant
import androidx.compose.material.icons.outlined.Work
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ButtonGroup
import androidx.compose.material3.ButtonGroupDefaults
import androidx.compose.material3.DropdownMenuItem
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.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.text.style.TextOverflow

val options = listOf("Work", "Restaurant", "Home")
val unCheckedIcons = listOf(Icons.Outlined.Work, Icons.Outlined.Restaurant, Icons.Outlined.Home)
val checkedIcons = listOf(Icons.Filled.Work, Icons.Filled.Restaurant, Icons.Filled.Home)
val checked = rememberSaveable { mutableStateListOf(false, false, false) }
val interactionSources = remember { List(options.size) { MutableInteractionSource() } }
ButtonGroup(
    overflowIndicator = { menuState ->
        ButtonGroupDefaults.OverflowIndicator(menuState = menuState)
    },
    expandedRatio = 1f,
) {
    options.forEachIndexed { index, label ->
        customItem(
            buttonGroupContent = {
                val contentPadding = ButtonDefaults.ButtonWithIconContentPadding
                val layoutDirection = LocalLayoutDirection.current
                ToggleButton(
                    checked = checked[index],
                    onCheckedChange = { checked[index] = it },
                    shapes =
                        when (index) {
                            0 -> ButtonGroupDefaults.connectedLeadingButtonShapes()
                            options.lastIndex ->
                                ButtonGroupDefaults.connectedTrailingButtonShapes()
                            else -> ButtonGroupDefaults.connectedMiddleButtonShapes()
                        },
                    contentPadding = contentPadding,
                    interactionSource = interactionSources[index],
                    modifier =
                        Modifier.animateWidth(
                            interactionSource = interactionSources[index],
                            compressionLimit =
                                contentPadding.calculateEndPadding(layoutDirection),
                        ),
                ) {
                    Icon(
                        if (checked[index]) checkedIcons[index] else unCheckedIcons[index],
                        contentDescription = "Localized description",
                    )
                    Spacer(Modifier.size(ToggleButtonDefaults.IconSpacing))
                    Text(
                        text = label,
                        softWrap = false,
                        maxLines = 1,
                        overflow = TextOverflow.Visible,
                    )
                }
            },
            menuContent = {
                DropdownMenuItem(
                    leadingIcon = { checkedIcons[index] },
                    text = { Text(label) },
                    onClick = {},
                    interactionSource = interactionSources[index],
                )
            },
        )
    }
}
```

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.

```kotlin
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.FlowRow
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.ButtonDefaults
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.runtime.saveable.rememberSaveable
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 rememberSaveable { 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)
        }
    }
}
```

```kotlin
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.FlowRow
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.ButtonDefaults
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.runtime.saveable.rememberSaveable
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 = rememberSaveable { 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)
        }
    }
}
```

```kotlin
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.ButtonDefaults
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.material3.ToggleButtonShapes
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
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 rememberSaveable { 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 =
                ToggleButtonShapes(
                    shape = shape,
                    pressedShape = ToggleButtonDefaults.pressedShape,
                    checkedShape = ButtonGroupDefaults.connectedButtonCheckedShape,
                ),
            modifier = Modifier.semantics { role = Role.RadioButton },
        ) {
            Text(label)
        }
    }
}
```

| Parameters |
|---|---|
| `overflowIndicator: @https://developer.android.com/reference/kotlin/androidx/compose/runtime/Composable (https://developer.android.com/reference/kotlin/androidx/compose/material3/ButtonGroupMenuState) -> https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | composable that is displayed at the end of the button group if it needs to overflow. It receives a `https://developer.android.com/reference/kotlin/androidx/compose/material3/ButtonGroupMenuState`. |
| `modifier: https://developer.android.com/reference/kotlin/androidx/compose/ui/Modifier = Modifier` | the `https://developer.android.com/reference/kotlin/androidx/compose/ui/Modifier` to be applied to the button group. |
| `expandedRatio: @https://developer.android.com/reference/kotlin/androidx/annotation/FloatRange(from = 0.0) https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-float/index.html = 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 `https://developer.android.com/reference/kotlin/androidx/compose/material3/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: https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/Arrangement.Horizontal = ButtonGroupDefaults.HorizontalArrangement` | The horizontal arrangement of the button group's children. |
| `verticalAlignment: https://developer.android.com/reference/kotlin/androidx/compose/ui/Alignment.Vertical = Alignment.Top` | The vertical alignment of the button group's children. |
| `content: https://developer.android.com/reference/kotlin/androidx/compose/material3/ButtonGroupScope.() -> https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | the content displayed in the button group, expected to use a composable that i s tagged with `https://developer.android.com/reference/kotlin/androidx/compose/material3/ButtonGroupScope#(androidx.compose.ui.Modifier).animateWidth(androidx.compose.foundation.interaction.InteractionSource)`. |