Skip to content

Commit a8fe6de

Browse files
arkivanovxxfast
authored andcommitted
Wrap ComponentContext with RouterContext
1 parent 0162022 commit a8fe6de

File tree

12 files changed

+143
-85
lines changed

12 files changed

+143
-85
lines changed

decompose-router-wear/src/androidMain/kotlin/io/github/xxfast/decompose/router/wear/content/RoutedContent.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ import androidx.wear.compose.material.SwipeToDismissBox
1717
import androidx.wear.compose.material.SwipeToDismissKeys.Background
1818
import androidx.wear.compose.material.rememberSwipeToDismissBoxState
1919
import com.arkivanov.decompose.Child
20-
import com.arkivanov.decompose.ComponentContext
2120
import com.arkivanov.decompose.router.stack.ChildStack
2221
import com.arkivanov.decompose.router.stack.pop
23-
import io.github.xxfast.decompose.LocalComponentContext
2422
import io.github.xxfast.decompose.router.LocalRouter
23+
import io.github.xxfast.decompose.router.LocalRouterContext
2524
import io.github.xxfast.decompose.router.Router
25+
import io.github.xxfast.decompose.router.RouterContext
2626

2727
@OptIn(ExperimentalWearFoundationApi::class)
2828
@Composable
@@ -31,9 +31,9 @@ fun <C : Parcelable> RoutedContent(
3131
modifier: Modifier = Modifier,
3232
content: @Composable (C) -> Unit,
3333
) {
34-
val stack: ChildStack<C, ComponentContext> by router.stack
35-
val active: Child.Created<C, ComponentContext> = stack.active
36-
val background: Child.Created<C, ComponentContext>? = stack.backStack.lastOrNull()
34+
val stack: ChildStack<C, RouterContext> by router.stack
35+
val active: Child.Created<C, RouterContext> = stack.active
36+
val background: Child.Created<C, RouterContext>? = stack.backStack.lastOrNull()
3737
val holder: SaveableStateHolder = rememberSaveableStateHolder()
3838
holder.RetainStates(stack.getConfigurations())
3939

@@ -55,7 +55,7 @@ fun <C : Parcelable> RoutedContent(
5555
) { isBackground ->
5656
val child = if (isBackground) requireNotNull(background) else active
5757
holder.SaveableStateProvider(child.configuration.key()) {
58-
CompositionLocalProvider(LocalComponentContext provides child.instance) {
58+
CompositionLocalProvider(LocalRouterContext provides child.instance) {
5959
HierarchicalFocusCoordinator(requiresFocus = { !isBackground }) {
6060
content(child.configuration)
6161
}

decompose-router/api/android/decompose-router.api

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
public final class io/github/xxfast/decompose/NavigatorKt {
2-
public static final fun getLocalComponentContext ()Landroidx/compose/runtime/ProvidableCompositionLocal;
3-
}
4-
51
public final class io/github/xxfast/decompose/router/Router : com/arkivanov/decompose/router/stack/StackNavigation {
62
public static final field $stable I
73
public fun <init> (Lcom/arkivanov/decompose/router/stack/StackNavigation;Landroidx/compose/runtime/State;)V
@@ -11,6 +7,25 @@ public final class io/github/xxfast/decompose/router/Router : com/arkivanov/deco
117
public fun unsubscribe (Lkotlin/jvm/functions/Function1;)V
128
}
139

10+
public final class io/github/xxfast/decompose/router/RouterContext : com/arkivanov/decompose/ComponentContext {
11+
public static final field $stable I
12+
public fun <init> (Lcom/arkivanov/essenty/lifecycle/Lifecycle;Lcom/arkivanov/essenty/statekeeper/StateKeeper;Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Lcom/arkivanov/essenty/backhandler/BackHandler;)V
13+
public synthetic fun <init> (Lcom/arkivanov/essenty/lifecycle/Lifecycle;Lcom/arkivanov/essenty/statekeeper/StateKeeper;Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Lcom/arkivanov/essenty/backhandler/BackHandler;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
14+
public fun getBackHandler ()Lcom/arkivanov/essenty/backhandler/BackHandler;
15+
public fun getInstanceKeeper ()Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;
16+
public fun getLifecycle ()Lcom/arkivanov/essenty/lifecycle/Lifecycle;
17+
public fun getStateKeeper ()Lcom/arkivanov/essenty/statekeeper/StateKeeper;
18+
}
19+
20+
public final class io/github/xxfast/decompose/router/RouterContextExtKt {
21+
public static final fun defaultRouterContext (Landroidx/activity/ComponentActivity;)Lio/github/xxfast/decompose/router/RouterContext;
22+
public static final fun defaultRouterContext (Landroidx/fragment/app/Fragment;Landroidx/activity/OnBackPressedDispatcher;)Lio/github/xxfast/decompose/router/RouterContext;
23+
}
24+
25+
public final class io/github/xxfast/decompose/router/RouterContextKt {
26+
public static final fun getLocalRouterContext ()Landroidx/compose/runtime/ProvidableCompositionLocal;
27+
}
28+
1429
public final class io/github/xxfast/decompose/router/RouterKt {
1530
public static final fun getLocalRouter ()Landroidx/compose/runtime/ProvidableCompositionLocal;
1631
public static final fun rememberOnRoute (Lkotlin/reflect/KClass;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance;

decompose-router/api/desktop/decompose-router.api

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
public final class io/github/xxfast/decompose/NavigatorKt {
2-
public static final fun getLocalComponentContext ()Landroidx/compose/runtime/ProvidableCompositionLocal;
3-
}
4-
51
public final class io/github/xxfast/decompose/router/Router : com/arkivanov/decompose/router/stack/StackNavigation {
62
public static final field $stable I
73
public fun <init> (Lcom/arkivanov/decompose/router/stack/StackNavigation;Landroidx/compose/runtime/State;)V
@@ -11,6 +7,20 @@ public final class io/github/xxfast/decompose/router/Router : com/arkivanov/deco
117
public fun unsubscribe (Lkotlin/jvm/functions/Function1;)V
128
}
139

10+
public final class io/github/xxfast/decompose/router/RouterContext : com/arkivanov/decompose/ComponentContext {
11+
public static final field $stable I
12+
public fun <init> (Lcom/arkivanov/essenty/lifecycle/Lifecycle;Lcom/arkivanov/essenty/statekeeper/StateKeeper;Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Lcom/arkivanov/essenty/backhandler/BackHandler;)V
13+
public synthetic fun <init> (Lcom/arkivanov/essenty/lifecycle/Lifecycle;Lcom/arkivanov/essenty/statekeeper/StateKeeper;Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Lcom/arkivanov/essenty/backhandler/BackHandler;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
14+
public fun getBackHandler ()Lcom/arkivanov/essenty/backhandler/BackHandler;
15+
public fun getInstanceKeeper ()Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;
16+
public fun getLifecycle ()Lcom/arkivanov/essenty/lifecycle/Lifecycle;
17+
public fun getStateKeeper ()Lcom/arkivanov/essenty/statekeeper/StateKeeper;
18+
}
19+
20+
public final class io/github/xxfast/decompose/router/RouterContextKt {
21+
public static final fun getLocalRouterContext ()Landroidx/compose/runtime/ProvidableCompositionLocal;
22+
}
23+
1424
public final class io/github/xxfast/decompose/router/RouterKt {
1525
public static final fun getLocalRouter ()Landroidx/compose/runtime/ProvidableCompositionLocal;
1626
public static final fun rememberOnRoute (Lkotlin/reflect/KClass;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance;

decompose-router/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ kotlin {
6666
implementation(libs.decompose.compose.multiplatform)
6767
implementation(libs.androidx.activity.ktx)
6868
implementation(libs.androidx.activity.compose)
69+
implementation(libs.androidx.fragment.ktx)
6970
}
7071
}
7172

decompose-router/src/androidInstrumentedTest/kotlin/io/github/xxfast/decompose/TestActivity.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,20 @@ import androidx.compose.material3.MaterialTheme
77
import androidx.compose.material3.Surface
88
import androidx.compose.runtime.CompositionLocalProvider
99
import androidx.core.view.WindowCompat
10-
import com.arkivanov.decompose.DefaultComponentContext
11-
import com.arkivanov.decompose.defaultComponentContext
10+
import io.github.xxfast.decompose.router.LocalRouterContext
11+
import io.github.xxfast.decompose.router.RouterContext
12+
import io.github.xxfast.decompose.router.defaultRouterContext
1213
import io.github.xxfast.decompose.screen.HomeScreen
1314

1415
class TestActivity : ComponentActivity() {
1516
override fun onCreate(savedInstanceState: Bundle?) {
1617
super.onCreate(savedInstanceState)
1718
WindowCompat.setDecorFitsSystemWindows(window, false)
18-
val rootComponentContext: DefaultComponentContext = defaultComponentContext()
19+
val rootRouterContext: RouterContext = defaultRouterContext()
1920

2021
setContent {
2122
Surface {
22-
CompositionLocalProvider(LocalComponentContext provides rootComponentContext) {
23+
CompositionLocalProvider(LocalRouterContext provides rootRouterContext) {
2324
MaterialTheme {
2425
HomeScreen()
2526
}

decompose-router/src/androidInstrumentedTest/kotlin/io/github/xxfast/decompose/screen/HomeScreen.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.slid
77
import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.stackAnimation
88
import com.arkivanov.decompose.router.stack.pop
99
import com.arkivanov.decompose.router.stack.push
10-
import io.github.xxfast.decompose.LocalComponentContext
10+
import io.github.xxfast.decompose.router.LocalRouterContext
1111
import io.github.xxfast.decompose.router.Router
1212
import io.github.xxfast.decompose.router.content.RoutedContent
1313
import io.github.xxfast.decompose.router.rememberRouter
@@ -26,7 +26,7 @@ fun HomeScreen() {
2626
animation = predictiveBackAnimation(
2727
animation = stackAnimation(slide()),
2828
onBack = { router.pop() },
29-
backHandler = LocalComponentContext.current.backHandler
29+
backHandler = LocalRouterContext.current.backHandler
3030
)
3131
) { screen ->
3232
when (screen) {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package io.github.xxfast.decompose.router
2+
3+
import androidx.activity.ComponentActivity
4+
import androidx.activity.OnBackPressedDispatcher
5+
import androidx.fragment.app.Fragment
6+
import com.arkivanov.decompose.defaultComponentContext
7+
8+
fun ComponentActivity.defaultRouterContext(): RouterContext =
9+
RouterContext(defaultComponentContext())
10+
11+
fun Fragment.defaultRouterContext(
12+
onBackPressedDispatcher: OnBackPressedDispatcher?,
13+
): RouterContext =
14+
RouterContext(defaultComponentContext(onBackPressedDispatcher))

decompose-router/src/commonMain/kotlin/io/github/xxfast/decompose/Navigator.kt

Lines changed: 0 additions & 45 deletions
This file was deleted.

decompose-router/src/commonMain/kotlin/io/github/xxfast/decompose/router/Router.kt

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,20 @@ import androidx.compose.runtime.DisallowComposableCalls
55
import androidx.compose.runtime.LaunchedEffect
66
import androidx.compose.runtime.ProvidableCompositionLocal
77
import androidx.compose.runtime.State
8+
import androidx.compose.runtime.mutableStateOf
89
import androidx.compose.runtime.remember
910
import androidx.compose.runtime.staticCompositionLocalOf
10-
import com.arkivanov.decompose.ComponentContext
1111
import com.arkivanov.decompose.router.stack.ChildStack
1212
import com.arkivanov.decompose.router.stack.StackNavigation
13+
import com.arkivanov.decompose.router.stack.childStack
14+
import com.arkivanov.decompose.value.Value
15+
import com.arkivanov.decompose.value.observe
1316
import com.arkivanov.essenty.instancekeeper.InstanceKeeper
1417
import com.arkivanov.essenty.instancekeeper.InstanceKeeper.Instance
1518
import com.arkivanov.essenty.instancekeeper.getOrCreate
19+
import com.arkivanov.essenty.lifecycle.Lifecycle
1620
import com.arkivanov.essenty.parcelable.Parcelable
1721
import com.arkivanov.essenty.statekeeper.StateKeeper
18-
import io.github.xxfast.decompose.LocalComponentContext
19-
import io.github.xxfast.decompose.rememberChildStack
2022
import kotlin.reflect.KClass
2123

2224
/***
@@ -28,7 +30,7 @@ import kotlin.reflect.KClass
2830
*/
2931
class Router<C : Parcelable>(
3032
private val navigator: StackNavigation<C>,
31-
val stack: State<ChildStack<C, ComponentContext>>,
33+
val stack: State<ChildStack<C, RouterContext>>,
3234
) : StackNavigation<C> by navigator
3335

3436
/***
@@ -51,16 +53,31 @@ fun <C : Parcelable> rememberRouter(
5153
stack: List<C>,
5254
handleBackButton: Boolean = true
5355
): Router<C> {
54-
val navigator: StackNavigation<C> = remember { StackNavigation() }
55-
val childStackState: State<ChildStack<C, ComponentContext>> = rememberChildStack(
56-
source = navigator,
57-
initialStack = { stack },
58-
key = key.toString(), // Has to use strings for Android 😢
59-
handleBackButton = handleBackButton,
60-
type = type,
61-
)
56+
val routerContext = LocalRouterContext.current
57+
val keyStr = key.toString()
6258

63-
return remember { Router(navigator = navigator, stack = childStackState) }
59+
return remember {
60+
routerContext.getOrCreate(key = keyStr) {
61+
val navigation = StackNavigation<C>()
62+
Router(
63+
navigator = navigation,
64+
stack = routerContext.childStack(
65+
source = navigation,
66+
initialStack = { stack },
67+
configurationClass = type,
68+
key = keyStr,
69+
handleBackButton = handleBackButton,
70+
childFactory = { _, childComponentContext -> RouterContext(childComponentContext) },
71+
).asState(routerContext.lifecycle),
72+
)
73+
}
74+
}
75+
}
76+
77+
private fun <T : Any> Value<T>.asState(lifecycle: Lifecycle): State<T> {
78+
val state = mutableStateOf(value)
79+
observe(lifecycle = lifecycle) { state.value = it }
80+
return state
6481
}
6582

6683
/***
@@ -77,7 +94,7 @@ fun <T : Instance> rememberOnRoute(
7794
key: Any = type.key,
7895
block: @DisallowComposableCalls (savedState: SavedStateHandle) -> T
7996
): T {
80-
val component: ComponentContext = LocalComponentContext.current
97+
val component: RouterContext = LocalRouterContext.current
8198
val stateKeeper: StateKeeper = component.stateKeeper
8299
val instanceKeeper: InstanceKeeper = component.instanceKeeper
83100
val instanceKey = "$key.instance"
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package io.github.xxfast.decompose.router
2+
3+
import androidx.compose.runtime.ProvidableCompositionLocal
4+
import androidx.compose.runtime.staticCompositionLocalOf
5+
import com.arkivanov.decompose.ComponentContext
6+
import com.arkivanov.decompose.DefaultComponentContext
7+
import com.arkivanov.essenty.backhandler.BackHandler
8+
import com.arkivanov.essenty.instancekeeper.InstanceKeeper
9+
import com.arkivanov.essenty.lifecycle.Lifecycle
10+
import com.arkivanov.essenty.statekeeper.StateKeeper
11+
12+
class RouterContext internal constructor(
13+
private val delegate: ComponentContext,
14+
) : ComponentContext by delegate {
15+
16+
constructor(
17+
lifecycle: Lifecycle,
18+
stateKeeper: StateKeeper? = null,
19+
instanceKeeper: InstanceKeeper? = null,
20+
backHandler: BackHandler? = null,
21+
) : this(DefaultComponentContext(lifecycle, stateKeeper, instanceKeeper, backHandler))
22+
23+
internal val storage: MutableMap<Any, Any> = HashMap()
24+
}
25+
26+
internal inline fun <reified T : Any> RouterContext.getOrCreate(key: Any, factory: () -> T) : T {
27+
var instance: T? = storage[key] as T?
28+
if (instance == null) {
29+
instance = factory()
30+
storage[key] = instance
31+
}
32+
33+
return instance
34+
}
35+
36+
/***
37+
* Compositional local for [RouterContext].
38+
*
39+
* Based on [Arkadii](https://github.com/arkivanov)'s [article](https://proandroiddev.com/a-comprehensive-hundred-line-navigation-for-jetpack-desktop-compose-5b723c4f256e)
40+
* Original [source](https://github.com/arkivanov/ComposeNavigatorExample/blob/d786d92632fe22e4d7874645ba2071fb813f9ace/navigator/src/commonMain/kotlin/com/arkivanov/composenavigatorexample/navigator/Navigator.kt)
41+
*/
42+
val LocalRouterContext: ProvidableCompositionLocal<RouterContext> =
43+
staticCompositionLocalOf { error("Root RouterContext was not provided") }

0 commit comments

Comments
 (0)