Archie
11/22/2020, 3:06 PMNavHost(navController, startDestination = startRoute) {
...
composable(route) {
...
val perScreenViewModel = viewModel() // This will be different from
}
composable(route) {
...
val perScreenViewModel = viewModel() // this instance
}
...
}
val appStateViewModel = viewModel()
NavHost(navController, startDestination = startRoute) {
...
}
Bradleycorn
11/22/2020, 4:15 PM// Created a sealed class that represents your different scopes
sealed class Scope {
object Scope1: Scope()
object Scope2: Scope()
}
// Created a "ScopedViewModel" class with a boolean so we can know if it has been cleared/destroyed already.
open class ScopedViewModel: ViewModel() {
var isDestoryed: Boolean = false
private set
override fun onCleared() {
super.onCleared()
isDestoryed = true
}
}
// Setup viewmodels that inherit from ScopedViewModel
class Scope1ViewModel(): ScopedViewModel() { //... }
class Scope2ViewModel(): ScopedViewModel() { //... }
//Setup an object that keeps track of which scoped viewmodels have been created,
//and can create and return them when necessary
object MyScopedViewModels {
private val viewModels: MutableMap<Scope, ScopedViewModel> = mutableMapOf()
fun getViewModel(scope: Scope): ScopedViewModel {
val viewModel = viewModels[scope]
return when {
viewModel == null -> createViewModel(scope)
viewModel.isDestoryed -> createViewModel(scope)
else -> viewModel
}
}
private fun createViewModel(scope: Scope): ScopedViewModel {
val viewModel = when (scope) {
Scope.Scope1 -> Scope1ViewModel()
Scope.Scope2 -> Scope2ViewModel()
}
viewModels[scope] = viewModel
return viewModel
}
}
Scope
composable that gets a viewmodel and composes one of a set of screens:
NavHost(navController, startDestination = startRoute) {
...
composable("/scope1/{screen}",
arguments = listOf(navArgument("screen") { type = Nattype.StringType })
) { backStackEntry ->
...
val perScopeiewModel = viewModel() // This will be different from
val screen = backStackEntry.arguments?.getString("screen") ?: "Screen1"
Scope1(perScopeViewModel, screen)
}
composable(route) {
...
val perScreenViewModel = viewModel() // this instance
}
...
}
....
@Composable
fun Scope1(viewModel: ViewModel, screen: String) {
when (screen) {
"Screen1" -> Screen1(viewModel)
"Screen2" -> Screen2(viewModel)
"Screen3" -> Screen3(viewModel)
else -> throw IllegalArgumentException("Screen $screen is not part of Scope 1")
}
Still not great, but maybe workable.
Also, 1.0.0-alpha02 of the navigation component supports nested nav graphs. I haven’t used them yet, but that might offer a way to do something where you can create the viewmodel in the higherlevel compose()
for the scope, and then pass it along to each nested screen. But again, I haven’t used it yet, so can’t say if or how well that would work.allan.conda
11/22/2020, 6:24 PMflorent
11/22/2020, 6:55 PMMark Murphy
11/23/2020, 1:32 PMnickbutcher
11/23/2020, 4:05 PMBradleycorn
11/23/2020, 4:05 PMIan Lake
11/23/2020, 4:48 PM