如何在Material3 1.4.0-alpha16中用animatedWidth实现ButtonGroup宽度动画?
问题背景
Google IO 2025推出的新ButtonGroup Composable,其非废弃重载的子项lambda未提供普通@Composable作用域,直接在其中调用带动画逻辑的按钮组件会触发报错:Composable invocations may only happen from the context of a @Composable function,同时无法直接使用animatedWidth Modifier实现点击宽度变化效果。
核心解决方案
将带动画的按钮逻辑封装为独立的@Composable组件,通过外部状态驱动,在ButtonGroup的add方法中引用该组件(add的content参数支持@UiComposable调用已定义的Composable函数)。
具体实现代码
1. 封装带宽度动画的按钮组件
@Composable fun AnimatedToggleButton( isSelected: Boolean, onClick: () -> Unit, text: String ) { // 定义宽度动画状态 val animatedWidth by animateDpAsState( targetValue = if (isSelected) 120.dp else 60.dp, animationSpec = spring(stiffness = Spring.StiffnessMediumLow) ) Button( onClick = onClick, modifier = Modifier.width(animatedWidth), colors = ButtonDefaults.buttonColors( containerColor = if (isSelected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surfaceVariant ) ) { Text(text = text) } }
2. 在ButtonGroup中集成动画按钮
@Composable fun ButtonGroupWithAnimatedButtons() { // 维护选中状态 var selectedIndex by remember { mutableIntStateOf(0) } val buttonTexts = listOf("选项1", "选项2", "选项3") ButtonGroup( modifier = Modifier .safeDrawingPadding() .padding(horizontal = 4.dp) .fillMaxWidth(), overflowIndicator = {} ) { buttonTexts.forEachIndexed { index, text -> add( content = { // 调用封装好的动画按钮,传入状态 AnimatedToggleButton( isSelected = selectedIndex == index, onClick = { selectedIndex = index }, text = text ) } ) } } }
关键说明
- 动画逻辑完全封装在独立的Composable组件内,避免在ButtonGroup的非Composable作用域中直接编写动画代码。
- 通过外部的选中状态驱动按钮宽度变化,确保动画状态的管理符合Composable的状态规则。
内容的提问来源于stack exchange,提问作者BenjyTec
相关产品推荐
相关产品推荐

