Skip to content

Commit 054c52e

Browse files
authored
Add Koin sample (#338)
1 parent 29eeb98 commit 054c52e

File tree

10 files changed

+387
-77
lines changed

10 files changed

+387
-77
lines changed

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting",
3737
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" }
3838
hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hilt" }
3939

40+
koin-compose-viewmodel = "io.insert-koin:koin-compose-viewmodel:4.1.1"
41+
4042
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
4143
kotlin-compiler-embeddable = { module = "org.jetbrains.kotlin:kotlin-compiler-embeddable", version.ref = "kotlin" }
4244
kotlin-gradle-plugin-api = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin-api", version.ref = "kotlin" }
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
package composegears.tiamat.app
22

33
import android.app.Application
4+
import composegears.tiamat.sample.content.state.KoinInit
45
import dagger.hilt.android.HiltAndroidApp
56

67
@HiltAndroidApp
7-
class TiamatSampleApp : Application()
8+
class TiamatSampleApp : Application() {
9+
10+
override fun onCreate() {
11+
super.onCreate()
12+
KoinInit.start()
13+
}
14+
}

sample/app-ios/iosApp/iOSApp.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import TiamatApp
33

44
@main
55
struct iOSApp: App {
6+
7+
init() {
8+
KoinInit.shared.start()
9+
}
610

711
var body: some Scene {
812
WindowGroup {

sample/app-jvm/kotlin/main.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@ import androidx.compose.ui.unit.DpSize
1010
import androidx.compose.ui.unit.dp
1111
import androidx.compose.ui.window.*
1212
import composegears.tiamat.sample.App
13+
import composegears.tiamat.sample.content.state.KoinInit
1314

1415
@OptIn(ExperimentalFoundationApi::class)
1516
fun main() {
17+
KoinInit.start()
18+
1619
application {
1720
Window(
1821
onCloseRequest = ::exitApplication,

sample/app-wasm/src/wasmJsMain/kotlin/composegears/tiamat/sample/main.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@ import androidx.compose.ui.unit.dp
1515
import androidx.compose.ui.unit.sp
1616
import androidx.compose.ui.window.ComposeViewport
1717
import com.composegears.tiamat.sample.BuildConfig
18+
import composegears.tiamat.sample.content.state.KoinInit
1819

1920
external fun onLoadFinished()
2021

2122
@OptIn(ExperimentalComposeUiApi::class)
2223
fun main() {
24+
KoinInit.start()
25+
2326
ComposeViewport(viewportContainerId = "TiamatTarget") {
2427
LaunchedEffect(Unit) { onLoadFinished() }
2528
App(

sample/shared/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ kotlin {
4646
api(libs.compose.ui.tooling.preview)
4747
api(libs.compose.material3)
4848
api(libs.compose.material3.window.size)
49+
api(libs.koin.compose.viewmodel)
4950
}
5051
}
5152
}

sample/shared/src/commonMain/kotlin/composegears/tiamat/sample/content/HomeScreen.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import composegears.tiamat.sample.content.navigation.patterns.NavPatternTabs
4343
import composegears.tiamat.sample.content.other.OtherDestinationsGraph
4444
import composegears.tiamat.sample.content.other.OtherExtensions
4545
import composegears.tiamat.sample.content.other.OtherNavStackAlteration
46+
import composegears.tiamat.sample.content.state.KoinViewModelScreen
4647
import composegears.tiamat.sample.content.state.StateCustomSaveState
4748
import composegears.tiamat.sample.content.state.StateRetain
4849
import composegears.tiamat.sample.content.state.StateViewModel
@@ -138,6 +139,11 @@ internal val HomeItems =
138139
description = "ViewModel usage demo",
139140
destination = StateViewModel
140141
),
142+
AppFeature(
143+
name = "Koin + ViewModel",
144+
description = "Example Koin integration",
145+
destination = KoinViewModelScreen
146+
),
141147
AppFeature(
142148
name = "Custom SaveState",
143149
description = "Custom save and restore state logic case",
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package composegears.tiamat.sample.content.state
2+
3+
import androidx.compose.foundation.border
4+
import androidx.compose.foundation.layout.Box
5+
import androidx.compose.foundation.layout.fillMaxSize
6+
import androidx.compose.foundation.layout.padding
7+
import androidx.compose.foundation.shape.RoundedCornerShape
8+
import androidx.compose.material3.MaterialTheme
9+
import androidx.compose.runtime.Composable
10+
import androidx.compose.runtime.collectAsState
11+
import androidx.compose.ui.Alignment
12+
import androidx.compose.ui.Modifier
13+
import androidx.compose.ui.tooling.preview.Preview
14+
import androidx.compose.ui.unit.dp
15+
import com.composegears.tiamat.compose.*
16+
import composegears.tiamat.sample.content.state.ui.ViewModelScreen1Content
17+
import composegears.tiamat.sample.content.state.ui.ViewModelScreen2Content
18+
import composegears.tiamat.sample.content.state.ui.ViewModelScreen3Content
19+
import composegears.tiamat.sample.ui.AppTheme
20+
import composegears.tiamat.sample.ui.Screen
21+
import composegears.tiamat.sample.ui.ScreenInfo
22+
import org.koin.compose.viewmodel.koinViewModel
23+
import org.koin.core.context.startKoin
24+
import org.koin.core.module.dsl.viewModelOf
25+
import org.koin.dsl.module
26+
27+
internal val koinModule = module {
28+
viewModelOf(::SimpleViewModel)
29+
viewModelOf(::SharedSimpleViewModel)
30+
viewModelOf(::SavedStateHandleViewModel)
31+
}
32+
33+
object KoinInit {
34+
fun start() {
35+
startKoin {
36+
modules(koinModule)
37+
}
38+
}
39+
}
40+
41+
internal val KoinViewModelScreen by navDestination(ScreenInfo()) {
42+
Screen("Koin ViewModel") {
43+
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
44+
val nc = rememberNavController(startDestination = KoinViewModelScreen1)
45+
Navigation(
46+
navController = nc,
47+
destinations = arrayOf(
48+
KoinViewModelScreen1,
49+
KoinViewModelScreen2,
50+
KoinViewModelScreen3,
51+
),
52+
modifier = Modifier
53+
.fillMaxSize()
54+
.padding(16.dp)
55+
.border(1.dp, MaterialTheme.colorScheme.outlineVariant, RoundedCornerShape(8.dp))
56+
)
57+
}
58+
}
59+
}
60+
61+
private val KoinViewModelScreen1 by navDestination {
62+
val nc = navController()
63+
64+
val sharedViewModel = koinViewModel<SharedSimpleViewModel>(viewModelStoreOwner = nc)
65+
66+
ViewModelScreen1Content(
67+
sharedViewModelCounter = sharedViewModel.counter.collectAsState().value,
68+
onNext = { nc.navigate(KoinViewModelScreen2) }
69+
)
70+
}
71+
72+
private val KoinViewModelScreen2 by navDestination {
73+
val nc = navController()
74+
75+
// this is regular view model bound to the screen
76+
val viewModel = koinViewModel<SimpleViewModel>()
77+
78+
// this is shared (bound to navController instead of screen) view model
79+
val sharedViewModel = koinViewModel<SharedSimpleViewModel>(viewModelStoreOwner = nc)
80+
81+
// this is saveable view model
82+
val saveableViewModel = koinViewModel<SavedStateHandleViewModel>()
83+
84+
ViewModelScreen2Content(
85+
viewModelCounter = viewModel.counter.collectAsState().value,
86+
sharedViewModelCounter = sharedViewModel.counter.collectAsState().value,
87+
saveableViewModelCounter = saveableViewModel.counter.collectAsState().value,
88+
onBack = { nc.back() },
89+
onNext = { nc.navigate(KoinViewModelScreen3) }
90+
)
91+
}
92+
93+
private val KoinViewModelScreen3 by navDestination {
94+
val nc = navController()
95+
96+
// this is shared (bound to navController instead of screen) view model
97+
val sharedViewModel = koinViewModel<SharedSimpleViewModel>(viewModelStoreOwner = nc)
98+
99+
ViewModelScreen3Content(
100+
sharedViewModelCounter = sharedViewModel.counter.collectAsState().value,
101+
onBack = { nc.back() }
102+
)
103+
}
104+
105+
@Preview
106+
@Composable
107+
private fun KoinViewModelScreenPreview() = AppTheme {
108+
TiamatPreview(destination = KoinViewModelScreen)
109+
}

sample/shared/src/commonMain/kotlin/composegears/tiamat/sample/content/state/StateViewModel.kt

Lines changed: 24 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
package composegears.tiamat.sample.content.state
22

33
import androidx.compose.foundation.border
4-
import androidx.compose.foundation.layout.*
4+
import androidx.compose.foundation.layout.Box
5+
import androidx.compose.foundation.layout.fillMaxSize
6+
import androidx.compose.foundation.layout.padding
57
import androidx.compose.foundation.shape.RoundedCornerShape
68
import androidx.compose.material3.MaterialTheme
7-
import androidx.compose.material3.Text
89
import androidx.compose.runtime.Composable
910
import androidx.compose.runtime.collectAsState
1011
import androidx.compose.ui.Alignment
1112
import androidx.compose.ui.Modifier
12-
import androidx.compose.ui.text.style.TextAlign
1313
import androidx.compose.ui.tooling.preview.Preview
1414
import androidx.compose.ui.unit.dp
1515
import androidx.lifecycle.SavedStateHandle
@@ -18,10 +18,12 @@ import androidx.lifecycle.createSavedStateHandle
1818
import androidx.lifecycle.viewModelScope
1919
import androidx.lifecycle.viewmodel.compose.viewModel
2020
import com.composegears.tiamat.compose.*
21-
import composegears.tiamat.sample.icons.Icons
22-
import composegears.tiamat.sample.icons.KeyboardArrowLeft
23-
import composegears.tiamat.sample.icons.KeyboardArrowRight
24-
import composegears.tiamat.sample.ui.*
21+
import composegears.tiamat.sample.content.state.ui.ViewModelScreen1Content
22+
import composegears.tiamat.sample.content.state.ui.ViewModelScreen2Content
23+
import composegears.tiamat.sample.content.state.ui.ViewModelScreen3Content
24+
import composegears.tiamat.sample.ui.AppTheme
25+
import composegears.tiamat.sample.ui.Screen
26+
import composegears.tiamat.sample.ui.ScreenInfo
2527
import kotlinx.coroutines.delay
2628
import kotlinx.coroutines.flow.MutableStateFlow
2729
import kotlinx.coroutines.flow.asStateFlow
@@ -57,22 +59,10 @@ private val StateViewModelScreen1 by navDestination {
5759
// this is shared (bound to navController instead of screen) view model
5860
val sharedViewModel = viewModel(nc) { SharedSimpleViewModel() }
5961

60-
Box(Modifier.fillMaxSize().padding(16.dp), contentAlignment = Alignment.Center) {
61-
Column(horizontalAlignment = Alignment.CenterHorizontally) {
62-
Text("Screen 1", style = MaterialTheme.typography.headlineMedium)
63-
VSpacer()
64-
Text(
65-
text = "sharedViewModel \$counter = ${sharedViewModel.counter.collectAsState().value}",
66-
textAlign = TextAlign.Center
67-
)
68-
VSpacer()
69-
AppButton(
70-
"Next",
71-
endIcon = Icons.KeyboardArrowRight,
72-
onClick = { nc.navigate(StateViewModelScreen2) }
73-
)
74-
}
75-
}
62+
ViewModelScreen1Content(
63+
sharedViewModelCounter = sharedViewModel.counter.collectAsState().value,
64+
onNext = { nc.navigate(StateViewModelScreen2) }
65+
)
7666
}
7767

7868
private val StateViewModelScreen2 by navDestination {
@@ -84,43 +74,13 @@ private val StateViewModelScreen2 by navDestination {
8474
// this is saveable view model
8575
val saveableViewModel = viewModel { SavedStateHandleViewModel(createSavedStateHandle()) }
8676

87-
Box(Modifier.fillMaxSize().padding(16.dp), contentAlignment = Alignment.Center) {
88-
Column(horizontalAlignment = Alignment.CenterHorizontally) {
89-
Text("Screen 2", style = MaterialTheme.typography.headlineMedium)
90-
VSpacer()
91-
Text(
92-
text = "viewModel \$counter = ${viewModel.counter.collectAsState().value}\n" +
93-
"sharedViewModel \$counter = ${sharedViewModel.counter.collectAsState().value}\n" +
94-
"saveableViewModel \$counter = ${saveableViewModel.counter.collectAsState().value}",
95-
textAlign = TextAlign.Center
96-
)
97-
VSpacer()
98-
Text(
99-
text = """
100-
Go next screen, then go back -> you will see that view models are restored
101-
—————
102-
Go back and reopen this screen -> view models will be recreated (except shared)
103-
—————
104-
Android: hide & re-open app -> saveableViewModel will restore it's saved state
105-
""".trimIndent(),
106-
textAlign = TextAlign.Center
107-
)
108-
VSpacer()
109-
Row {
110-
AppButton(
111-
"Back",
112-
startIcon = Icons.KeyboardArrowLeft,
113-
onClick = { nc.back() }
114-
)
115-
HSpacer()
116-
AppButton(
117-
"Next",
118-
endIcon = Icons.KeyboardArrowRight,
119-
onClick = { nc.navigate(StateViewModelScreen3) }
120-
)
121-
}
122-
}
123-
}
77+
ViewModelScreen2Content(
78+
viewModelCounter = viewModel.counter.collectAsState().value,
79+
sharedViewModelCounter = sharedViewModel.counter.collectAsState().value,
80+
saveableViewModelCounter = saveableViewModel.counter.collectAsState().value,
81+
onBack = { nc.back() },
82+
onNext = { nc.navigate(StateViewModelScreen3) }
83+
)
12484
}
12585

12686
private val StateViewModelScreen3 by navDestination {
@@ -129,22 +89,10 @@ private val StateViewModelScreen3 by navDestination {
12989
// this is shared (bound to navController instead of screen) view model
13090
val sharedViewModel = viewModel(nc) { SharedSimpleViewModel() }
13191

132-
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
133-
Column(horizontalAlignment = Alignment.CenterHorizontally) {
134-
Text("Screen 3", style = MaterialTheme.typography.headlineMedium)
135-
VSpacer()
136-
Text(
137-
text = "sharedViewModel \$counter = ${sharedViewModel.counter.collectAsState().value}",
138-
textAlign = TextAlign.Center
139-
)
140-
VSpacer()
141-
AppButton(
142-
"Back",
143-
startIcon = Icons.KeyboardArrowLeft,
144-
onClick = { nc.back() }
145-
)
146-
}
147-
}
92+
ViewModelScreen3Content(
93+
sharedViewModelCounter = sharedViewModel.counter.collectAsState().value,
94+
onBack = { nc.back() }
95+
)
14896
}
14997

15098
// ---------------------- view models ---------------------------

0 commit comments

Comments
 (0)