is there any standard way of preserving the same v...
# compose
m
is there any standard way of preserving the same viewModel instance across composables navigation, for instance, BottomNavigation?
z
probably with jetpack navigation 1.4.0-alpha01 once navigation-compose starts to support the new "save backstack" argument
m
hm.. makes sense
currently im using sth like this:
Copy code
val navController = rememberNavController()

val screen1ViewModel = viewModel<Screen1ViewModel>()
val screen2ViewModel = viewModel<Screen2ViewModel>()
val screen3ViewModel = viewModel<Screen3ViewModel>()

    ...

NavHost(
        navController,
        startDestination = MainScreen.Screen1.route,
        modifier = Modifier.padding(top = 16.dp, bottom = 16.dp)
    ) {

        composable(MainScreen.Screen1.route) { Screen.Screen1(screen1ViewModel) }
        composable(MainScreen.Screen2.route) { Screen.Screen2(screen2ViewModel) }
        composable(MainScreen.Screen3.route) { Screen.Screen3(screen3ViewModel) }
    }
}
which is stupid. i want lazy viewmodel initialization
z
you can also handle the child composables inside a shared parent, and the shared parent would be the only destination known to NavHost
m
ok, thanks! I’ll wait for it theb
i ended up adding sth stupid like this
Copy code
interface LazyViewModel<T : ViewModel> {
    @Composable
    fun get(): T
}

class LazyViewModelImpl<T : ViewModel>(
    private val viewModelClass: Class<T>
) : LazyViewModel<T> {
    private var viewModel: T? = null

    @Composable
    override fun get(): T {
        check(Looper.getMainLooper() == Looper.myLooper())

        if (viewModel == null) {
            viewModel = viewModel(viewModelClass)
        }
        return viewModel!!
    }
}

@Composable
inline fun <reified T : ViewModel> lazyViewModel(): LazyViewModel<T> {
    return LazyViewModelImpl(T::class.java)
}
at least now i get lazy with same instance 😛
i don’t see how the design dictates that a BottomNavView lose viewModel state when navigating between tabs. its weird. I go to second tab and back, and expect same composable as before, not a reset
z
@myanmarking did you read the docs on bottom nav? I had the same complaint but by changing some flags in the navigate function I was able to retain the state of the view and not create a new instance of the composable
m
i read. didn’t find anything about that
there is restoreState = true, but it has to do with process death. not about that
i
ViewModels are in memory only, they'll never be a persistence layer that survives process death and recreation
But yes, Navigation 2.4.0 will keep your ViewModels in memory as you swap between bottom nav items if you use those flags, lazily creating them the first time you go to that tab
m
which flag in particular?
Copy code
restoreState?
at the moment, everytime i switch between bottomNav composables, a new viewmodel is used. That’s what i am trying to figure out if it is by design, or sth missing
i
The
saveState
and
restoreState
flags exactly as mentioned in the docs: https://developer.android.com/jetpack/compose/navigation#bottom-nav
r
What if you don't want to keep a ViewModel across different bottom nav tabs? How do you know, inside the ViewModel's
init
, which tab you're in? I can get the navigation args there, but I feel like need access to the navigation stack (of routes). ?
i
Every destination is its own
ViewModelStoreOwner
, so it has its own set of ViewModel instances. Generally, every ViewModel class should only expose the exact set of data needed to populate a single particular screen / store the data of one navigation graph, so you'd always know what destination you're on
r
Thanks. It appears my app design went astray when, for this SearchScreen, I wanted to pass a SearchQuery as an arg. It's an immutable object with many properties. I didn't see how to pass it as a nav arg (Parcelable is not allowed), and then things went downhill. It doesn't seem right to persist the query, get an ID for it, and pass the ID. How do you pass a nav arg like this?