kotlin-inject-viewmodel is a custom annotation processor that builds on top of
kotlin-inject-anvil to provide a way to inject ViewModels similar to
Hilt and @HiltViewModel.
Important
Only the Android and iOS targets have been implemented and tested.
Desktop targets are planned and will be implemented next.
The project requires kotlin-inject and kotlin-inject-anvil so please refer to their respective docs for setup.
For kotlin-inject-viewmodel:
dependencies {
ksp("com.teobaranga.kotlin.inject.viewmodel:compiler:$version")
implementation("com.teobaranga.kotlin.inject.viewmodel:runtime:$version")
// For Compose
implementation("com.teobaranga.kotlin.inject.viewmodel:runtime-compose:$version")
}- Plugs in nicely into existing projects using Hilt wanting to migrate to kotlin-inject without requiring a different injection strategy for ViewModels
- Compose and Activity/Fragment support
- Support all types of ViewModels, both with injected dependencies and with assisted dependencies
- Automatic injection of SavedStateHandle dependencies
Given any ViewModel, add the @ContributesViewModel annotation with a scope such as anvil's AppScope.
A ViewModel factory will be generated per scope and it will known how to create all the ViewModels contributed
to that scope.
@Inject
@ContributesViewModel(AppScope::class)
class MyViewModel(val foo: Foo) : ViewModel()Add a way to extract the generated ViewModel factory from the component.
@MergeComponent(AppScope::class)
@SingleIn(AppScope::class)
abstract class AppComponent {
@ForScope(AppScope::class)
abstract val vmFactory: ViewModelProvider.Factory
}The factory can be used where needed to override the defaults and create ViewModels through injection.
For Compose, the standard viewModel Composable API can be used with the generated factory.
val viewModel = viewModel<MyViewModel>(
factory = (application as App).appComponent.vmFactory
)For convenience, the runtime-compose dependency provides a simpler way to access ViewModels without repeating
the factory parameter. Provide a LocalViewModelFactoryOwner once at the top of the Composable tree then use the
injectedViewModel function to get a ViewModel using that factory.
CompositionLocalProvider(
// Provide a way to access the ViewModel factory to injectedViewModel
// calls down the composable tree
LocalViewModelFactoryOwner provides object : ViewModelFactoryOwner {
override val viewModelFactory: ViewModelProvider.Factory
get() = (application as App).appComponent.vmFactory
}
) {
// No explicit factory needed. Works with navigation as well.
val viewModel = injectedViewModel<MyViewModel>()
}For Activities or Fragments, you can use existing APIs from androidx.activity.viewModels or
androidx.fragment.app.viewModels, respectively.
class MyActivity : ComponentActivity() {
override val defaultViewModelProviderFactory: ViewModelProvider.Factory
get() = (application as App).appComponent.vmFactory
// Simple ViewModel
val myViewModel by viewModels<MyViewModel>()
}Assisted injection is supported, eg:
@Inject
@ContributesViewModel(
scope = AppScope::class,
assistedFactory = MyViewModel.Factory::class,
)
class MyViewModel(@Assisted val foo: Foo) : ViewModel() {
@AssistedFactory
interface Factory {
operator fun invoke(foo: Foo): MyViewModel
}
}
// Activity:
class MyActivity : ComponentActivity() {
override val defaultViewModelProviderFactory: ViewModelProvider.Factory
get() = (application as App).appComponent.vmFactory
// Assisted ViewModel
val myAssistedViewModel by viewModels<MyViewModel>(
extras = defaultViewModelCreationExtras.withCreationCallback<MyViewModel.Factory> { factory ->
factory(Foo())
},
)
}
// Compose
val myAssistedViewModel = injectedViewModel<MyViewModel, MyViewModel.Factory>(
creationCallback = { factory ->
factory(Foo())
},
)For more practical examples, see the sample app.
Kotlin Inject ViewModel is distributed under the terms of the Apache License (Version 2.0). See the license for more information.