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

ModalNavigationDrawer点击抽屉项后无法跳转页面的问题排查

问题:ModalNavigationDrawer菜单点击后页面无法跳转

问题描述

使用Jetpack Compose的ModalNavigationDrawer组件时,点击Drawer内的菜单项目后,页面没有触发跳转逻辑。以下是相关代码片段:

TaskListEvent.kt

sealed class TaskListEvent {
    data class OnDeleteTask(val task: Task) : TaskListEvent()
    object OnUndoDeleteTask : TaskListEvent()
    data class OnEditClick(val task: Task) : TaskListEvent()
    object OnAddTask : TaskListEvent()
    class OnDrawerNavigationClick(val navigationItem: NavigationItem) : TaskListEvent()
}

Routes.kt

sealed class Routes(val route: String) {
    object TASKSLIST : Routes(route = "tasks_list")
    object ADDEDITTASK : Routes(route = "add_edit_task")
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@Composable
fun Navigation(navController: NavHostController) {
    NavHost(navController = navController, startDestination = Routes.TASKSLIST.route) {
        composable(
            Routes.TASKSLIST.route
        ) {
            TasksListScreen(onNavigate = {
                navController.navigate(it.route)
            })
        }
        composable(
            route = Routes.ADDEDITTASK.route + "?taskId={taskId}",
            arguments = listOf(navArgument(name = "taskId") {
                type = NavType.IntType
                defaultValue = -1
            })
        ) {
            AddEditScreen(
                onPopBackStack = {
                    navController.popBackStack()
                })
        }
    }
}
data class NavigationItem(
    val title: String,
    val selectedIcon: ImageVector,
    val unselectedIcon: ImageVector,
    val route: String
)

val items = listOf(
    NavigationItem(
        title = "Planner",
        selectedIcon = Icons.Default.Done,
        unselectedIcon = Icons.Default.Done,
        route = Routes.TASKSLIST.route
    ),
    NavigationItem(
        title = "Library",
        selectedIcon = Icons.Default.Email,
        unselectedIcon = Icons.Default.Email,
        route = Routes.ADDEDITTASK.route
    )
)

TaskListViewModel.kt

@HiltViewModel
class TaskListViewModel @Inject constructor(private val repository: TaskRepositoryImplementation) :
    ViewModel() { 
        // 省略其他代码...
        fun onEvent(event: TaskListEvent) {
            when(event) {
                // 其他事件处理...
                is TaskListEvent.OnDrawerNavigationClick -> {
                    sendUiEvent(UiEvent.Navigate(event.navigationItem.route))
                }
            }
        }

        private fun sendUiEvent(event: UiEvent) {
            viewModelScope.launch(Dispatchers.IO) {
                _uiEvent.send(event)
            }
        }
    }

TasksList.kt

@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TasksListScreen(
    modifier: Modifier = Modifier,
    onNavigate: (UiEvent.Navigate) -> Unit,
    viewModel: TaskListViewModel = hiltViewModel()
) {
    val tasks = viewModel.tasks.collectAsState(initial = emptyList())
    val snackbarHostState = remember { SnackbarHostState() }
    val coroutineScope = rememberCoroutineScope()
    val snackBarController = SnackBarController(coroutineScope)
    LaunchedEffect(key1 = true) {
        viewModel.uiEvent.collectLatest { event ->
            when (event) {
                // 省略其他事件处理...
                is UiEvent.Navigate -> {
                    onNavigate(event)
                }
                else -> Unit
            }
        }
    }
    val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
    var selectedItemIndex by rememberSaveable {
        mutableIntStateOf(0)
    }
    ModalNavigationDrawer(drawerContent = {
        ModalDrawerSheet {
            items.forEachIndexed { index, item ->
                Spacer(modifier = Modifier.height(12.dp))
                NavigationDrawerItem(
                    label = { Text(item.title) },
                    selected = index == selectedItemIndex,
                    onClick = {
                        TaskListEvent.OnDrawerNavigationClick(item)
                        selectedItemIndex = index
                        coroutineScope.launch {
                            drawerState.close()
                        }
                    },
                    icon = {
                        Icon(
                            imageVector = if (index == selectedItemIndex) {
                                item.selectedIcon
                            } else item.unselectedIcon, contentDescription = item.title
                        )
                    },
                    modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding))
            }
        }
    }, drawerState = drawerState) { 
        // 省略页面主体代码...
    }
}

问题原因

核心问题出在TasksList.kt的NavigationDrawerItem的onClick回调中:你只是创建了TaskListEvent.OnDrawerNavigationClick(item)事件对象,但没有将该事件传递给ViewModel的事件处理方法(比如onEvent),导致ViewModel无法触发后续的导航逻辑。

解决方案

  1. 修改TasksList.kt中的onClick回调,调用ViewModel的onEvent方法发送事件:
onClick = {
    viewModel.onEvent(TaskListEvent.OnDrawerNavigationClick(item)) // 关键:将事件传递给ViewModel
    selectedItemIndex = index
    coroutineScope.launch {
        drawerState.close()
    }
},
  1. 额外检查点:
    • 确保UiEvent.Navigate数据类正确定义,持有路由字符串:
      sealed class UiEvent {
          data class Navigate(val route: String) : UiEvent()
          // 其他事件...
      }
      
    • 跳转到ADDEDITTASK路由时,因为该路由注册时带有taskId参数(默认值-1),当前NavigationItem中的route为"add_edit_task",由于参数有默认值,导航会正常工作;如果需要显式传参,可以将NavigationItem的route改为"add_edit_task?taskId=-1",或者在ViewModel中拼接参数后再发送导航事件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 13:04:51