Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nested navigation issue (BottomBar) #682

Open
AHuminskyi opened this issue Sep 14, 2024 · 4 comments
Open

Nested navigation issue (BottomBar) #682

AHuminskyi opened this issue Sep 14, 2024 · 4 comments
Labels
feedback needed Extra attention is needed

Comments

@AHuminskyi
Copy link

AHuminskyi commented Sep 14, 2024

Hello. I have an issue with the nested NavGraphs. Could help me to figure it out, please?
The issue:
java.lang.IllegalArgumentException: Navigation destination that matches request NavDeepLinkRequest{ uri=android-app://androidx.navigation/choose_language_screen } cannot be found in the navigation graph ComposeNavGraph(0x4392cb4c) route=bottom_bar startDestination={Destination(0x442b361f) route=home_screen}

I have this:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableRealEdgeToEdge()
        setContent {
            AppTheme {
                DestinationsNavHost(navGraph = NavGraphs.root)
            }
        }
    }
}

In scope of this MainActivity RootNavGraph I need to show the login screen, onboarding flow and other fullscreen screen.
Then, after login flow I need to open screen with the BottomAppBar.

@Composable
private fun MainScreenContent() {
    val navController = rememberNavController()
    val destinationNavigator = navController.rememberDestinationsNavigator()
    Scaffold(
        bottomBar = {
            BottomBar(
                navController = navController,
                destinationsNavigator = destinationNavigator,
            )
        }
    ) { paddingValues ->
        DestinationsNavHost(
            modifier = Modifier.padding(bottom = paddingValues.calculateBottomPadding()),
            navGraph = NavGraphs.bottomBar,
            navController = navController,
        )
    }
}

And than in some of bottom bar screens I want to open the full screen but can't do it.

@BottomBarNavGraph
@Destination
@Composable
fun ProfileScreen(
    navigator: DestinationsNavigator,
) {
    ProfileScreenContent(
        navigateToLanguageScreen = {
            // crash here
            navigator.navigate(ChooseLanguageScreenDestination)
        }
    )
}
@Destination
@Composable
fun ChooseLanguageScreen(
    modifier: Modifier = Modifier,
    onChoose: () -> Unit = { },
) {
...
}

I'm using the v1 of ComposeDestination.
Attached the screenshot for you. From the attached screen I need to open the fullscreen Language Screen.
image

I understand that I should use the Root navController because current navigator.navigate(ChooseLanguageScreenDestination) is working in scope of bottomBar but don't understand how to pass it to the ChooseLanguageScreen function correctly from the ProfileScreen that located in the bottomBar.
Thank you very much for any help!

@skawue
Copy link

skawue commented Oct 4, 2024

Problem is because ChooseLanguageScreen is not seen in NavGraphs.bottomBar. I have the same problem, did you find a solution?

@mwdch
Copy link

mwdch commented Oct 9, 2024

I have the same problem. What should I do? I am thinking of removing the destination library and using the navigation library instead.

@raamcosta
Copy link
Owner

raamcosta commented Oct 9, 2024

Hi there guys 👋

I'm sorry for not getting back to you sooner. I haven't had a lot of time these last months.

Anyway, this is a question I get at least once every couple of months, if you dig around in the closed issues you can find lots of similar questions with an answer.
@mwdch It's not related to Compose Destinations. You can use the official library and have the exact same issue.

The problem comes from the nested NavHosts and the typical misconception that NavHost is same as a NavGraph. In fact, NavHost != NavGraph. You can have a single NavHost for the whole app and still have nested graphs.

As @AHuminskyi pointed out, the problem is that each NavHost has its own navController and you cannot use one NavController to navigate to destinations of the other NavHost.
By the way, I hope you guys are using a root level NavGraph for each DestinationsNavHost (so in the example "root" and "bottomBar" should both be top level, i.e, have no parent graph).

The solution becomes clear once we know this: you need to find a way to provide the "root" navController to the "bottomBar" destinations.

What I would do:

// 👉Create a class that just holds the wanted navigator - it simplifies passing it down to other composables
// because "dependenciesContainerBuilder" uses the class type to identify what to pass where
// and it knows already the type "DestinationsNavigator", so using that directly is not a good idea
class RootDestinationsNavigator(value: DestinationsNavigator): DestinationsNavigator by value

// 👉If I understand the example well, this should also be itself a destination of "root", right? 🤔 
@RootNavGraph
@Composable
private fun MainScreenContent(val rootNavController: NavController) { // 👉Because this belongs to root, then this is root's navController
    val navController = rememberNavController() // 👉this will be bottomBar's NavController
    val destinationNavigator = navController.rememberDestinationsNavigator()
    Scaffold(
        bottomBar = {
            BottomBar(
                navController = navController,
                destinationsNavigator = destinationNavigator,
            )
        }
    ) { paddingValues ->
        DestinationsNavHost(
            modifier = Modifier.padding(bottom = paddingValues.calculateBottomPadding()),
            navGraph = NavGraphs.bottomBar,
            navController = navController,
            dependenciesContainerBuilder = {
                // 👉 pass it to all destinations of bottomBar who need it
                dependency(RootDestinationsNavigator(rootNavController.rememberDestinationsNavigator()))
            }
        )
    }
}

// 👉then on all destinations of "root" graph, you can add "RootDestinationsNavigator" as a parameter and the library will provide it:
@BottomBarNavGraph
@Destination
@Composable
fun ProfileScreen(
    rootNavigator: RootDestinationsNavigator,
    navigator: DestinationsNavigator, // 👉you can also still receive this one if needed, then use the one correspondent to the destination you're navigating to
) {
    ProfileScreenContent(
        navigateToLanguageScreen = {
            // NO crash here :)
            rootNavigator.navigate(ChooseLanguageScreenDestination)
        }
    )
}

Hope this helps! Please do let me know! 🙁

@raamcosta raamcosta added the feedback needed Extra attention is needed label Oct 9, 2024
@mwdch
Copy link

mwdch commented Oct 10, 2024

Hi @raamcosta, thank you so much for your prompt response! Your help solved my issue. I really appreciate your support and the time you took to assist me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feedback needed Extra attention is needed
Projects
None yet
Development

No branches or pull requests

4 participants