You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose:ModalBottomSheet动画卡顿问题求助

Jetpack Compose Material3 ModalBottomSheet 动画卡顿问题排查与优化

问题原因分析

  1. 状态管理混乱:MainScreen用布尔值直接控制MyBottomSheet的创建/销毁,同时MyBottomSheet内部又自行管理modalBottomSheetState并调用show(),双重控制导致组件频繁重组,打断动画流程。
  2. 焦点请求时机错误:MyTextField在组件首次组合就请求焦点,此时BottomSheet正处于显示动画阶段,软键盘弹出抢占UI线程资源,引发卡顿。
  3. 冗余状态操作:MyBottomSheet内部监听isVisible并手动调用show()属于重复操作,ModalBottomSheet会根据sheetState自动处理显示逻辑,手动调用反而干扰动画。

优化方案及代码修改

1. 统一状态管理,避免组件频繁销毁重建

将modalBottomSheetState提升到MainScreen中统一管理,直接通过状态控制BottomSheet的显示/隐藏,不再用布尔值控制组件的创建销毁,让ModalBottomSheet始终处于组合树中。

2. 延迟焦点请求,等待动画完成

在MyTextField中监听BottomSheet的状态,仅当BottomSheet完全展开后再请求焦点,避免软键盘弹出打断动画。

3. 移除冗余状态控制代码

删除MyBottomSheet内部自行调用show()的逻辑,由外部统一控制状态。


修改后的MainScreen.kt

@OptIn(ExperimentalMaterial3Api::class)
@Preview(showBackground = true)
@Composable
fun MainScreen() {
    val modalBottomSheetState = rememberModalBottomSheetState(
        initialValue = ModalBottomSheetValue.Hidden
    )
    val coroutineScope = rememberCoroutineScope()
    
    ModalBottomSheet(
        onDismissRequest = {
            coroutineScope.launch {
                modalBottomSheetState.hide()
            }
        },
        sheetState = modalBottomSheetState,
        dragHandle = { BottomSheetDefaults.DragHandle() },
        content = {
            MyTextField(sheetState = modalBottomSheetState)
        }
    )

    Box(
        modifier = Modifier
            .padding(all = 8.dp)
            .fillMaxSize()
    ) {
        MyFloatingActionButton(
            onClick = {
                coroutineScope.launch {
                    modalBottomSheetState.show()
                }
            },
            modifier = Modifier
                .align(Alignment.BottomEnd)
                .padding(bottom = 80.dp)
                .padding(end = 10.dp)
        )
    }
}

修改后的MyTextField.kt

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyTextField(
    modifier: Modifier = Modifier,
    sheetState: ModalBottomSheetState
) {
    var text by remember { mutableStateOf("") }
    val focusRequester = remember { FocusRequester() }

    TextField(
        value = text,
        onValueChange = { text = it },
        label = { Text("Label") },
        modifier = modifier
            .fillMaxWidth()
            .focusRequester(focusRequester)
    )

    // 等待BottomSheet完全展开后再请求焦点
    LaunchedEffect(sheetState.currentValue) {
        if (sheetState.currentValue == ModalBottomSheetValue.Expanded) {
            focusRequester.requestFocus()
        }
    }
}

简化后的MyBottomSheet.kt(可选)

如果需要保留封装,可以简化为纯内容组件,不再管理状态:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyBottomSheet(
    modifier: Modifier = Modifier,
    sheetState: ModalBottomSheetState,
    onDismiss: () -> Unit
) {
    ModalBottomSheet(
        onDismissRequest = onDismiss,
        sheetState = sheetState,
        dragHandle = { BottomSheetDefaults.DragHandle() },
        modifier = modifier,
        content = {
            MyTextField(sheetState = sheetState)
        }
    )
}

额外优化建议

  • 若BottomSheet内有复杂组件,使用remember或derivedStateOf减少不必要的重组。
  • 检查动画执行时是否有UI线程耗时操作(如网络请求、大量计算),将此类操作移至IO线程。
  • 极端卡顿情况下,可尝试给Activity添加android:hardwareAccelerated="false"(仅作为最后手段)。

内容的提问来源于stack exchange,提问作者Ali Soltani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 17:43:10