raamcosta / compose-destinations

Annotation processing library for type-safe Jetpack Compose navigation with no boilerplate.
https://composedestinations.rafaelcosta.xyz
Apache License 2.0
3.23k stars 134 forks source link

How to handle mutual navigation between multiple modules? #670

Closed sceneren closed 3 months ago

sceneren commented 3 months ago

I have 3 modules, AppModule, AModule, BModule,

@NavHostGraph()
annotation class MainGraph {
    @ExternalNavGraph<ANavGraph>
    @ExternalNavGraph<BNavGraph>
    companion object Includes
}

@NavGraph<ExternalModuleGraph>
internal annotation class AGraph
@NavGraph<ExternalModuleGraph>
internal annotation class BGraph

AGraph contains 3 screens: A1Screen, A2Screen, A3Screen, BGraph contains 3 screens: B1Screen, B2Screen, B3Screen, Now I need to navigate from A1Screen--->B1Screen--->B2Screen-->A2Screen, I can now navigate through navigator.navigate(routeStr), but the parameter can only be of String type. I don't know how to implement ScreenDestination so that it can be used in all modules

raamcosta commented 3 months ago

Hi 👋

Module A doesn't know about module B, so to navigate to B, you need to do it on a place that knows both A and B - that place is the main module. What you do is you pass to module A some lambdas or an implementation of module A's navigator interface that can do it. Example:


// module A

interface ModuleAExternalNavigator { // calling it "External" since it is meant to navigate to external modules.
     fun goToB1Screen(b1ScreenNavArg1: Arg1Type)
}

// main module

class ModuleAExternalNavigatorImpl(
    navigator: DestinationsNavigator
) {
    fun goToB1Screen(b1ScreenNavArg1: Arg1Type) {
        navitator.navigate(B1ScreenDestination(b1ScreenNavArg1))
    }
}

Then you pass the implementation of that Navigator to module A, via one of these ways documented here:

https://composedestinations.rafaelcosta.xyz/v2/multi-module-setup#receive-navhost-parameters

You can also find more in-depth information there!

Let me know if this helps!

sceneren commented 3 months ago

Thanks for your help!!! This is very useful, but it is a bit cumbersome to use. Can you support navigation by carrying parameters through routes? I think this is a very common scenario.

raamcosta commented 3 months ago

Having feature modules is always going to add some of this. It’s a tradeoff you choose to make when adding modules.

You can already do it technically, but I would strongly discourage it because your A module doesn’t depend on B module so it doesn’t know anything about it. If you use B’s route like that you add an implicit dependency which will be a point you’ll have to keep in mind (and all your team) when module B changes, forever.

The other approach may be a bit of boilerplate but it’s typesafe and explicit, it also takes 5min to write so it’s not an actual problem 🙂