androidandroid-jetpack-composeandroid-jetpack-compose-material3material-3-expressive

How to use the Material3 Expressive ButtonGroup with the animatedWidth Modifier?


During the Google IO 2025, they presented a new ButtonGroup Composable that animates the width of the contained Buttons once they are clicked. However, the latest non-deprecated overload does not offer a Composable scope and I don't see how we can use the animatedWidth Modifier in there.

ButtonGroup(
    modifier = Modifier
        .safeDrawingPadding()
        .padding(horizontal = 4.dp)
        .fillMaxWidth(),
    overflowIndicator = {}
) {
    // Error: Composable invocations may only happen from the context of a @Composable function
    ToggleButton() { }
}

How can I implement this behavior in material3-android:1.4.0-alpha16?


Solution

  • The ButtonGroup initially presented during the I/O was deprecated in alpha-15. To now get the desired animation of width, we need to use one of the following helper functions defined within the ButtonGroupScope:

    If you want a toggle behavior of the contained buttons, you should go with toggleableItem(), and this function will apply the animatedWidth Modifier that was shown in the presentation for you.

    Please have a look at the following code:

    @OptIn(ExperimentalMaterial3ExpressiveApi::class)
    @Composable
    fun ToggleButtonGroupDemo() {
    
        val buttonTexts = listOf("Work", "Food", "Coffee")
        val buttonIcons = listOf(Icons.Outlined.Work, Icons.Outlined.Restaurant, Icons.Outlined.Coffee)
        var selectedItemIndex by remember { mutableIntStateOf(0) }
    
        ButtonGroup(
            modifier = Modifier
                .safeDrawingPadding()
                .padding(horizontal = 4.dp)
                .fillMaxWidth(),
            overflowIndicator = {}
        ) {
            buttonTexts.forEachIndexed { index, label ->
                toggleableItem(
                    weight = 1f,
                    checked = selectedItemIndex == index,
                    onCheckedChange = { selectedItemIndex = index },
                    label = label,
                    icon = {
                        Icon(imageVector = buttonIcons[index], contentDescription = "")
                    }
                )
            }
        }
    }
    

    Output:

    enter image description here