How can I have a viewmodel that persists all throughout my app? Then one that persists only one scre...
z
How can I have a viewmodel that persists all throughout my app? Then one that persists only one screen? With them both supporting backstack. I'm using this small library https://github.com/X1nto/Taxi as opposed to compose destinations and androidx navigation cause its a lot simpler and animation is super easy. I'm just not sure how to get my viewmodels to behave how I want them to.
Copy code
Surface {
    Taxi(
        modifier = Modifier.fillMaxSize(),
        navigator = navigator,
        transitionSpec = { fadeIn() with fadeOut() }
    ) { destination ->
        when (destination) {
            is AppDestination.Root -> RootScreen(navigator)
            is AppDestination.Search -> SearchScreen(navigator = navigator)
            is AppDestination.Player -> PlayerScreen(
                navigator = navigator,
                videoId = destination.videoId
            )
            is AppDestination.Channel -> ChannelScreen(
                navigator = navigator,
                channelId = destination.channelId
            )
            is AppDestination.Settings -> SettingsScreen(onClickBack = navigator::pop)
        }
    }
}
Settings, Search, Player, Channel should all be scoped to the one screen. I'm using koin for injecting my viewmodels like this
Copy code
fun SearchScreen(
    viewModel: SearchViewModel = getViewModel(),
    navigator: BackstackNavigator<AppDestination>
) {
i
That navigation library offers no support for scoping ViewModels to individual screens - the only thing it scope per screen is the saved state (i.e.,
rememberSaveable
): https://github.com/X1nto/Taxi/blob/6c5a814bd27bb0e2380052eb882cb0f564f4364b/lib/src/main/java/com/xinto/taxi/Taxi.kt#L41
z
So the library would have to add support for it? I was discussing with the developer of it and they had me convinced that the library wasn't gonna be responsible for it. I tried figuring out how to scope it to screens but I couldn't figure it out
i
You can't just tack it on after the fact, no. E.g., when they pop a screen off the back stack, that needs to be a signal to destroy the entire
ViewModelStore
associated with that screen (which would be what would clear out every ViewModel you scoped to that
ViewModelStore
). But you can't even destroy the ViewModel right there - you need to wait for your screen to actually finishing animating out (as it will continue to recompose as it exits its
AnimatedContent
with whatever transitionSpec you've defined)
And actually, I don't think that library even handles save state correctly - you need to call
saveableStateHolder.removeState
when you pop a screen off the back stack, so I'm pretty sure any screen you ever push onto that stack is going to be saved forever, even if you popped it off the stack
It probably also has the unfortunate case where if you pop a screen off the stack, then go back to it (assuming you are defining your
AppDestination
as an
object
like their example does), that instead of getting a fresh copy of your screen, you end up getting the previous, popped screen's state back...
z
Yea, I was having my screens saving data and not getting cleared. ^ exactly
i
Which, I guess if that's what you want....then having a global ViewModel that never gets cleared is at least consistent 🙃
(well, it would get cleared when the containing fragment/activity is cleared, so eventually it'll get cleaned up)
z
What I want is a global state, or some way to keep some stuff all through out my app. Since the main functionality is playing videos which would need to be accessed in the full screen player and the mini-player too. And other screens would have regular screen-scoped viewmodels
i
yeah, that's a pretty normal requirement, which is why Navigation Compose has supported that out of the box since the first alpha back in October 2020. Not sure why other navigation libraries haven't done something similar (they certainly can, nothing is stopping them - Navigation Compose uses the same public APIs that are also available to them)
d
What's wrong with having an activity scoped VM?
Unless your activity is being destroyed when you go to mini player?
z
It's all one activity
d
So that activity is global then
Scope the ViewModel to the Activity and boom you have a global ViewModel no?
z
i'm not sure how to do that
d
Pass the activity in as the lifecycle owner.
When you get the VM from the provider.
i
That's the default scope if your navigation library isn't doing anything special. I don't think that's the unsolvable problem though: it is the single screen scoped ViewModels
d
Maybe I am not understanding the problem. Can you get the state you require from a data source that is tied to the lifecycle of the Application class then?
Like a repository would be a singleton who's reference is held by the Application class.
That way all consumers get the same source of truth.
z
@Ian Lake so I'm guessing my only choice here is using androidx navigation? not really a fan of the way it handles arguments though, and strings for routes
i
Or Compose Destinations if you want to abstract those bits entirely away. The only arguments you should passing in 99% of the time are the unique IDs of what you are loading anyways
z
I was using compose destinations but its method of animations was causing me problems and had slow build times with it
i
It is pretty straightforward to write two line type safe wrappers around Navigation Compose if you don't want to take the build time speed hit of having to generate it every time, we had talked about that previously: https://kotlinlang.slack.com/archives/CJLTWPH7S/p1660233995063049?thread_ts=1660227596.881239&amp;cid=CJLTWPH7S
z
@Ian Lake found this pretty neat library that looks like it solves all the problems ive had with the other libs: https://github.com/kiwicom/navigation-compose-typed
i
That's a really slick approach, thanks for sharing it
z
it would be cool if the official library got typesafe arguments too someday
i
That's the plan, yep! Feel free to star the feature request (and maybe leave a comment linking to this new library too): https://issuetracker.google.com/issues/188693139