D
Domen LaniΕ‘nik
Guest
While developing the Space Flight News app, I needed to inject the Android Context into Koin to set up SQLDelight and to trigger a native share intent.
There were quite a few examples of initializing Koin for every platform independently, using the initKoin function. However, I was using the KoinApplication composable function wrapper to initiate Koin from within the shared App composable function and found a lack of examples on how to do this.
This is a short article sharing how to set up your Compose Multiplatform app so that you can access the Android Context in your Koin modules when using the KoinApplication composable function wrapper.
Featured image showing logos of Koin and Compose Multiplatform.
One solution I saw recommended frequently was to create an empty Application class in the androidMain source set and store the context inside a static variable from onCreate() and access it within your Koin modules.
// composeApp/androidMain/MyApp.kt
class MyApp: Application() {
override fun onCreate() {
super.onCreate()
androidContext = this
}
}
@SuppressLint("StaticFieldLeak")
lateinit var androidContext: Context
// composeApp/androidMain/AppModule.android.kt
actual val platformModule: Module
get() = module {
// provide Context to Koin
single<Context> { androidContext }
// retrieve Context using get()
single<ShareService> { AndroidShareService(context = get()) }
}
While this might work, itβs not a scalable solution and can lead to bugs, crashes, and memory leaks.
The option that I ended up using in my Space Flight News app is to add an optional KoinAppDeclaration argument to the main App composable function. This enables passing custom config to the Koin initialization thatβs platform-specific. Itβs only used by Android, while iOS and Desktop are not using it.
// composeApp/commonMain/App.kt
@Composable
fun App(koinAppDeclaration: KoinAppDeclaration? = null) {
KoinApplication(application = {
koinAppDeclaration?.invoke(this)
modules(appModule, platformModule)
}) {
AppTheme {
MainNavigationHost()
}
}
}
Then in the MainActivity in androidMain, weβre building the KoinAppDeclaration instance and using the androidContext function from Koin to declare the specific instance of Context that Koin should use whenever we want to inject it.
// composeApp/androidMain/MainActivity.kt
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
// provide KoinAppDeclaration
App({
androidContext([email protected])
})
}
}
}
As Context is an Android-only concept; we can only access it within the androidMain source set, meaning that we must have platform-specific modules.
// composeApp/commonMain/AppModule.kt
val appModule = module {
single<ApiService> { ApiService() }
single<ArticlesRepository> {
ArticlesRepository(
databaseDriverFactory = get(),
api = get()
)
}
viewModel { ArticleListViewModel(repository = get()) }
}
expect val platformModule: Module
Within the actual implementation of the platformModule in androidMain source set we can retrieve the Context using the standard get() function.
// composeApp/androidMain/AppModule.android.kt
actual val platformModule: Module
get() = module {
single<DatabaseDriverFactory> { AndroidDatabaseDriverFactory(context = get()) }
single<ShareService> { AndroidShareService(context = get()) }
}
Release 4.1.0 of Koin added a new KoinMultiplatformApplication composable function that is the same as KoinApplication, but automatically injects the Android Context.
@OptIn(KoinExperimentalAPI::class)
@Composable
fun App() {
KoinMultiplatformApplication(config = koinConfiguration {
modules(appModule, platformModule)
}) {
AppTheme {
MainNavigationHost()
}
}
}
We looked at a couple of options on how to inject and use Android Context within a Compose Multiplatform app when using Koin and KoinApplication composable function.
Hopefully, you found this helpful. Share your approach in the comments.
Resources:
Injecting Android Context in Compose Multiplatform with Koin was originally published in ProAndroidDev on Medium, where people are continuing the conversation by highlighting and responding to this story.
Continue reading...
There were quite a few examples of initializing Koin for every platform independently, using the initKoin function. However, I was using the KoinApplication composable function wrapper to initiate Koin from within the shared App composable function and found a lack of examples on how to do this.
This is a short article sharing how to set up your Compose Multiplatform app so that you can access the Android Context in your Koin modules when using the KoinApplication composable function wrapper.

Featured image showing logos of Koin and Compose Multiplatform.
Note: this article assumes the reader is familiar with Koin, Kotlin Multiplatform, and Compose Multiplatform. If you want to learn more, consider first reading Part 2 of Building a Space News App with Compose Multiplatform for Android, iOS, and Desktop.
Wrong approach
One solution I saw recommended frequently was to create an empty Application class in the androidMain source set and store the context inside a static variable from onCreate() and access it within your Koin modules.
// composeApp/androidMain/MyApp.kt
class MyApp: Application() {
override fun onCreate() {
super.onCreate()
androidContext = this
}
}
@SuppressLint("StaticFieldLeak")
lateinit var androidContext: Context
// composeApp/androidMain/AppModule.android.kt
actual val platformModule: Module
get() = module {
// provide Context to Koin
single<Context> { androidContext }
// retrieve Context using get()
single<ShareService> { AndroidShareService(context = get()) }
}
While this might work, itβs not a scalable solution and can lead to bugs, crashes, and memory leaks.
Option 1: Providing Context through optional KoinAppDeclaration
The option that I ended up using in my Space Flight News app is to add an optional KoinAppDeclaration argument to the main App composable function. This enables passing custom config to the Koin initialization thatβs platform-specific. Itβs only used by Android, while iOS and Desktop are not using it.
// composeApp/commonMain/App.kt
@Composable
fun App(koinAppDeclaration: KoinAppDeclaration? = null) {
KoinApplication(application = {
koinAppDeclaration?.invoke(this)
modules(appModule, platformModule)
}) {
AppTheme {
MainNavigationHost()
}
}
}
Then in the MainActivity in androidMain, weβre building the KoinAppDeclaration instance and using the androidContext function from Koin to declare the specific instance of Context that Koin should use whenever we want to inject it.
// composeApp/androidMain/MainActivity.kt
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
// provide KoinAppDeclaration
App({
androidContext([email protected])
})
}
}
}
As Context is an Android-only concept; we can only access it within the androidMain source set, meaning that we must have platform-specific modules.
// composeApp/commonMain/AppModule.kt
val appModule = module {
single<ApiService> { ApiService() }
single<ArticlesRepository> {
ArticlesRepository(
databaseDriverFactory = get(),
api = get()
)
}
viewModel { ArticleListViewModel(repository = get()) }
}
expect val platformModule: Module
Within the actual implementation of the platformModule in androidMain source set we can retrieve the Context using the standard get() function.
// composeApp/androidMain/AppModule.android.kt
actual val platformModule: Module
get() = module {
single<DatabaseDriverFactory> { AndroidDatabaseDriverFactory(context = get()) }
single<ShareService> { AndroidShareService(context = get()) }
}
Option 2: Using experimental KoinMultiplatformApplication in Koin 4.1.0
Release 4.1.0 of Koin added a new KoinMultiplatformApplication composable function that is the same as KoinApplication, but automatically injects the Android Context.
Note: the API is experimental and might change in future releases.
@OptIn(KoinExperimentalAPI::class)
@Composable
fun App() {
KoinMultiplatformApplication(config = koinConfiguration {
modules(appModule, platformModule)
}) {
AppTheme {
MainNavigationHost()
}
}
}
Conclusion
We looked at a couple of options on how to inject and use Android Context within a Compose Multiplatform app when using Koin and KoinApplication composable function.
Hopefully, you found this helpful. Share your approach in the comments.
Resources:
- https://insert-koin.io/docs/reference/koin-compose/compose/βββofficial documentation
- https://carrion.dev/en/posts/koin-cmp/βββhelpful article from Ignacio CarriΓ³n
- https://stackoverflow.com/questions...-into-sqldelight-driver-using-koin-within-kmpβββsome of the alternative recommendations
Injecting Android Context in Compose Multiplatform with Koin was originally published in ProAndroidDev on Medium, where people are continuing the conversation by highlighting and responding to this story.
Continue reading...