Jetpack Compose integration for Koin dependency injection framework providing Compose-specific APIs for dependency injection
—
Functions for injecting dependencies into Composable functions with type safety and performance optimization through Compose's remember system. The koinInject function provides three overloads to handle different parameter scenarios.
Resolve Koin dependency for a given type without parameters.
/**
* Resolve Koin dependency for given Type T
*
* @param qualifier - dependency qualifier
* @param scope - Koin's root by default
* @return instance of type T
*/
@Composable
inline fun <reified T> koinInject(
qualifier: Qualifier? = null,
scope: Scope = currentKoinScope()
): TUsage Examples:
import org.koin.compose.koinInject
@Composable
fun UserScreen() {
// Basic injection
val userRepository: UserRepository = koinInject()
// With qualifier
val apiClient: ApiClient = koinInject(qualifier = named("public"))
// With specific scope
val sessionService: SessionService = koinInject(scope = someSpecificScope)
// Use the injected dependencies
LaunchedEffect(Unit) {
val users = userRepository.getUsers()
// Handle users...
}
}Resolve Koin dependency with parameters provided via lambda. Note: This version unwraps parameters to ParametersHolder and will be triggered on recomposition.
/**
* Resolve Koin dependency for given Type T
*
* Note: this version unwrap parameters to ParametersHolder in order to let remember all parameters
* This parameters unwrap will be triggered on recomposition
*
* For better performances we advise to use koinInject(Qualifier,Scope,ParametersHolder)
*
* @param qualifier - dependency qualifier
* @param scope - Koin's root by default
* @param parameters - injected parameters (with lambda & parametersOf())
* @return instance of type T
*/
@Composable
inline fun <reified T> koinInject(
qualifier: Qualifier? = null,
scope: Scope = currentKoinScope(),
noinline parameters: ParametersDefinition
): TUsage Examples:
import org.koin.compose.koinInject
import org.koin.core.parameter.parametersOf
@Composable
fun UserDetailScreen(userId: String) {
// Inject with parameters using lambda
val userService: UserService = koinInject {
parametersOf(userId, "detailed")
}
// More complex parameters
val analyticsService: AnalyticsService = koinInject(
qualifier = named("user_analytics")
) {
parametersOf(userId, System.currentTimeMillis())
}
// Use the services
LaunchedEffect(userId) {
val user = userService.getUser()
analyticsService.trackUserView(user)
}
}Resolve Koin dependency with pre-created parameters. This approach provides better performance as parameters are not unwrapped on each recomposition.
/**
* Resolve Koin dependency for given Type T
*
* @param qualifier - dependency qualifier
* @param scope - Koin's root by default
* @param parametersHolder - parameters (used with parametersOf(), no lambda)
* @return instance of type T
*/
@Composable
inline fun <reified T> koinInject(
qualifier: Qualifier? = null,
scope: Scope = currentKoinScope(),
parametersHolder: ParametersHolder
): TUsage Examples:
import org.koin.compose.koinInject
import org.koin.core.parameter.parametersOf
import androidx.compose.runtime.remember
@Composable
fun UserDetailScreen(userId: String) {
// Create parameters holder once
val userParams = remember(userId) {
parametersOf(userId, "detailed")
}
// Inject with parameters holder (better performance)
val userService: UserService = koinInject(
parametersHolder = userParams
)
// Multiple services with same parameters
val userCache: UserCache = koinInject(
qualifier = named("memory_cache"),
parametersHolder = userParams
)
// Use the services
LaunchedEffect(userId) {
val user = userService.getUser()
userCache.store(user)
}
}All koinInject functions use Compose's remember internally for performance optimization:
// Internal implementation uses remember for caching
return remember(qualifier, scope) {
scope.get(T::class, qualifier)
}
// With parameters, both qualifier, scope, and parameters are remembered
return remember(qualifier, scope, parametersHolder) {
scope.getWithParameters(T::class, qualifier, parametersHolder)
}For best performance with parameters:
// ❌ Poor performance - parameters recreated on every recomposition
val service: Service = koinInject { parametersOf(userId, timestamp) }
// ✅ Better performance - parameters remembered
val params = remember(userId) { parametersOf(userId, timestamp) }
val service: Service = koinInject(parametersHolder = params)import org.koin.core.qualifier.named
@Composable
fun ApiScreen() {
val publicApi: ApiClient = koinInject(qualifier = named("public"))
val adminApi: ApiClient = koinInject(qualifier = named("admin"))
val internalApi: ApiClient = koinInject(qualifier = named("internal"))
}import org.koin.core.qualifier.StringQualifier
@Composable
fun DatabaseScreen() {
val readOnlyDb = koinInject<Database>(qualifier = StringQualifier("readonly"))
val writeDb = koinInject<Database>(qualifier = StringQualifier("write"))
}By default, koinInject uses the current composition's scope:
@Composable
fun MyScreen() {
// Uses currentKoinScope() by default
val service: MyService = koinInject()
}@Composable
fun ScopedComponent() {
val customScope = remember { getKoin().createScope<MyScope>("custom_id") }
val scopedService: ScopedService = koinInject(scope = customScope)
DisposableEffect(customScope) {
onDispose {
customScope.close()
}
}
}// Core parameter types
class ParametersHolder
typealias ParametersDefinition = () -> ParametersHolder
// Qualifier types
interface Qualifier
class StringQualifier(val value: String) : Qualifier
fun named(name: String): Qualifier
// Scope types
interface Scope {
fun <T : Any> get(clazz: KClass<T>, qualifier: Qualifier?): T
fun <T : Any> getWithParameters(clazz: KClass<T>, qualifier: Qualifier?, parameters: ParametersHolder): T
}Common injection errors:
@Composable
fun SafeInjection() {
try {
val service: MyService = koinInject()
// Use service
} catch (e: Exception) {
// Handle injection failure
Text("Service unavailable: ${e.message}")
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-insert-koin--koin-compose