Search code examples
androidandroid-jetpack-composeandroid-jetpack-navigation

ModalNavigationDrawer not navigating to another screen after clicking an item in ModalDrawerSheet


I'm having an issue with ModalNavigationDrawer. It is supposed to navigate to another screen after clicking an item in the ModalDrawerSheet, but it does nothing. Here are my files:

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")
}

Navigation.kt:

@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()
                })
        }
    }
}

NavigationItem.kt:

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() { [...]
            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) { [...] }

I have tried to change the route type from "string" to actual "route" type in NavigationItem.kt and make the needed changes in the TaskListViewModel.kt:

replaced sendUiEvent(UiEvent.Navigate(event.navigationItem.route)) with sendUiEvent(UiEvent.Navigate(event.navigationItem.route.toString()))

but nothing seemed to work anyway. Do you have any ideas on how to make it work?


Solution

  • When you write TaskListEvent.OnDrawerNavigationClick(item), you're creating an event, but you're not actually doing anything with that event - it never makes it to your viewModel.uiEvent flow to actually get processed. You'll need to connect these two pieces together if you want to process the event from the click.