Reputation: 2099
I currently have two NavGraphs
in my app, one for authentication routes, like login and registration and one for the home routes when logged in.
I currently need to pass the navController
as a parameter in every composable when I want to use it. That is definitely not scalable and so I'm wondering if it's possible to make it injectable so that I can just request it through a DI framework like Koin
. I'm currently using Koin
in my App so it would be good if I can just use it's dependency injection tools, to inject a navController
as a singleton
.
I currently create the navController
inside the MainActifity
:
class MainActivity : AppCompatActivity() {
private lateinit var navController: NavHostController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startKoin {
androidLogger(if (BuildConfig.DEBUG) Level.ERROR else Level.NONE)
androidContext(this@MainActivity)
modules(listOf(networkModule, viewModelModule, interactorsModule))
}
setContent {
navController = rememberAnimatedNavController()
SetupNavGraph(navController = navController)
}
}
}
I then pass it into the NavHost
that holds the two NavGraphs
:
fun SetupNavGraph(
navController: NavHostController
) {
AnimatedNavHost(
navController = navController,
startDestination = AUTH_GRAPH_ROUTE,
route = ROOT_GRAPH_ROUTE
) {
homeNavGraph(navController = navController)
authNavGraph(navController = navController)
}
}
Below is my NavGraph
for all authentication routes. I also pass the NavController
as a parameter so that I can use it inside the screen.
fun NavGraphBuilder.authNavGraph(
navController: NavController
) {
navigation(
startDestination = Screen.Login.route,
route = AUTH_GRAPH_ROUTE
) {
composable(
Screen.Login.route,
) {
val loginViewModel: LoginViewModel = getViewModel()
LoginScreen(
navController = navController,
state = loginViewModel.state.value,
onTriggerEvent = loginViewModel::onTriggerEvent
)
}
composable(
Screen.Register.route,
) {
val registerViewModel: RegisterViewModel = getViewModel()
RegisterScreen(
navController = navController,
state = registerViewModel.state.value,
onTriggerEvent = registerViewModel::onTriggerEvent
)
}
}
}
The NavGraph
for all pages when logged in is similar to my auth one, so not worth showing.
It would be cool if I can just create a navController
as a single
through Koin but that is not possible because rememberNavController
function can only be called inside a Composable.
Is there any solution in solving this problem of passing the navController
into every nested composable?
Upvotes: 5
Views: 5894
Reputation: 87894
For easy testing/previewing, it's recommended to pass only handlers instead of the nav controller itself, and do all real navigation inside SetupNavGraph
when a handler is called. More info can be found here.
If it doesn't suits you, you can create your own compositional local, like this:
val LocalNavController = compositionLocalOf<NavHostController> {
error("No LocalNavController provided")
}
Provide it with CompositionLocalProvider
:
setContent {
CompositionLocalProvider(LocalNavController provides rememberAnimatedNavController()) {
SetupNavGraph()
}
}
And then in any composable inside CompositionLocalProvider
, you can get your nav controller with LocalNavController.current
.
Upvotes: 10