Navigation in a modular Android project
Even though AAC’s Navigation Component seems to be able to handle navigation in modular android project, for those who would like to implement it in a more old-fashion way, I would try to provide some thoughts on how to do it.
On app structure

As basis of architecture we’ll pick feature-per-module principle with some base modules (like Core and Core UI) and delegate navigation between features to Mediator module.
Core
In the core module we will define our flows as Kotlin’s sealed class to achieve the goal of transparency in what data we have to pass to another flow.
Also here, in core, we’ll define Mediator interface in the following way:
And finally we’ll make some additions to Cicerone’s SupportFragmentNavigator to make it able to handle flows as separate unit. (In case you haven’t heard about this great library, then you should definitely check it out!)
And then core module DI will look like this:
Here I use Koin as DI framework, which I find the most concise way to show DI in article cause it’s synthax is self-explanatory and you don’t have to be a Dagger Master to understand how dependencies are provided somewhere. But you can of course use framework of your choice.
Mediator
In the mediator module we’ll define couple of interfaces which will determine a contract by which features will provide entry points to them.
Also we’ll implement Mediator interface.
And finally Mediator module’s DI:
Core UI
Moving on to Core UI, here we’ll stop to define Base Activity in following way:
Main Flow
Finally let’s see how it will work inside some feature module, main flow, for example. First we’ll implement MainFlowIntentProvider like this:
This will provide a place where Mediator will seek for Intent for this specific flow. Here getFlowIntentFor is a simple extension function to cover basic Android Intent creation.
And, finally to make it all work we need to override Navigator inside of our MainActivity
So, now, when we’re inside this flow and we’ll call router to start new flow we’ll go to this place, ask mediator to find intent by flow and then mediator will call some specific flow intent provider implementation to get this flow-based intent that we’re looking for. To make it all work, last thing we need is provide our MainFlowIntentProviderImpl.
That’s it. By adding this Mediator layer we can achieve separation of features so that they don’t know about each other and we can navigate between them by using this Flow concept in the way where we can, for example, force user of our feature to pass some input data without which we aren’t able to start our flow.
Thanks for reading till the end. I hope this article will be usefull for you in some way.