Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improving Development Support For Android Jetpack Compose Previews #1763

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright 2017-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.koin.compose.preview

import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import org.koin.compose.KoinIsolatedContext
import org.koin.core.context.stopKoin
import org.koin.core.module.Module
import org.koin.core.KoinApplication
import org.koin.dsl.koinApplication

/**
* Starts a new isolated KoinApplication for the provided list of Koin modules.
* Can be used if you have multiple compose previews in one file that require certain definitions.
*
* @param modules list of definitions required for the preview
* @param content compose preview content
*
* @author Yanneck Reiß
*/
@Composable
fun KoinPreviewApplication(
modules: () -> List<Module>,
content: @Composable () -> Unit
) {
var isInitialComposition: Boolean by remember { mutableStateOf(true) }
var koinApplicationHolder: DynamicIsolatedContextHolder = remember { DynamicIsolatedContextHolder(modules()) }

KoinIsolatedContext(
context = koinApplicationHolder.koinApp,
content = {

// Here we access the KoinPreviewApplication when we call stopKoin() because the KoinContext composable
// provides it in the CompositionLocal
DisposableEffect(modules()) {
// For the first run of this effect we don't want to do anything
if (!isInitialComposition) {
// Otherwise we stop the current KoinContext which refers to the provided
// Koin instance from our koinApplicationHolder, stop and recreate it
// with the fresh list of modules
stopKoin()
koinApplicationHolder = DynamicIsolatedContextHolder(modules())
}
// First run has been done, next time the list of modules change,
// we actually want to reinitialize the KoinApplication
isInitialComposition = false

// If the preview disposes, we stop the KoinContext
onDispose {
stopKoin()
}
}

// The preview content composable
content()
}
)
}

/**
* Isolated context holder that takes list of definitions to dynamically
* create a KoinApplication with the required definitions.
*
* @param modules list of definitions
*
* @author Yanneck Reiß
*/
private class DynamicIsolatedContextHolder(
modules: List<Module>
) {

val koinApp: KoinApplication = koinApplication(
appDeclaration = { modules(modules) }
)
}
14 changes: 8 additions & 6 deletions docs/reference/koin-compose/compose.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,19 @@ Difference between `KoinAndroidContext` and `KoinContext`:

### Compose Preview with Koin

The `KoinApplication` function is also interesting to start dedicated context for preview. This can be also used to help with Compose preview:
If the composables you want to preview depend on definitions provided by Koin, you can use the `KoinPreviewApplication'.
This function allows you to launch a dedicated Koin context for each of your composable previews:

```kotlin
@Composable
@Preview
fun App() {
KoinApplication(application = {
// your preview config here
modules(previewModule)
fun MyComposablePreview() {
KoinPreviewApplication(modules = {
// Your definitions which the composable you want to preview here depend on here
listOf(previewModule)
}) {
// Compose to preview with Koin
// Composable to preview with Koin here
MyComposable()
}
}
```
Expand Down