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

Jetpack Compose点击LazyColumn行项显示底部弹窗崩溃问题排查

崩溃原因排查与解决方案

核心崩溃原因

  1. 全局变量非法赋值导致状态混乱
    你把sheetState和sheetScope作为全局变量在ShowBottomSheetDialog中赋值,而Composable的状态必须通过remember在局部持有。全局变量会在每次重组时被覆盖,引发BottomSheet状态不一致,最终导致崩溃。

  2. 强制非空断言触发空指针异常
    sheetUser!!的使用没有确保sheetUser一定非空,在状态更新的间隙(比如showSheet先变为true,sheetUser还没完成赋值)或配置变更时,sheetUser可能为null,直接触发NPE。

  3. Composition阶段启动协程违反生命周期
    在if(showSheet)代码块中直接调用sheetScope.launch,会在每次Composable重组时重复执行,多次触发sheetState.show(),导致BottomSheet内部状态异常。

  4. BottomSheet布局结构错误
    ModalBottomSheetLayout应该作为根布局包裹所有需要显示BottomSheet的内容,而不是在条件判断中单独创建。原代码中ModalBottomSheetLayout的content为空,会破坏原有布局层级,引发渲染异常。


修复后的完整代码

修改后的SetRecyclerview函数

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun SetRecyclerview(viewModel: UserViewModel, paddingValues: PaddingValues) {
    val isLoading = viewModel.isLoading.value
    var showSheet by remember { mutableStateOf(false) }
    var sheetUser: User? by remember { mutableStateOf(null) }
    val userList = viewModel.userData.collectAsState().value

    // 局部持有BottomSheet状态和协程作用域,避免全局变量问题
    val sheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden)
    val sheetScope = rememberCoroutineScope()

    // 用LaunchedEffect监听状态变化,仅在showSheet改变时执行协程操作
    LaunchedEffect(showSheet) {
        if (showSheet) {
            sheetState.show()
        } else {
            sheetState.hide()
        }
    }

    // ModalBottomSheetLayout作为根布局,包裹所有内容
    ModalBottomSheetLayout(
        sheetState = sheetState,
        sheetContent = {
            // 安全访问sheetUser,避免空指针
            sheetUser?.let { BottomSheetItem(user = it) }
        },
        sheetBackgroundColor = Color.White,
        sheetShape = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp),
    ) {
        if (isLoading) {
            ProgressBarComponent()
        } else {
            LazyColumn(
                modifier = Modifier.padding(0.dp, 5.dp, 0.dp, 5.dp)
            ) {
                userList.forEach { user ->
                    items(user.results.size) { index ->
                        val currentUser = user.results[index]
                        EachRow(user = currentUser, onClick = {
                            sheetUser = currentUser
                            showSheet = true
                        })
                    }
                }
            }
        }
    }
}

保留原BottomSheetItem函数

//Bottom Sheet Item
@Composable
fun BottomSheetItem(user: User) {
    Log.e("TAG", "BottomSheetItem: " + user.email)   
}

关键修改说明

  • 用remember在SetRecyclerview局部持有sheetState和sheetScope,确保状态与当前Composable节点绑定,避免全局状态冲突。
  • 使用LaunchedEffect监听showSheet的变化,仅在状态变更时执行协程操作,符合Compose生命周期规则,避免重复触发。
  • ModalBottomSheetLayout作为根布局包裹所有内容,保证BottomSheet显示时不会破坏原有布局结构。
  • 用sheetUser?.let替代强制非空断言,安全访问用户数据,避免空指针异常。
  • 移除了危险的@SuppressLint("CoroutineCreationDuringComposition")注解,代码符合Compose最佳实践。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 18:40:55