docs/reference/koin-core/viewmodel.md
Koin provides multiplatform ViewModel support through the koin-core-viewmodel module. This allows you to declare and inject AndroidX ViewModel instances across all Kotlin Multiplatform targets.
Add the core ViewModel dependency:
// build.gradle.kts (commonMain)
implementation("io.insert-koin:koin-core-viewmodel:$koin_version")
For platform-specific injection APIs, add:
// Android
implementation("io.insert-koin:koin-android:$koin_version")
// Compose Multiplatform
implementation("io.insert-koin:koin-compose-viewmodel:$koin_version")
class UserViewModel(
private val repository: UserRepository
) : ViewModel()
val appModule = module {
viewModel<UserViewModel>()
}
@KoinViewModel
class UserViewModel(
private val repository: UserRepository
) : ViewModel()
val appModule = module {
// With constructor reference
viewModelOf(::UserViewModel)
// With lambda
viewModel { UserViewModel(get()) }
}
Pass parameters at injection time using @InjectedParam:
class DetailViewModel(
@InjectedParam val itemId: String,
private val repository: DetailRepository
) : ViewModel()
val appModule = module {
viewModel<DetailViewModel>()
}
@KoinViewModel
class DetailViewModel(
@InjectedParam val itemId: String,
private val repository: DetailRepository
) : ViewModel()
val appModule = module {
viewModel { params ->
DetailViewModel(
itemId = params.get(),
repository = get()
)
}
}
ViewModels that need their own scoped dependencies use the viewModelScope archetype. Dependencies declared inside viewModelScope are tied to the ViewModel's lifecycle.
val appModule = module {
viewModelScope {
scoped<UserCache>()
scoped<UserRepository>()
viewModel<UserViewModel>()
}
}
@ViewModelScope
class UserCache
@ViewModelScope
class UserRepository(private val cache: UserCache)
@KoinViewModel
@ViewModelScope
class UserViewModel(
private val repository: UserRepository
) : ViewModel()
val appModule = module {
viewModelScope {
scoped { UserCache() }
scoped { UserRepository(get()) }
viewModel { UserViewModel(get()) }
}
}
:::info
Dependencies inside viewModelScope are created when the ViewModel is first accessed and destroyed when the ViewModel is cleared.
:::
:::caution Requires the viewModelScopeFactory() option
Declaring the ViewModel itself inside viewModelScope { } (so Koin creates its scope automatically) requires enabling the viewModelScopeFactory() option in your Koin configuration:
startKoin {
options(viewModelScopeFactory())
modules(appModule)
}
Without it, resolving the ViewModel fails with:
No definition found for type 'MyViewModel' on scope '['_root_']'
because the ViewModel is registered under the ViewModel scope archetype, and that scope is only created when the option is enabled. (This is separate from the manual ScopeViewModel pattern, which creates its own scope and does not need the option.)
:::
Use koinViewModel() in Composable functions:
@Composable
fun UserScreen() {
val viewModel = koinViewModel<UserViewModel>()
// or with parameters
val detailVM = koinViewModel<DetailViewModel> { parametersOf("item_123") }
}
Use by viewModel() delegate in Activity or Fragment:
class UserActivity : AppCompatActivity() {
private val viewModel: UserViewModel by viewModel()
// With parameters
private val detailVM: DetailViewModel by viewModel { parametersOf("item_123") }
}
Add SavedStateHandle to your ViewModel constructor - Koin injects it automatically:
@KoinViewModel
class MyViewModel(
private val handle: SavedStateHandle,
private val repository: UserRepository
) : ViewModel() {
val userId: String? = handle["userId"]
}
val appModule = module {
viewModel<MyViewModel>() // Compiler Plugin DSL
// or
viewModelOf(::MyViewModel) // Classic DSL
}
| Approach | Module Declaration | Scope Declaration |
|---|---|---|
| Compiler Plugin DSL | viewModel<MyVM>() | viewModelScope { viewModel<MyVM>() } |
| Annotations | @KoinViewModel | @KoinViewModel @ViewModelScope |
| Classic DSL | viewModelOf(::MyVM) | viewModelScope { viewModelOf(::MyVM) } |
| Platform | Injection API |
|---|---|
| Compose | koinViewModel<MyVM>() |
| Android | by viewModel() |