Skip to content

Commit e57a902

Browse files
authored
Merge pull request #969 from arkivanov/LocalLifecycleOwner-docs
Add docs for LocalLifecycleOwner
2 parents 2d39ef3 + 5cc1cdc commit e57a902

File tree

3 files changed

+122
-0
lines changed

3 files changed

+122
-0
lines changed

docs/component/jetpack-component-context.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,58 @@ private class RootViewModel(
102102
val state: MutableStateFlow<Int> = savedStateHandle.getMutableStateFlow(key = "state", initialValue = 0)
103103
}
104104
```
105+
106+
### LocalLifecycleOwner in Compose
107+
108+
By default, Decompose does nothing with `LocalLifecycleOwner`. Most likely you will get the hosting Activity (or Fragment) `Lifecycle`, it will not respond to screen changes. You can use the following approach to overcome this limitation.
109+
110+
```kotlin
111+
interface RootComponent {
112+
val stack: Value<ChildStack<*, Child>>
113+
114+
// Child implements LifecycleOwner via delegation
115+
sealed class Child(lifecycleOwner: LifecycleOwner) : LifecycleOwner by lifecycleOwner {
116+
class HomeChild(val component: HomeComponent, lifecycleOwner: LifecycleOwner) : Child(lifecycleOwner)
117+
}
118+
}
119+
120+
class DefaultRootComponent(
121+
componentContext: JetpackComponentContext,
122+
) : RootComponent, JetpackComponentContext by componentContext {
123+
124+
private val nav = StackNavigation<Config>()
125+
126+
override val stack: Value<ChildStack<*, RootComponent.Child>> =
127+
childStack(
128+
source = nav,
129+
serializer = Config.serializer(),
130+
initialConfiguration = Config.Home,
131+
) { cfg, ctx ->
132+
when (cfg) {
133+
is Config.Home ->
134+
RootComponent.Child.HomeChild(
135+
component = DefaultHomeComponent(ctx),
136+
lifecycleOwner = ctx, // The child JetpackComponentContext implements LifecycleOwner
137+
)
138+
}
139+
}
140+
141+
@Serializable
142+
private sealed interface Config {
143+
@Serializable
144+
data object Home : Config
145+
}
146+
}
147+
148+
@Composable
149+
fun RootContent(component: RootComponent) {
150+
Children(stack = component.stack) {
151+
// Provide LocalLifecycleOwner for child screens
152+
CompositionLocalProvider(LocalLifecycleOwner provides it.instance) {
153+
when (val child = it.instance) {
154+
is RootComponent.Child.HomeChild -> HomeContent(child.component)
155+
}
156+
}
157+
}
158+
}
159+
```

sample/app-android/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import com.arkivanov.gradle.setupAndroidApp
33
plugins {
44
id("com.android.application")
55
id("kotlin-android")
6+
id("kotlinx-serialization")
67
id("org.jetbrains.compose")
78
id("org.jetbrains.kotlin.plugin.compose")
89
id("com.arkivanov.gradle.setup")
@@ -27,7 +28,9 @@ android {
2728

2829
dependencies {
2930
implementation(project(":decompose"))
31+
implementation(project(":jetpack-component-context"))
3032
implementation(project(":extensions-android"))
33+
implementation(project(":extensions-compose"))
3134
implementation(project(":sample:shared:shared"))
3235
implementation(project(":sample:shared:compose"))
3336
implementation(compose.runtime)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.arkivanov.sample.app
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.compose.runtime.CompositionLocalProvider
5+
import androidx.lifecycle.LifecycleOwner
6+
import androidx.lifecycle.compose.LocalLifecycleOwner
7+
import com.arkivanov.decompose.extensions.compose.stack.Children
8+
import com.arkivanov.decompose.jetpackcomponentcontext.JetpackComponentContext
9+
import com.arkivanov.decompose.router.stack.ChildStack
10+
import com.arkivanov.decompose.router.stack.StackNavigation
11+
import com.arkivanov.decompose.router.stack.childStack
12+
import com.arkivanov.decompose.value.Value
13+
import kotlinx.serialization.Serializable
14+
15+
interface HomeComponent
16+
17+
class DefaultHomeComponent(ctx: JetpackComponentContext) : HomeComponent
18+
19+
@Composable
20+
fun HomeContent(component: HomeComponent) {
21+
}
22+
23+
interface RootComponent {
24+
val stack: Value<ChildStack<*, Child>>
25+
26+
sealed class Child(lifecycleOwner: LifecycleOwner) : LifecycleOwner by lifecycleOwner {
27+
class HomeChild(val component: HomeComponent, lifecycleOwner: LifecycleOwner) : Child(lifecycleOwner)
28+
}
29+
}
30+
31+
class DefaultRootComponent(
32+
componentContext: JetpackComponentContext,
33+
) : RootComponent, JetpackComponentContext by componentContext {
34+
35+
private val nav = StackNavigation<Config>()
36+
37+
override val stack: Value<ChildStack<*, RootComponent.Child>> =
38+
childStack(
39+
source = nav,
40+
serializer = Config.serializer(),
41+
initialConfiguration = Config.Home,
42+
) { cfg, ctx ->
43+
when (cfg) {
44+
is Config.Home -> RootComponent.Child.HomeChild(DefaultHomeComponent(ctx), ctx)
45+
}
46+
}
47+
48+
@Serializable
49+
private sealed interface Config {
50+
@Serializable
51+
data object Home : Config
52+
}
53+
}
54+
55+
@Composable
56+
fun RootContent(component: RootComponent) {
57+
Children(stack = component.stack) {
58+
CompositionLocalProvider(LocalLifecycleOwner provides it.instance) {
59+
when (val child = it.instance) {
60+
is RootComponent.Child.HomeChild -> HomeContent(child.component)
61+
}
62+
}
63+
}
64+
}

0 commit comments

Comments
 (0)