5

I have the app and I have 2 Navigation Graphs there. First is the RootNavGraph:

@Composable
fun RootNavGraph(navController: NavHostController) {
    NavHost(navController, startDestination = RootGraphItem.Splash.route) {
        composable(route = RootGraphItem.Splash.route) {
            SplashScreen(navController = navController)
        }
        composable(route = RootGraphItem.Login.route) {
            LoginScreen(navController = navController)
        }
        composable(route = RootGraphItem.Register.route) {
            RegisterScreen(navController = navController)
        }
        composable(route = RootGraphItem.Main.route) {
            MainScreen()
        }
        composable(route = RootGraphItem.BasicInfo.route) {
            BasicInfoScreen(navController = navController)
        }
        composable(route = RootGraphItem.GenderIdentity.route) {
            GenderIdentityScreen(navController = navController)
        }
        composable(route = RootGraphItem.SexualOrientation.route) {
            SexualOrientationScreen(navController = navController)
        }
        composable(route = RootGraphItem.Pronouns.route) {
            PronounsScreen(navController = navController)
        }
        composable(route = RootGraphItem.Interests.route) {
            InterestsScreen(navController = navController)
        }
        composable(route = RootGraphItem.Description.route) {
            DescriptionScreen(navController = navController)
        }
        composable(route = RootGraphItem.Photos.route) {
            PhotosScreen(navController = navController)
        }
        composable(route = RootGraphItem.SearchingPreferences.route) {
            SearchingPreferencesScreen(navController = navController)
        }
        composable(route = RootGraphItem.AgeHeightLocation.route) {
            AgeHeightLocationScreen(navController = navController)
        }
        composable(route = RootGraphItem.WhyYouHere.route) {
            WhyYouHereScreen(navController = navController)
        }
        composable(route = RootGraphItem.Start.route) {
            StartScreen(navController = navController)
        }
        composable(route = RootGraphItem.Location.route) {
            LocationScreen(navController = navController)
        }
        composable(route = RootGraphItem.UniversityWork.route) {
            UniversityWorkScreen(navController = navController)
        }
    }
}

Second is the BottomNavGraph(for the bottom navigation bar):


@Composable
fun BottomNavGraph(navController: NavHostController) {
    NavHost(navController, startDestination = BottomNavItem.Home.screen_route) {
        composable(route = BottomNavItem.Home.screen_route){
            HomeScreen(navController)
        }
        composable(route = BottomNavItem.Chat.screen_route){
            LastChatsScreen()
        }
        composable(route = BottomNavItem.Profile.screen_route){
            ProfileScreen(navController)
        }
    }
}

I have a log out icon in a ProfileScreen from BottomNavBar:

@Composable
fun ProfileScreen(navController: NavHostController) {
    val context = LocalContext.current
    val loggedInViewModel: LoggedInViewModel = viewModel()
    var loggedOut = loggedInViewModel.getLoggedOutLiveData().observeAsState()
    if(loggedOut.value != null && loggedOut.value == true) {
        navController.navigate(RootGraphItem.Login.route) <- error occurs here
    }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.White)
            .verticalScroll(rememberScrollState())
    ) {
        Row(
            modifier = Modifier
                .padding(horizontal = 16.dp, vertical = 32.dp)
                .fillMaxWidth(),
            horizontalArrangement = Arrangement.SpaceBetween,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Text(
                text = "Your profile",
                style = TextStyle(fontSize = 32.sp, fontWeight = FontWeight.W400)
            )
                Icon(
                    modifier = Modifier
                        .size(32.dp)
                        .clickable {
                            loggedInViewModel.logOut()
                        },
                    painter = painterResource(id = R.drawable.icon_logout),
                    contentDescription = null
                )
        }
        ...
}

and after I click the log out icon, app should log out the user and navigate to the Login Screen, but it returns an error:

java.lang.IllegalArgumentException: Navigation destination that matches request NavDeepLinkRequest{ uri=android-app://androidx.navigation/login_screen } cannot be found in the navigation graph NavGraph(0x0) startDestination={Destination(0x442b361f) route=home_screen}

What is wrong with the navigation? How should I handle it?

2
  • suppose there are 2 Navhost, like bottom tab footer is there too. you cant use 1, but you have to use 2, how to do that? Commented Feb 2, 2024 at 10:02
  • Try like this: youtube.com/watch?v=FIEnIBq7Ups Commented Feb 2, 2024 at 20:13

5 Answers 5

1

You need to send a navController to RootNavGraph in startDestination or in App class. You should assign it like below before passing it;

val navController = rememberNavController()
RootNavGraph(navController)

You can handle it this way.

Sign up to request clarification or add additional context in comments.

Comments

1

Well, I guess, a workaround for this can be, when user execute the log out event instead of composable doing work of navigating to another screen you can handle this event in the viewModel of the composable screen which host Bottom Navigation. Assuming, Main Screen() is hosting the Bottom Navigation if user makes an event, viewModel of the Main screen would get notified which make MainScreen to navigate instead of the ProfileScreen().

Comments

1

You need to ensure that the screen you're trying to navigate to is under the same NavHost. In Compose navigation, you can navigate within the current navigation graph or explicitly navigate to another navigation graph's start destination.

In your case, LoginScreen and ProfileScreen are not defined under the same NavHost. To resolve this, you have two options:

  1. Move those screens around to fit them under the same NavHost.

  2. When navigating back from the profile screen, navigate to the start destination of your other graph first (RootGraphItem.Splash.route). Only after that, you'd be able to navigate to the desired destination, such as RootGraphItem.Login.route.

Comments

1

You should EITHER separate your splash flow, authentication flow, and profile flow into different navGraphs and call them in a single NavHost instead of multiple NavHosts then when you logout you can simply navigate to your auth graph OR if you must use multiple NavHosts then you must place them as they'd be nested NavHosts and your topLevel NavHost must contain your auth navGraph. I think you must separate these splash, authentication(login & register), profile flows etc. either way because with the way you implemented them rn is very hard to manage. I will drop a simple example below just to give an idea.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyAppTheme {
                RootNavGraph()
            }
        }
    }
}

@Composable
fun RootNavGraph() {
    val navController = rememberNavController()
    val navBackStackEntry by navController.currentBackStackEntryAsState()
    val isLoggedIn = hiltViewModel<SplashViewModel>().isLoggedIn.collectAsStateWithLifecycle()
    Scaffold(
        bottomBar = {
            NavigationBar {...}
        }
    ) { padding ->
        NavHost(
            modifier = Modifier.padding(padding),
            navController = navController,
            startDestination = if (isLoggedIn) "search" else "login"
        ) {
            //You can place your splash screen as a single composable here
            composable("splash") {
                SplashScreen(
                    onTimeOut = {
                         //here you can again check isLoggedIn
                         navController.navigate(...)
                    }
                )
            }
            loginNavGraph(navController)
            searchNavGraph(navController)
            profileNavGraph(navController)
        }
    }
}
//Your grahps defined like this
fun NavGraphBuilder.profileNavGraph(navController: NavController) {
    navigation(route = "profile", startDestination = "profile_page") {
                          composable("profile_page") {
            AndroidViewBinding(FragmentProfileBinding::inflate) {
                btnNavigate.setOnClickListener {
                    navController.navigate("profile_detail")
                }
            }
    }
}

Comments

1

Have you checked which NavHostController object you have passed to the ProfileScreen. It should be the RootNavHost's, otherwise you are trying to make your BottomNavHost navigate to a destination that is not registered to it.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.