如何在Jetpack Compose中实现FAB展开为Sheet的动效?
实现Jetpack Compose Material3风格FAB展开为选项Sheet
要实现你要的FAB展开为底部选项Sheet的效果,核心是用BottomSheetScaffold配合状态动画,以下是完整的实现代码:
import androidx.compose.animation.animateDpAsState import androidx.compose.animation.animateFloatAsState import androidx.compose.animation.core.tween import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row 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.Add import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Person import androidx.compose.material.icons.filled.Share import androidx.compose.material.icons.filled.Upload import androidx.compose.material3.BottomSheetScaffold import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.SheetState import androidx.compose.material3.Text import androidx.compose.material3.rememberBottomSheetScaffoldState import androidx.compose.material3.rememberStandardBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate import androidx.compose.ui.unit.dp @OptIn(ExperimentalMaterial3Api::class) @Composable fun FabToSheetDemo() { // 控制Sheet展开/收起的状态 var isSheetExpanded by remember { mutableStateOf(false) } // Sheet状态,设置初始隐藏 val sheetState = rememberStandardBottomSheetState( initialValue = if (isSheetExpanded) SheetState.Expanded else SheetState.Hidden ) val scaffoldState = rememberBottomSheetScaffoldState(bottomSheetState = sheetState) // FAB旋转动画:展开时旋转180度(加号变叉号) val fabRotation by animateFloatAsState( targetValue = if (isSheetExpanded) 180f else 0f, animationSpec = tween(durationMillis = 300), label = "" ) // Sheet高度过渡动画 val sheetPeekHeight by animateDpAsState( targetValue = if (isSheetExpanded) 200.dp else 0.dp, animationSpec = tween(durationMillis = 300), label = "" ) BottomSheetScaffold( scaffoldState = scaffoldState, sheetPeekHeight = sheetPeekHeight, sheetContent = { // Sheet内的选项列表 Column( modifier = Modifier .fillMaxWidth() .padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { OptionItem(icon = Icons.Default.Upload, text = "上传文件") OptionItem(icon = Icons.Default.Share, text = "分享内容") OptionItem(icon = Icons.Default.Person, text = "个人中心") } }, floatingActionButton = { FloatingActionButton( onClick = { isSheetExpanded = !isSheetExpanded }, modifier = Modifier.rotate(fabRotation) ) { Icon( imageVector = if (isSheetExpanded) Icons.Default.Close else Icons.Default.Add, contentDescription = if (isSheetExpanded) "关闭" else "打开选项" ) } } ) { innerPadding -> // 主内容区域,这里可以替换成你的页面内容 Scaffold(modifier = Modifier.padding(innerPadding)) { Column( modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Text(text = "主页面内容", style = MaterialTheme.typography.headlineMedium) } } } } @Composable fun OptionItem(icon: androidx.compose.ui.graphics.vector.ImageVector, text: String) { Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(16.dp) ) { IconButton(onClick = { /* 处理选项点击 */ }) { Icon(imageVector = icon, contentDescription = text) } Text(text = text, style = MaterialTheme.typography.bodyLarge) } }
关键实现细节:
- BottomSheetScaffold:Material3官方提供的底部Sheet脚手架,天然支持Sheet与FAB的布局关联,避免手动处理层级问题。
- 状态动画:
- 用
animateFloatAsState实现FAB图标的旋转过渡,让加号平滑转成叉号。 - 用
animateDpAsState控制Sheet的高度变化,实现从隐藏到展开的平滑过渡。
- 用
- Sheet状态管理:通过
isSheetExpanded变量切换Sheet的显示状态,配合rememberStandardBottomSheetState确保状态持久化。
你之前尝试的AnimatedContent切换FAB和Column的方式,问题在于没有利用BottomSheet的原生布局特性,导致动画和层级出现偏差,用上述方案可以完美匹配Material3官网的效果。
内容的提问来源于stack exchange,提问作者Chandresh Parmar
相关产品推荐
相关产品推荐

