Scoped ViewModel Compose
With scoped-viewmodel-compose you can tie the lifecycle of a ViewModel to a Composable.
When to use Scoped ViewModels
ViewModels can outlive the lifecycle of their owners, typically an Activity or a Fragment. When your app grows and you want to show a larger number of Composables on a single screen or navigate between several screens within a single Fragment your ViewModels won't be cleaned up at the right time. The Fragment or Activity scope becomes too big for your ViewModel. Scoped ViewModels solve this problem.
Consider using Jetpack Compose Navigation
Before you start using this library check out Jetpacks navigation library Navigating with Compose. The library creates a new ViewModelStore for each route destination and clears the ViewModels when you navigate away. If you don't want to use navigation-compose, or you have your own implementation for navigation, scoped-viewmodel-compose can be a great option for you. You can also use them in combination. Internally they both use a similar approach.
How to bind the ViewModel lifecycle to Compose
Creating a new ViewModel scope
To create a new ViewModel for the current Composables lifecycle use the CreateScope Composable. The ViewModel will survive configuration changes, but will be cleared when the Composable is detached.
@Composable
fun MyComposable() {
CreateScope { vm: MyViewModel ->
// Use your vm here
}
}
Retrieving a scoped ViewModel
In any child Composable use scopedViewModel to retrieve a previously created ViewModel.
@Composable
fun ChildComposable() {
val vm = scopedViewModel<MyViewModel>()
// Your composable here ...
}
Lifecycle of scoped ViewModels
The ViewModels survive configuration changes and the Composable will automatically retrieve the same instance again. It will be cleared when the Composable is detached.
Nesting ViewModel Scopes
You can nest scopes created with CreateScope to create different ViewModels. If you use it to create ViewModels of the same class scopedViewModel will always return the ViewModel that was created closest to the call in the Composable tree.
@Composable
fun MyComposable() {
CreateScope { outer: MyViewModel ->
Column {
CreateScope { inner: MyViewModel
val vm: MyViewModel = scopedViewModel()
// inner MyViewModel instance
assertEquals(vm, inner)
}
val vm: MyViewModel = scopedViewModel()
// outer MyViewModel instance
assertEquals(vm, outer)
}
}
}