StevM23
StevM23

Reputation: 27

Pressing a button to navigate to another screen throws java.lang.IllegalArgumentException: Cannot navigate to NavDeepLinkRequest

I made an app for learning about Bottom Bar Navigation with Android Jetpack Compose and then decided extend it. Added LoginScreen.

In MainActivity I call function MainScreen.

@Composable
fun MainScreen() {
    val navController = rememberNavController()
    var showTopBar by rememberSaveable { mutableStateOf(true) } // to hide top bar in login screen
    var showBottomBar by rememberSaveable { mutableStateOf(true) } // to hide bottom bar in login screen
    val navBackStackEntry by navController.currentBackStackEntryAsState()

    showTopBar = when (navBackStackEntry?.destination?.route) {
        "login" -> false
        else -> true
    }

    showBottomBar = when (navBackStackEntry?.destination?.route) {
        "login" -> false
        else -> true
    }

    Scaffold(
        topBar = { if (showTopBar) MyTopAppBar() },
        bottomBar = { if (showBottomBar) BottomNavigationBar(navController) },
        content = {
            Box(modifier = Modifier.padding(it)) {
                Navigation(navController = navController)
            }
        }
    )
}

This is Navigation function.

@Composable
fun Navigation(navController: NavHostController) {
    NavHost(navController = navController, startDestination = NavigationItem.Login.route) {
        composable(NavigationItem.Lessons.route) {
            LessonsScreen()
        }
        composable(NavigationItem.Practice.route) {
            PracticeScreen()
        }
        composable(NavigationItem.Profile.route) {
            ProfileScreen()
        }
        composable(NavigationItem.Login.route) {
            LoginScreen()
        }
    }
}

Function for bottom navigation bar.

@Composable
fun BottomNavigationBar(navController: NavController) {
    val items = listOf(
        NavigationItem.Lessons,
        NavigationItem.Practice,
        NavigationItem.Profile
    )

    NavigationBar {
        val navBackStackEntry by navController.currentBackStackEntryAsState()
        val currentRoute = navBackStackEntry?.destination?.route



        items.forEach { item ->
            NavigationBarItem(
                label = { Text(item.title) },
                icon = {
                    Icon(
                        painter = painterResource(item.icon),
                        contentDescription = item.title,
                        modifier = Modifier.size(25.dp)
                    )
                },
                selected = currentRoute == item.route,
                onClick = {
                    navController.navigate(item.route)
                    {
                        navController.graph.startDestinationRoute?.let { route ->
                            popUpTo(route) {
                                saveState = true
                            }
                        }
                        launchSingleTop = true
                        restoreState = true
                    }
                },
            )
        }
    }
}

Class NavigationItem for screens as objects. Initially it was supposed for BottomNavigationBar only. Then I added object Login.

sealed class NavigationItem(
    var route: String,
    var icon: Int,
    var title: String
) {
    object Lessons : NavigationItem(
        route = "lessons",
        icon = R.drawable.book,
        title = "Lessons"
    )

    object Practice : NavigationItem(
        route = "practice",
        icon = R.drawable.practice,
        title = "Practice"
    )

    object Profile : NavigationItem(
        route = "profile",
        icon = R.drawable.user,
        title = "Profile"
    )

    object Login : NavigationItem(
        route = "login",
        icon = R.drawable.book, // can't pass null
        title = "Login"
    )
}

And function LoginSection - part of LoginScreen

@Composable
private fun LoginSection() {
    val navController = rememberNavController()
    LoginTextField(
        label = "Email",
        trailing = "",
        modifier = Modifier.fillMaxWidth()
    )

    Spacer(modifier = Modifier.height(MaterialTheme.dimens.small2))

    LoginTextField(
        label = "Password",
        trailing = "Forgot?",
        modifier = Modifier.fillMaxWidth()
    )

    Spacer(modifier = Modifier.height(MaterialTheme.dimens.small3))

    Button(
        modifier = Modifier
            .fillMaxWidth()
            .height(MaterialTheme.dimens.buttonHeight),
        onClick = {
            // problem here
            navController.navigate(NavigationItem.Lessons.route)
                  },
        colors = ButtonDefaults.buttonColors(
            containerColor = if (isSystemInDarkTheme()) BlueGray else Black,
            contentColor = Color.White
        ),
        shape = RoundedCornerShape(size = 4.dp)
    ) {
        Text(
            text = "Log in",
            style = MaterialTheme.typography.labelMedium.copy(fontWeight = FontWeight.Medium)
        )
    }
}

When I press the button "Log in" I get that:

java.lang.IllegalArgumentException: Cannot navigate to NavDeepLinkRequest{ uri=android-app://androidx.navigation/lessons }. Navigation graph has not been set for NavController androidx.navigation.NavHostController@e54b35e.

I know I have to change the code if I want to add more screens and functionality but I can't see where exactly is the problem now. Appreciate your help.

Upvotes: 0

Views: 65

Answers (1)

praveen kumar
praveen kumar

Reputation: 134

Problem is in the composable function you didn't pass the navController inside of LoginScreen()

@Composable
fun Navigation(navController: NavHostController) {
NavHost(navController = navController, startDestination = 
NavigationItem.Login.route) {
    composable(NavigationItem.Lessons.route) {
        LessonsScreen()
    }
    composable(NavigationItem.Practice.route) {
        PracticeScreen()
    }
    composable(NavigationItem.Profile.route) {
        ProfileScreen()
    }
    composable(NavigationItem.Login.route) {
        LoginScreen()
    }
 }

solution is

  @Composable
fun Navigation(navController: NavHostController) {
NavHost(navController = navController, startDestination = 
NavigationItem.Login.route) {
    composable(NavigationItem.Lessons.route) {
        LessonsScreen()
    }
    composable(NavigationItem.Practice.route) {
        PracticeScreen()
    }
    composable(NavigationItem.Profile.route) {
        ProfileScreen()
    }
    composable(NavigationItem.Login.route) {
     // pass the same instance of navcontroller ,
     // dont create new instance constructor params
        LoginScreen(navController)
    }
 }

Upvotes: 1

Related Questions