I’m currently migrating our code to use `compose-navigation`. Are there any guidelines currently abo...
a
I’m currently migrating our code to use
compose-navigation
. Are there any guidelines currently about multi-module with this? Let’s say I have two feature modules, what’s a good way to navigate screens between them without depending on each other? I’m thinking to put hoist the actual navigation into the top-level navigation (where the NavHost is), I wonder if I should be putting all those instances in there, which would be a lot for a highly-modular application.
And with this approach I’m not sure how to handle cases where a use case (executed from the ViewModel) needs to trigger a navigation. For example, a button press executes a use case, and if it executes successfully the navigation proceeds. I have tried tracking the current navigation as state then trigger the navigation in a
SideEffect(currentDestination)
, but I get problems of failure to navigate to the same route because the
SideEffect
doesn’t run again if it’s the same route.
i
Well, there's two parts to your question: how to build your graph and how to navigate. When it comes to building your graph, I think it is important to realize that each module can provide its own graph (say, in the form of a
NavGraphBuilder.moduleNameGraph(NavController)
extension) that your main module calls when it constructs the
NavHost
- that way, the logic on the internal structuring of your graph is totally contained within that module. This is the same as using an
<include>
in the XML based world: https://developer.android.com/guide/navigation/navigation-nested-graphs#include When to comes to navigating itself, the indirection through routes already decouples any compile time dependency between modules - any destination can
navigate("profile/$userId")
and have that work.
As explained in the testing section of the docs (https://developer.android.com/jetpack/compose/navigation#testing), you really want to decouple your screens themselves, ViewModels, etc. from Navigation, instead using lambdas from where you are building your graph (which would ideally be the only place that has access to the
NavController
).
If you need to do some work from an onClick before proceeding, you might consider using
rememberCoroutineScope
- then your button press could trigger a suspending operation, check the result, then call your navigate lambda only if needed: https://developer.android.com/jetpack/compose/kotlin#coroutines
The nice part with that approach is that is automatically cancels if the user has navigated away, etc.
a
Thank you! I should note that I'm migrating from the Fragment Navigator. I did translate the <include> into nested navigation() graphs so that's nice. About the routes, I missed that they are decoupled since I had defined the routes as sealed classes within their feature modules, And I can't access their routes, but it would actually work if I hardcode it like in your example. I could try putting these routes in a shared module, what do you think about that idea?
In our current approach we did try to decouple navigation away from composables, but coupled with the ViewModel with interfaces like onProfileButtonClick(). Do you mean I could try something like this?
Copy code
Button(
  onClick = {
     coroutineScope.launch {
        if (viewModel.onProfileClick()) {
           navigateToUserProfile()
        }
     }
  }
)
i
If your architecture has imposed a compile time dependency (since Navigation has not imposed any such dependency), then that would be your call on how you want to handle that
👍 1
Yeah, that would be one way to handle clicks that need to do some work before actually doing something. The other solution would indeed be a
LaunchedEffect
collecting on a Channel backed Flow suitable for single events: https://elizarov.medium.com/shared-flows-broadcast-channels-899b675e805c#49e3