Skip to content

Commit 8e29dd9

Browse files
committed
Uncomment and update the ViewModel code in the "common" module, add the necessary dependencies, and update the demo Material3 composable to use a Material3ViewModel to demonstrate ViewModels
An exception is thrown with the message "ViewModelStore should be set before setGraph call" on JS DOM in an internal app but not in the demo.
1 parent c005afb commit 8e29dd9

File tree

10 files changed

+47
-20
lines changed

10 files changed

+47
-20
lines changed

common/build.gradle.kts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,12 @@ kotlin {
5757
api("com.varabyte.kobweb:kobweb-compose:${DependencyVersions.kobweb}")
5858
implementation("com.huanshankeji:compose-html-common:${DependencyVersions.huanshankejiComposeHtml}")
5959

60-
// TODO not used yet
6160
/*
6261
The UI module depends on the lifecycle module to use `androidx.lifecycle.ViewModelStoreOwner`.
6362
See https://github.com/JetBrains/compose-multiplatform-core/blob/jb-main/compose/ui/ui/build.gradle#L87.
6463
This is actually only needed for JS DOM.
6564
*/
66-
//implementation(cpnProject(project, ":lifecycle-viewmodel"))
65+
implementation(commonDependencies.jetbrainsAndroidx.lifecycle.viewmodel())
6766
}
6867
}
6968
}

common/src/jsMain/kotlin/com/huanshankeji/compose/ui/platform/DefaultViewModelOwnerStore.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.huanshankeji.compose.ui.platform
22

3-
/*
43
// copied and adapted from "DefaultViewModelOwnerStore.skiko.kt" in `androidx.compose.ui.platform`
54

65
import androidx.compose.runtime.Composable
@@ -22,4 +21,3 @@ internal val LocalInternalViewModelStoreOwner = staticCompositionLocalOf<ViewMod
2221
@Composable
2322
fun findComposeDefaultViewModelStoreOwner(): ViewModelStoreOwner? =
2423
LocalInternalViewModelStoreOwner.current
25-
*/

common/src/jsMain/kotlin/com/huanshankeji/compose/ui/window/ComposeWindow.js.kt

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,36 @@
11
package com.huanshankeji.compose.ui.window
22

3-
/*
43
import androidx.compose.runtime.Composable
54
import androidx.compose.runtime.Composition
65
import androidx.compose.runtime.CompositionLocalProvider
6+
import androidx.lifecycle.ViewModelStore
7+
import androidx.lifecycle.ViewModelStoreOwner
8+
import com.huanshankeji.compose.ExperimentalApi
79
import com.huanshankeji.compose.ui.platform.LocalInternalViewModelStoreOwner
810
import org.jetbrains.compose.web.dom.DOMScope
911
import org.jetbrains.compose.web.renderComposableInBody
1012
import org.w3c.dom.HTMLBodyElement
1113

12-
// TODO not used yet so made private
13-
private fun renderComposableInBodyWithLifecycle(
14+
@ExperimentalApi
15+
class SimpleViewModelStoreOwner : ViewModelStoreOwner {
16+
override val viewModelStore: ViewModelStore = ViewModelStore()
17+
}
18+
19+
fun renderComposableInBodyWithViewModelStoreOwner(
1420
content: @Composable DOMScope<HTMLBodyElement>.() -> Unit
1521
): Composition =
1622
renderComposableInBody {
1723
// copied and adapted from `ComposeWindow` in "ComposeWindow.web.kt" in `androidx.compose.ui.window`
1824
// also see `ComposeViewport` on Wasm JS
25+
@OptIn(ExperimentalApi::class)
1926
CompositionLocalProvider(
20-
//LocalSystemTheme provides systemThemeObserver.currentSystemTheme.value, // TODO add back if needed one day
21-
//LocalLifecycleOwner provides this, // TODO
22-
LocalInternalViewModelStoreOwner provides TODO(),
27+
/* TODO add back these 2 lines below if needed one day
28+
in a function possibly named `renderComposableInBodyWithLifecycle` */
29+
//LocalSystemTheme provides systemThemeObserver.currentSystemTheme.value,
30+
//LocalLifecycleOwner provides this,
31+
LocalInternalViewModelStoreOwner provides SimpleViewModelStoreOwner(),
2332
content = {
2433
content()
2534
}
2635
)
2736
}
28-
*/

demo/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ kotlin {
4949
implementation(compose.runtime)
5050
implementation(cpnProject(project, ":material2"))
5151
implementation(cpnProject(project, ":material3"))
52+
implementation(cpnProject(project, ":lifecycle-viewmodel"))
5253
implementation(cpnProject(project, ":navigation"))
5354
/*
5455
see https://github.com/JetBrains/compose-multiplatform-core/blob/476d43b99a27696d12ef087e8028d90789645ba7/compose/ui/ui/build.gradle#L54

demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Material3.kt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.huanshankeji.compose.material.demo
22

33
import androidx.compose.runtime.*
44
import androidx.compose.ui.unit.dp
5+
import com.huanshankeji.androidx.lifecycle.viewmodel.compose.viewModel
56
import com.huanshankeji.compose.ExtRecommendedApi
67
import com.huanshankeji.compose.foundation.layout.*
78
import com.huanshankeji.compose.foundation.rememberScrollState
@@ -27,10 +28,12 @@ import com.huanshankeji.compose.ui.Modifier
2728
import com.huanshankeji.compose.material3.Button as RowScopeButton
2829

2930
@Composable
30-
fun Material3(/*modifier: Modifier = Modifier*/) {
31+
fun Material3(/*modifier: Modifier = Modifier*/
32+
viewModel: Material3ViewModel = viewModel { Material3ViewModel() }
33+
) {
3134
Column(Modifier.verticalScroll(rememberScrollState()).innerContentPadding(), Arrangement.spacedBy(16.dp)) {
32-
var count by remember { mutableStateOf(0) }
33-
val onClick: () -> Unit = { count++ }
35+
val count by viewModel.countState.collectAsState()
36+
val onClick: () -> Unit = { viewModel.countState.value++ }
3437
val buttonContent: @Composable () -> Unit = {
3538
TaglessText(count.toString())
3639
}
@@ -56,7 +59,8 @@ fun Material3(/*modifier: Modifier = Modifier*/) {
5659
FilledTonalIconButton(onClick, content = iconButtonContent)
5760
OutlinedIconButton(onClick, content = iconButtonContent)
5861
}
59-
val (checked, onCheckedChange) = remember { mutableStateOf(false) }
62+
val checked = viewModel.checkedState.collectAsState().value
63+
val onCheckedChange: (Boolean) -> Unit = { viewModel.checkedState.value = it }
6064
val iconToggleButtonContent: @Composable () -> Unit = {
6165
Icon(if (checked) Icons.Default.Add else Icons.Default.Remove, null)
6266
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.huanshankeji.compose.material.demo
2+
3+
import androidx.lifecycle.ViewModel
4+
import kotlinx.coroutines.flow.MutableStateFlow
5+
6+
class Material3ViewModel : ViewModel() {
7+
val countState = MutableStateFlow(0)
8+
val checkedState = MutableStateFlow(false)
9+
}
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package com.huanshankeji.compose.material.demo
22

33
import com.huanshankeji.compose.html.material3.require
4-
import org.jetbrains.compose.web.renderComposableInBody
4+
import com.huanshankeji.compose.ui.window.renderComposableInBodyWithViewModelStoreOwner
55

66
fun main() {
77
require("material-symbols/outlined.css")
8-
renderComposableInBody { App() }
8+
//renderComposableInBody { App() } // "No ViewModelStoreOwner was provided via LocalViewModelStoreOwner"
9+
renderComposableInBodyWithViewModelStoreOwner { App() }
910
}

lifecycle-viewmodel/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import com.huanshankeji.cpnProject
12
import com.huanshankeji.team.`Shreck Ye`
23
import com.huanshankeji.team.pomForTeamDefaultOpenSource
34

@@ -16,6 +17,9 @@ kotlin {
1617
*/
1718
api(compose.runtime)
1819
api(commonDependencies.jetbrainsAndroidx.lifecycle.viewmodel())
20+
// only needed on JS DOM actually
21+
// https://github.com/JetBrains/compose-multiplatform-core/blob/f1e03d0784631a88201931a6a6a708cdd090be57/lifecycle/lifecycle-viewmodel-compose/build.gradle#L58
22+
api(cpnProject(project, ":common"))
1923
}
2024
}
2125
composeUiMain {

lifecycle-viewmodel/src/commonMain/kotlin/ViewModel.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ expect inline fun <reified VM : ViewModel> viewModel(
4646
@Deprecated(
4747
"Use the one with a `viewModelStoreOwner` parameter instead. " +
4848
"This function might be removed in the future. " +
49-
"Make sure you call this function with named arguments please so your source still compile when this is removed."
49+
"If you call this function with a `key` argument, make sure you used a named argument " +
50+
"so your source still compiles when this is removed."
5051
)
5152
@Composable
5253
inline fun <reified VM : ViewModel> viewModel(

lifecycle-viewmodel/src/jsMain/kotlin/LocalViewModelStoreOwner.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package com.huanshankeji.androidx.lifecycle.viewmodel.compose
22

33
import androidx.compose.runtime.Composable
4+
import androidx.compose.runtime.InternalComposeApi
45
import androidx.compose.runtime.ProvidedValue
56
import androidx.compose.runtime.compositionLocalOf
67
import androidx.lifecycle.ViewModelStoreOwner
8+
import com.huanshankeji.compose.ui.platform.findComposeDefaultViewModelStoreOwner
79

810
// copied and adapted from "LocalViewModelStoreOwner.kt" and "LocalViewModelStoreOwner.jb.kt" in `androidx.lifecycle.viewmodel.compose`
911

@@ -20,7 +22,7 @@ object LocalViewModelStoreOwner {
2022
}
2123
}
2224

25+
@OptIn(InternalComposeApi::class)
2326
@Composable
2427
internal fun findViewTreeViewModelStoreOwner(): ViewModelStoreOwner? =
25-
// TODO
26-
null //findComposeDefaultViewModelStoreOwner()
28+
findComposeDefaultViewModelStoreOwner()

0 commit comments

Comments
 (0)