Koin dependency injection integration with Jetpack Compose for Kotlin Multiplatform development.
—
Type-safe dependency resolution functions that integrate seamlessly with Compose recomposition, supporting parameters, qualifiers, and custom scopes for flexible dependency injection patterns.
Resolve Koin dependency without parameters using reified generics for type safety.
/**
* Resolve Koin dependency without parameters
* @param qualifier Dependency qualifier (optional)
* @param scope Koin scope (default: current scope)
* @return Instance of type T
*/
@Composable
inline fun <reified T> koinInject(
qualifier: Qualifier? = null,
scope: Scope = currentKoinScope()
): TUsage Examples:
@Composable
fun UserScreen() {
// Basic injection
val repository: UserRepository = koinInject()
val apiService: ApiService = koinInject()
// With qualifier
val cacheRepo: UserRepository = koinInject(qualifier = named("cache"))
val networkRepo: UserRepository = koinInject(qualifier = named("network"))
// With custom scope
val sessionService: SessionService = koinInject(scope = getKoin().getScope("session"))
LaunchedEffect(Unit) {
val users = repository.getAllUsers()
// Use injected dependencies
}
}Resolve Koin dependency with parameters using a lambda function. Note that parameters are unwrapped on recomposition, which may impact performance.
/**
* Resolve Koin dependency with parameters (unwraps to ParametersHolder on recomposition)
* @param qualifier Dependency qualifier (optional)
* @param scope Koin scope (default: current scope)
* @param parameters Injected parameters with lambda
* @return Instance of type T
* @note Parameters unwrapped on recomposition - use ParametersHolder version for better performance
*/
@Composable
inline fun <reified T> koinInject(
qualifier: Qualifier? = null,
scope: Scope = currentKoinScope(),
noinline parameters: ParametersDefinition
): TUsage Examples:
@Composable
fun DetailScreen(userId: String) {
// Inject with parameters - unwrapped on each recomposition
val userService: UserService = koinInject { parametersOf(userId) }
val userProfile: UserProfile = koinInject(
qualifier = named("detailed")
) { parametersOf(userId, true) }
// Use the injected services
LaunchedEffect(userId) {
val user = userService.getUser()
// ...
}
}Resolve Koin dependency with ParametersHolder for better performance by avoiding parameter unwrapping on recomposition.
/**
* Resolve Koin dependency with ParametersHolder (better performance)
* @param qualifier Dependency qualifier (optional)
* @param scope Koin scope (default: current scope)
* @param parametersHolder Parameters (use parametersOf(), no lambda)
* @return Instance of type T
*/
@Composable
inline fun <reified T> koinInject(
qualifier: Qualifier? = null,
scope: Scope = currentKoinScope(),
parametersHolder: ParametersHolder
): TUsage Examples:
@Composable
fun OptimizedDetailScreen(userId: String) {
// Better performance - parameters not unwrapped on recomposition
val userParams = remember(userId) { parametersOf(userId) }
val userService: UserService = koinInject(parametersHolder = userParams)
val detailedParams = remember(userId) { parametersOf(userId, true) }
val userProfile: UserProfile = koinInject(
qualifier = named("detailed"),
parametersHolder = detailedParams
)
// Use the injected services
}When injecting dependencies with parameters, choose the appropriate method based on performance requirements:
@Composable
fun PerformanceExample(id: String) {
// ❌ Poor performance - unwrapped on every recomposition
val service1: MyService = koinInject { parametersOf(id) }
// ✅ Better performance - parameters remembered
val params = remember(id) { parametersOf(id) }
val service2: MyService = koinInject(parametersHolder = params)
}Inject from appropriate scopes to optimize memory usage and lifecycle management:
@Composable
fun ScopeExample() {
// Application-level singleton
val appService: AppService = koinInject()
// Session-scoped instance
val sessionScope = getKoin().getScope("session")
val sessionService: SessionService = koinInject(scope = sessionScope)
// Feature-scoped instance
val featureScope = getKoin().getScope("feature")
val featureService: FeatureService = koinInject(scope = featureScope)
}The injection functions use reified generics to maintain full type safety:
@Composable
fun TypeSafetyExample() {
// Type inferred automatically
val stringRepo: Repository<String> = koinInject()
val userRepo: Repository<User> = koinInject()
// With qualifiers for different implementations
val cacheRepo: Repository<User> = koinInject(qualifier = named("cache"))
val dbRepo: Repository<User> = koinInject(qualifier = named("database"))
}Dependency injection functions will throw exceptions if dependencies cannot be resolved:
NoBeanDefFoundException: When the requested dependency is not definedBeanCreationException: When there's an error creating the bean instanceUnknownKoinContext: When Koin context is not properly initialized@Composable
fun SafeInjection() {
try {
val service: MyService = koinInject()
// Use service
} catch (e: NoBeanDefFoundException) {
// Handle missing dependency
} catch (e: BeanCreationException) {
// Handle creation error
}
}Injected dependencies work seamlessly with Compose state and effects:
@Composable
fun IntegrationExample() {
val repository: DataRepository = koinInject()
var data by remember { mutableStateOf<List<Data>?>(null) }
var loading by remember { mutableStateOf(false) }
LaunchedEffect(repository) {
loading = true
try {
data = repository.loadData()
} finally {
loading = false
}
}
// UI based on injected data
if (loading) {
CircularProgressIndicator()
} else {
LazyColumn {
items(data.orEmpty()) { item ->
DataItem(item)
}
}
}
}Inject ViewModels and other state holders for MVVM patterns:
@Composable
fun ViewModelExample() {
val viewModel: UserViewModel = koinInject()
val uiState by viewModel.uiState.collectAsState()
LaunchedEffect(Unit) {
viewModel.loadUsers()
}
UserList(
users = uiState.users,
onUserClick = viewModel::selectUser
)
}parametersOf() calls in remember with appropriate keysInstall with Tessl CLI
npx tessl i tessl/maven-io-insert-koin--koin-compose-jvm