Jetpack Compose Bottom Sheet Drawer自动打开问题修复咨询
问题描述
我有如下Compose组件:
@Composable fun ProductDetails(viewModel: ProductDetailsViewModel) { val productInstalmentsByOcrName by viewModel.productInstalmentsByOcrName.collectAsStateWithLifecycle() val product by viewModel.product.collectAsStateWithLifecycle() val scope = rememberCoroutineScope() val drawerState = rememberBottomDrawerState(BottomDrawerValue.Closed) BottomDrawer( drawerState = drawerState, drawerContent = { ReportModalContent(ocrProductNames = productInstalmentsByOcrName?.map { it.first } ?: emptyList(), onReport = { ocrProductName -> viewModel.onReport(ocrProductName) scope.launch { drawerState.close() } }) }, ) { // .... 其余代码... IconButton( onClick = { scope.launch { drawerState.open() } }, modifier = Modifier.align(Alignment.TopEnd) ) { // ... 其余代码 }
该组件对应应用内一个页面,导航进入时首次渲染抽屉处于关闭状态(drawerState.isOpen == false),但随即无需用户点击打开按钮,组件重绘后抽屉自动打开。
代码中未在其他位置操作drawerState,曾怀疑与Navigation组件有关,但设为初始页面仍出现该问题;排查过其他页面相同BottomDrawer的干扰,注释相关代码后问题依旧。
附导航相关代码:
文件 NavigationComposable.kt
fun Navigation(viewModel: NavigationViewModel = koinViewModel()) { val navController = rememberNavController() val startDestination by viewModel.startRoute.collectAsStateWithLifecycle() NavHost(navController = navController, startDestination = startDestination) { composable(Routes.AUTH) { AuthScreen(navController = navController) } composable(Routes.PRODUCT_LIST) { ProductList(navController = navController) } composable( Routes.PRODUCT_DETAILS, arguments = listOf(navArgument(Routes.PRODUCT_ID_ARG) { type = NavType.IntType }) ) { val productId = it.arguments?.getInt(Routes.PRODUCT_ID_ARG) ProductDetails(viewModel = getViewModel { parametersOf(productId) }) } } }
文件 NavigationViewModel.kt
class NavigationViewModel(authRepository: AuthRepository) : ViewModel() { val startRoute =authRepository.isLoggedIn.map { if (it) Routes.PRODUCT_LIST else Routes.AUTH }.stateIn(viewModelScope, SharingStarted.Eagerly, Routes.AUTH) }
修复方案
1. 改用rememberSaveable保存抽屉状态
当前drawerState使用remember创建,组件重组时可能出现状态意外重置或错误恢复。使用rememberSaveable搭配BottomDrawerState.Saver,能确保状态在配置变更、组件重组时稳定保留:
val drawerState = rememberSaveable(saver = BottomDrawerState.Saver) { BottomDrawerState(BottomDrawerValue.Closed) }
2. 过滤流的重复数据
检查productInstalmentsByOcrName和product的状态流,若频繁发送重复值会导致组件不必要的重组,间接触发抽屉状态异常。在ViewModel中给流添加distinctUntilChanged()过滤重复数据:
// 在ProductDetailsViewModel中修改对应流 val productInstalmentsByOcrName = yourSourceFlow.distinctUntilChanged() val product = anotherSourceFlow.distinctUntilChanged()
3. 初始化时强制关闭抽屉
通过LaunchedEffect在组件首次渲染后强制关闭抽屉,确保初始状态稳定:
LaunchedEffect(Unit) { drawerState.close() }
4. 检查子组件的焦点请求
排查ReportModalContent内部是否有自动获取焦点的控件(如输入框),这类控件的requestFocus()操作可能间接触发抽屉打开。移除或禁用子组件的自动焦点逻辑。
内容的提问来源于stack exchange,提问作者Adrian Pascu
相关产品推荐
相关产品推荐

