如何无动画关闭Jetpack Compose的ModalBottomSheetLayout?
实现无动画关闭ModalBottomSheet并流畅切换内容
方案一:复用同一个BottomSheet切换内部内容(推荐)
与其关闭再打开新的BottomSheet,直接复用现有弹窗、切换内部内容是最流畅的实现方式,视觉上完全是同一个弹窗的内容更新,没有任何切换间隙。
@Composable @OptIn(ExperimentalMaterialApi::class) fun ModalBottomSheetSample() { val state = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden) val scope = rememberCoroutineScope() // 用状态变量控制当前显示的弹窗内容 var currentSheetContent by remember { mutableStateOf(0) } ModalBottomSheetLayout( sheetState = state, sheetContent = { when(currentSheetContent) { 0 -> SheetContentFirstPage(onSwitch = { currentSheetContent = 1 }) 1 -> SheetContentSecondPage(onSwitch = { currentSheetContent = 0 }) } } ) { Button(onClick = { scope.launch { state.show() } }) { Text("打开弹窗") } } } // 封装不同的弹窗内容组件 @Composable fun SheetContentFirstPage(onSwitch: () -> Unit) { Column(modifier = Modifier.padding(16.dp)) { Text(text = "第一页内容", fontSize = 18.sp) Spacer(modifier = Modifier.height(24.dp)) Button(onClick = onSwitch) { Text("切换到第二页") } } } @Composable fun SheetContentSecondPage(onSwitch: () -> Unit) { Column(modifier = Modifier.padding(16.dp)) { Text(text = "第二页内容", fontSize = 18.sp) Spacer(modifier = Modifier.height(24.dp)) Button(onClick = onSwitch) { Text("回到第一页") } } }
方案二:修改状态动画,实现无动画关闭
如果必须要关闭当前弹窗再打开新的,可以通过自定义animationSpec,让弹窗关闭时瞬间完成,用户几乎感知不到关闭过程,从而模拟内容切换的流畅感。
@Composable @OptIn(ExperimentalMaterialApi::class) fun ModalBottomSheetSample() { // 自定义动画规则:关闭时用无动画,打开时保留默认滑动动画 val state = rememberModalBottomSheetState( initialValue = ModalBottomSheetValue.Hidden, animationSpec = object : AnimationSpec<Float> { override fun animateTo( initialValue: Float, targetValue: Float, initialVelocity: Float, animationScope: AnimationScope<Float> ): AnimationResult<Float> { // 判断目标状态是否为隐藏,是则用InstantSpec完成无动画切换 return if (targetValue == ModalBottomSheetValue.Hidden.requireOffset()) { InstantSpec.animateTo(initialValue, targetValue, initialVelocity, animationScope) } else { SwipeableDefaults.AnimationSpec.animateTo(initialValue, targetValue, initialVelocity, animationScope) } } } ) val scope = rememberCoroutineScope() var isNewContent by remember { mutableStateOf(false) } ModalBottomSheetLayout( sheetState = state, sheetContent = { if (isNewContent) { Text(text = "新弹窗内容") } else { Column { Text(text = "原弹窗内容") Button(onClick = { scope.launch { // 无动画关闭 state.hide() // 切换内容状态后立即打开 isNewContent = true state.show() } }) { Text("切换新内容") } } } } ) { Button(onClick = { scope.launch { state.show() } }) { Text("打开弹窗") } } }
注意事项
- 优先选择方案一,因为完全不需要状态切换,内容切换的流畅度最高,也更符合Compose的状态驱动设计理念。
- 方案二中,
requireOffset()用于获取对应状态的偏移值,确保动画判断准确;如果需要全局统一无动画关闭,可以封装自定义的rememberModalBottomSheetState方法。
内容的提问来源于stack exchange,提问作者Rainmaker
相关产品推荐
相关产品推荐

