ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Follow publication

Place Scope Handling on Auto-Pilot with Koin & Compose Navigation

Mihai Batista
ProAndroidDev
Published in
4 min readOct 4, 2024

--

Photo by Jigar Panchal on Unsplash

Scope & Navigation Definition

const val ROUTE_GRAPH_FLOW: NavGraphRoute = "route_graph_flow"

/**
* Navigation graph of the custom flow.
*/
fun NavGraphBuilder.flowNavGraph(
navController: NavController,
) {
navigation(
startDestination = routeScreen1.getRouteWithPlaceholders(),
route = ROUTE_GRAPH_FLOW,
) {
screen1(
onNextClick = {
navController.navigateToScreen2()
}
)
screen2(
onFinishFlowClick = {
navController.navigateToStart()
}
)
}
}
val scopeModule = module {

/**
* Dependencies of [ROUTE_GRAPH_FLOW] scope.
*/
scope(named(ROUTE_GRAPH_FLOW)) {

viewModel {
Screen1ViewModel(
flowRepository = get(),
)
}

// add other viewModels

scoped<FlowRepository> {
FlowRepositoryImpl(
authRepository = get()
)
}

// add other repositories, use cases, DAOs
}
}

Injecting Scoped Dependencies

val scope = koinInstance.getOrCreateScope(
scopeId = "route_graph_flow",
qualifier = named("route_graph_flow")
)

val viewModel: ScopedViewModel = koinViewModel(
scope = scope
)

The solution

🔓 Key API: addOnDestinationChangedListener gives us the current destination (screen) and implicitly the nested nav graph (flow) that the destination is part of.

if (currentNavGraphRoute != null) {
val scopeForCurrentNavGraphRoute = koinInstance.getOrCreateScope(
scopeId = currentNavGraphRoute,
qualifier = named(currentNavGraphRoute)
)
scopeToInject = scopeForCurrentNavGraphRoute
} else {
scopeToInject = rootScope
}

.....

CompositionLocalProvider(
LocalKoinScope provides scopeToInject,
content = content
)
val currentNavGraphRoute = destination.parent?.route
val previousNavGraphRoute = lastKnownNavGraphRoute

if (previousNavGraphRoute != null && currentNavGraphRoute != previousNavGraphRoute) {
val lastScope = koinInstance.getOrCreateScope(
scopeId = previousNavGraphRoute,
qualifier = named(previousNavGraphRoute)
)
lastScope.close()
}
var scopeToInject by remember {
val lastKnownNavGraphRoute = lastKnownNavGraphRoute
mutableStateOf(
value = if (lastKnownNavGraphRoute != null) {
koinInstance.getOrCreateScope(
scopeId = lastKnownNavGraphRoute,
qualifier = named(lastKnownNavGraphRoute)
)
} else {
rootScope
}
)
}

--

--

Published in ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Written by Mihai Batista

Senior Android Engineer at Accenture Song Berlin

Responses (3)