优化ModalBottomSheetLayout底部弹窗高度,解决切换时跳动问题
问题原因
你遇到的弹窗跳动问题,核心是依赖onGloballyPositioned动态获取TopAppBar高度的时机滞后:调用openSheet时,你立刻计算sumHeight并显示弹窗,但此时TopAppBar可能还没完成布局,topAppBarHeight还是初始的Dp.Unspecified,导致弹窗先以错误高度显示,等onGloballyPositioned触发后高度更新,就会出现跳动。
优化方案
方案1:提前固定TopAppBar高度,避免动态计算
Material组件的TopAppBar有明确的默认高度(比如标准SmallTopAppBar是56.dp),可以直接用常量代替动态获取,彻底消除计算滞后问题:
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden) val scope = rememberCoroutineScope() // 直接使用TopAppBar的标准高度,无需动态获取 val topAppBarHeight = 56.dp var currentSheet by remember { mutableStateOf(0) } val openSheet: (sheet: Int) -> Unit = { currentSheet = it scope.launch { sheetState.show() } } val closeSheet: () -> Unit = { scope.launch { sheetState.hide() } } ModalBottomSheetLayout( modifier = Modifier.fillMaxSize(), sheetState = sheetState, sheetContent = { Scaffold( // 移除手动设置的height,让内容自适应,ModalBottomSheet会自动计算高度 topBar = { TopAppBar( modifier = Modifier.clickable(onClick = closeSheet), title = { Text("TopAppBarSheetContent") } ) }, content = { padding -> Box( modifier = Modifier .padding(padding) // 加上Scaffold的content padding .height((currentSheet * 50).dp) .fillMaxWidth() .background(Color.Blue) ) }, ) }, content = { Scaffold( modifier = Modifier.fillMaxSize(), topBar = { TopAppBar(title = { Text("TopAppBarContent") }) }, content = { innerPadding -> LazyColumn( modifier = Modifier.padding(innerPadding), contentPadding = PaddingValues(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { items(50) { item -> Box( modifier = Modifier .clip(CircleShape) .fillMaxWidth() .height(75.dp) .background(Color.Red) .clickable { openSheet(item) } ) } } } ) } )
方案2:让内容自适应高度,完全放弃手动计算
如果TopAppBar高度可能动态变化(比如自定义样式),可以让sheetContent的内容自己决定高度,ModalBottomSheet会自动测量并使用正确的高度,无需手动计算总和:
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden) val scope = rememberCoroutineScope() var currentSheet by remember { mutableStateOf(0) } val openSheet: (sheet: Int) -> Unit = { currentSheet = it scope.launch { sheetState.show() } } val closeSheet: () -> Unit = { scope.launch { sheetState.hide() } } ModalBottomSheetLayout( modifier = Modifier.fillMaxSize(), sheetState = sheetState, sheetContent = { Scaffold( topBar = { TopAppBar( modifier = Modifier.clickable(onClick = closeSheet), title = { Text("TopAppBarSheetContent") } ) }, content = { padding -> Box( modifier = Modifier .padding(padding) .height((currentSheet * 50).dp) .fillMaxWidth() .background(Color.Blue) ) }, ) }, content = { Scaffold( modifier = Modifier.fillMaxSize(), topBar = { TopAppBar(title = { Text("TopAppBarContent") }) }, content = { innerPadding -> LazyColumn( modifier = Modifier.padding(innerPadding), contentPadding = PaddingValues(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { items(50) { item -> Box( modifier = Modifier .clip(CircleShape) .fillMaxWidth() .height(75.dp) .background(Color.Red) .clickable { openSheet(item) } ) } } } ) } )
关键改进点
- 移除了
onGloballyPositioned动态获取高度的逻辑,避免布局回调滞后导致的高度计算错误 - 去掉了手动设置
sumHeight的代码,让ModalBottomSheet自动测量内容高度,从根源上解决跳动问题 - 保留了原有的业务逻辑,只是优化了高度计算的方式
内容的提问来源于stack exchange,提问作者Sky
相关产品推荐
相关产品推荐

