ModalNavigationDrawer打开时按返回键未关抽屉而是返回上页的问题
我使用Jetpack Compose和Compose Destinations开发Android应用,应用中包含ModalNavigationDrawer。该抽屉可通过手势正常开合,但当抽屉处于打开状态时,按下返回键无法关闭抽屉,而是会最小化应用或返回上一页面。推测是Compose Destinations拦截了返回操作并优先处理回退栈,但初步查阅文档未找到相关解决方法,Stack Overflow上的拦截方案又较为繁琐,想咨询是否有官方推荐的简洁合规解决方案。
主应用代码
@Composable fun App() { val navEngine = rememberNavHostEngine() val navController = navEngine.rememberNavController() val startRoute = HomeRouteDestination Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { DestinationsNavHost( engine = navEngine, navController = navController, navGraph = NavGraphs.root, startRoute = startRoute ) } }
抽屉组件代码
private data class DrawerItem( val icon: ImageVector, val name: String, val destination: DirectionDestination ) @OptIn(ExperimentalMaterial3Api::class) @Composable fun NavDrawerWrapper( navigator: DestinationsNavigator = EmptyDestinationsNavigator, content: @Composable (() -> Unit, () -> Unit) -> Unit ) { val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) val scope = rememberCoroutineScope() val openDrawer: () -> Unit = { scope.launch { drawerState.open() } } val closeDrawer: () -> Unit = { scope.launch { drawerState.close() } } val items = listOf( DrawerItem(Icons.Default.List, "Menu", HomeRouteDestination), DrawerItem(Icons.Default.Settings, "Settings", SettingsRouteDestination) ) var selectedItem by remember { mutableStateOf(items[0]) } ModalNavigationDrawer( drawerState = drawerState, drawerContent = { DrawerSheet( items, selectedItem, onSelectItem = { item -> closeDrawer() selectedItem = item navigator.navigate(item.destination) } ) }, content = { content(openDrawer, closeDrawer) } ) } @OptIn(ExperimentalMaterial3Api::class) @Composable private fun DrawerSheet( items: List<DrawerItem> = listOf(), selectedItem: DrawerItem? = null, onSelectItem: (DrawerItem) -> Unit = {} ) { ModalDrawerSheet { LazyColumn(modifier = Modifier.padding(horizontal = 15.dp, vertical = 20.dp)) { items(items.size) { index -> val item = items[index] NavigationDrawerItem( label = { Text(text = item.name) }, icon = { Icon(imageVector = item.icon, contentDescription = item.name) }, selected = item == selectedItem, onClick = { onSelectItem(item) } ) } } } }
官方推荐解决方案
使用Compose官方提供的BackHandlerAPI即可简洁解决这个问题,无需复杂的拦截逻辑。BackHandler专门用于自定义返回键行为,当抽屉处于打开状态时,拦截返回键并执行关闭抽屉的操作,优先级高于Compose Destinations的回退栈处理。
修改后的NavDrawerWrapper代码
只需在NavDrawerWrapper中添加一行BackHandler代码:
@OptIn(ExperimentalMaterial3Api::class) @Composable fun NavDrawerWrapper( navigator: DestinationsNavigator = EmptyDestinationsNavigator, content: @Composable (() -> Unit, () -> Unit) -> Unit ) { val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) val scope = rememberCoroutineScope() val openDrawer: () -> Unit = { scope.launch { drawerState.open() } } val closeDrawer: () -> Unit = { scope.launch { drawerState.close() } } // 添加这行代码拦截返回键 BackHandler(enabled = drawerState.isOpen) { closeDrawer() } val items = listOf( DrawerItem(Icons.Default.List, "Menu", HomeRouteDestination), DrawerItem(Icons.Default.Settings, "Settings", SettingsRouteDestination) ) var selectedItem by remember { mutableStateOf(items[0]) } ModalNavigationDrawer( drawerState = drawerState, drawerContent = { DrawerSheet( items, selectedItem, onSelectItem = { item -> closeDrawer() selectedItem = item navigator.navigate(item.destination) } ) }, content = { content(openDrawer, closeDrawer) } ) }
原理说明
BackHandler的enabled参数控制是否激活拦截:当drawerState.isOpen为true(抽屉打开)时,拦截返回键事件。- 拦截后执行
closeDrawer()方法,通过协程关闭抽屉,此时不会触发Compose Destinations的回退栈操作,完全符合官方推荐的自定义返回行为规范。
内容的提问来源于stack exchange,提问作者stijndcl
相关产品推荐
相关产品推荐

