Skip to content

Commit

Permalink
doc + test update
Browse files Browse the repository at this point in the history
test update
  • Loading branch information
arnaudgiuliani committed Jan 10, 2025
1 parent f3c4f6f commit a4abf5f
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 38 deletions.
58 changes: 38 additions & 20 deletions docs/reference/koin-compose/compose.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,58 +20,59 @@ for an Android/Multiplatform app, use the following packages:
- `koin-compose-viewmodel` - Compose ViewModel API
- `koin-compose-viewmodel-navigation` - Compose ViewModel API with Navigation API integration

## Starting Koin in a Compose App with KoinApplication
## Starting over an existing Koin context (Koin already started)

The function `KoinApplication` helps to create Koin application instance, as a Composable:
Some time the `startKoin` function is already used in the application, to start Koin in your application (like in Android main app class, the Application class). In that case you need to inform your Compose application about the current Koin context with `KoinContext` or `KoinAndroidContext`. Those functions reuse current Koin context and bind it to the Compose application.

```kotlin
@Composable
fun App() {
KoinApplication(application = {
modules(...)
}) {

// your screens here ...
// Set current Koin instance to Compose context
KoinContext() {

MyScreen()
}
}
```

The `KoinApplication` function will handle start & stop of your Koin context, regarding the cycle of the Compose context. This function start and stop a new Koin application context.

:::info
In an Android Application, the `KoinApplication` will handle any need to stop/restart Koin context regarding configuration changes or drop of Activities.
Difference between `KoinAndroidContext` and `KoinContext`:
- `KoinAndroidContext` is looking into current Android app context for Koin instance
- `KoinContext` is looking into current GlobalContext for Koin instances
:::

:::note
This replaces the use of the classic `startKoin` application function.
If you get some `ClosedScopeException` from a Composable, either use `KoinContext` on your Composable or ensure to have proper Koin start configuration [with Android context](/docs/reference/koin-android/start.md#from-your-application-class)
:::

## Starting over an existing Koin context
## Starting Koin with a Compose App - KoinApplication

Some time the `startKoin` function is already used in the application, to start Koin in your application (like in Android main app class, the Application class). In that case you need to inform your Compose application about the current Koin context with `KoinContext` or `KoinAndroidContext`. Those functions reuse current Koin context and bind it to the Compose application.
The function `KoinApplication` helps to create Koin application instance, as a Composable:

```kotlin
@Composable
fun App() {
// Set current Koin instance to Compose context
KoinContext() {

KoinApplication(application = {
modules(...)
}) {

// your screens here ...
MyScreen()
}
}
```

The `KoinApplication` function will handle start & stop of your Koin context, regarding the cycle of the Compose context. This function start and stop a new Koin application context.

:::info
Difference between `KoinAndroidContext` and `KoinContext`:
- `KoinAndroidContext` is looking into current Android app context for Koin instance
- `KoinContext` is looking into current GlobalContext for Koin instances
In an Android Application, the `KoinApplication` will handle any need to stop/restart Koin context regarding configuration changes or drop of Activities.
:::

:::note
If you get some `ClosedScopeException` from a Composable, either use `KoinContext` on your Composable or ensure to have proper Koin start configuration [with Android context](/docs/reference/koin-android/start.md#from-your-application-class)
This replaces the use of the classic `startKoin` application function.
:::


### Compose Preview with Koin

The `KoinApplication` function is interesting to start dedicated context for preview. This can be also used to help with Compose preview:
Expand Down Expand Up @@ -122,6 +123,23 @@ fun App(myService: MyService = koinInject()) {
}
```

### Injecting into a @Composable with Parameters

While you request a new dependency from Koin, you may need to inject parameters. To do this you can use `parameters` parameter of the `koinInject` function, with the `parametersOf()` function like this:

```kotlin
@Composable
fun App() {
val myService = koinInject<MyService>(parameters = parametersOf("a_string"))
}
```

:::info
You can use parameters with lambda injection like `koinInject<MyService>{ parametersOf("a_string") }`, but this can have a performance impact if your recomposing a lot around. This version with lambda needs to unwrap your parameters on call, to help avoid remembering your parameters.

From version 4.0.2 of Koin, koinInject(Qualifier,Scope,ParametersHolder) is introduced to let you use parameters in the most efficient way
:::

## ViewModel for @Composable

The same way you have access to classical single/factory instances, you gain access to the following Koin ViewModel API:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package org.koin.sample.androidx.compose
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.Button
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
Expand All @@ -20,6 +22,7 @@ import org.koin.sample.androidx.compose.data.MySingle
import org.koin.sample.androidx.compose.di.secondModule
import org.koin.sample.androidx.compose.viewmodel.SSHViewModel
import org.koin.sample.androidx.compose.viewmodel.UserViewModel
import java.util.UUID


@Composable
Expand All @@ -43,6 +46,9 @@ fun App(userViewModel: UserViewModel = koinViewModel()) {
item {
ButtonForCreate("-X- Main") { created = !created }
}
item {
MyScreen()
}
}
} else {
Surface(modifier = Modifier.padding(8.dp)) {
Expand All @@ -52,6 +58,27 @@ fun App(userViewModel: UserViewModel = koinViewModel()) {

}

@Composable
fun MyScreen() {

var someValue by remember { mutableStateOf("initial") }
val myDependency = koinInject<MyFactory>(parameters = parametersOf(someValue))

SideEffect {
println("MyScreen 1")
}

Column {
Text(text = myDependency.id)
TextField(someValue, onValueChange = { someValue = it })
Button(onClick = {
if (someValue == "") someValue = "${UUID.randomUUID()}"
}) {
Text("Update")
}
}
}

@Composable
fun ViewModelComposable(
parentStatus: String = "- status -",
Expand All @@ -70,21 +97,6 @@ fun ViewModelComposable(
}
}

// Preview

//val fakeKoin = module {
// singleOf(::MySingle)
// factoryOf(::MyInnerFactory)
//}
//
//@Preview
//@Composable
//fun PreviewViewModelComposable() {
// KoinApplication(moduleList = { listOf(fakeKoin) }) {
// SingleComposable()
// }
//}


@Composable
fun SingleComposable(
Expand All @@ -105,7 +117,7 @@ fun SingleComposable(
@Composable
fun FactoryComposable(
parentStatus: String = "- status -",
myFactory: MyFactory = koinInject { parametersOf("stable_status") }
myFactory: MyFactory = koinInject(parameters = parametersOf("stable_status"))
) {
var created by remember { mutableStateOf(false) }
rememberKoinModules(modules = { listOf(secondModule) })
Expand All @@ -132,7 +144,7 @@ fun FactoryComposable(
//TODO Hold instance until recreate Composable
fun InnerFactoryComposable(
parentStatus: String,
myFactory: MyInnerFactory = koinInject { parametersOf("_stable_") }
myFactory: MyInnerFactory = koinInject(parameters = parametersOf("_stable_"))
) {
var created by remember { mutableStateOf(false) }
if (created) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ class MainApplication : Application() {

startKoin {
androidLogger(Level.DEBUG)
// printLogger(Level.DEBUG)
androidContext(this@MainApplication)
modules(appModule)
}
Expand Down

0 comments on commit a4abf5f

Please sign in to comment.