Repository: arkivanov/Decompose
Pull request #242 (2022-10-24 19:23:38)

Use Children in Stack and Overlay


Patch:
diff --git a/decompose/api/android/decompose.api b/decompose/api/android/decompose.api
index 0344efa5..f626bc80 100644
--- a/decompose/api/android/decompose.api
+++ b/decompose/api/android/decompose.api
@@ -132,9 +132,7 @@ public final class com/arkivanov/decompose/router/overlay/OverlayNavigationFacto
 	public static final fun OverlayNavigation ()Lcom/arkivanov/decompose/router/overlay/OverlayNavigation;
 }
 
-public abstract interface class com/arkivanov/decompose/router/overlay/OverlayNavigationSource {
-	public abstract fun subscribe (Lkotlin/jvm/functions/Function1;)V
-	public abstract fun unsubscribe (Lkotlin/jvm/functions/Function1;)V
+public abstract interface class com/arkivanov/decompose/router/overlay/OverlayNavigationSource : com/arkivanov/decompose/router/children/NavigationSource {
 }
 
 public final class com/arkivanov/decompose/router/overlay/OverlayNavigationSource$Event {
@@ -188,9 +186,7 @@ public final class com/arkivanov/decompose/router/stack/StackNavigationFactoryKt
 	public static final fun StackNavigation ()Lcom/arkivanov/decompose/router/stack/StackNavigation;
 }
 
-public abstract interface class com/arkivanov/decompose/router/stack/StackNavigationSource {
-	public abstract fun subscribe (Lkotlin/jvm/functions/Function1;)V
-	public abstract fun unsubscribe (Lkotlin/jvm/functions/Function1;)V
+public abstract interface class com/arkivanov/decompose/router/stack/StackNavigationSource : com/arkivanov/decompose/router/children/NavigationSource {
 }
 
 public final class com/arkivanov/decompose/router/stack/StackNavigationSource$Event {
@@ -220,19 +216,11 @@ public final class com/arkivanov/decompose/router/stack/StackNavigatorExtKt {
 	public static synthetic fun replaceCurrent$default (Lcom/arkivanov/decompose/router/stack/StackNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V
 }
 
-public final class com/arkivanov/decompose/router/stack/StackSaverImpl$SavedEntry$Creator : android/os/Parcelable$Creator {
-	public fun <init> ()V
-	public final fun createFromParcel (Landroid/os/Parcel;)Lcom/arkivanov/decompose/router/stack/StackSaverImpl$SavedEntry;
-	public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object;
-	public final fun newArray (I)[Lcom/arkivanov/decompose/router/stack/StackSaverImpl$SavedEntry;
-	public synthetic fun newArray (I)[Ljava/lang/Object;
-}
-
-public final class com/arkivanov/decompose/router/stack/StackSaverImpl$SavedState$Creator : android/os/Parcelable$Creator {
+public final class com/arkivanov/decompose/router/stack/StackSavedNavState$Creator : android/os/Parcelable$Creator {
 	public fun <init> ()V
-	public final fun createFromParcel (Landroid/os/Parcel;)Lcom/arkivanov/decompose/router/stack/StackSaverImpl$SavedState;
+	public final fun createFromParcel (Landroid/os/Parcel;)Lcom/arkivanov/decompose/router/stack/StackSavedNavState;
 	public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object;
-	public final fun newArray (I)[Lcom/arkivanov/decompose/router/stack/StackSaverImpl$SavedState;
+	public final fun newArray (I)[Lcom/arkivanov/decompose/router/stack/StackSavedNavState;
 	public synthetic fun newArray (I)[Ljava/lang/Object;
 }
 
diff --git a/decompose/api/jvm/decompose.api b/decompose/api/jvm/decompose.api
index aaad04cf..9ab45558 100644
--- a/decompose/api/jvm/decompose.api
+++ b/decompose/api/jvm/decompose.api
@@ -111,9 +111,7 @@ public final class com/arkivanov/decompose/router/overlay/OverlayNavigationFacto
 	public static final fun OverlayNavigation ()Lcom/arkivanov/decompose/router/overlay/OverlayNavigation;
 }
 
-public abstract interface class com/arkivanov/decompose/router/overlay/OverlayNavigationSource {
-	public abstract fun subscribe (Lkotlin/jvm/functions/Function1;)V
-	public abstract fun unsubscribe (Lkotlin/jvm/functions/Function1;)V
+public abstract interface class com/arkivanov/decompose/router/overlay/OverlayNavigationSource : com/arkivanov/decompose/router/children/NavigationSource {
 }
 
 public final class com/arkivanov/decompose/router/overlay/OverlayNavigationSource$Event {
@@ -167,9 +165,7 @@ public final class com/arkivanov/decompose/router/stack/StackNavigationFactoryKt
 	public static final fun StackNavigation ()Lcom/arkivanov/decompose/router/stack/StackNavigation;
 }
 
-public abstract interface class com/arkivanov/decompose/router/stack/StackNavigationSource {
-	public abstract fun subscribe (Lkotlin/jvm/functions/Function1;)V
-	public abstract fun unsubscribe (Lkotlin/jvm/functions/Function1;)V
+public abstract interface class com/arkivanov/decompose/router/stack/StackNavigationSource : com/arkivanov/decompose/router/children/NavigationSource {
 }
 
 public final class com/arkivanov/decompose/router/stack/StackNavigationSource$Event {
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/GettingList.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/GettingList.kt
new file mode 100644
index 00000000..2de29f76
--- /dev/null
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/GettingList.kt
@@ -0,0 +1,10 @@
+package com.arkivanov.decompose
+
+internal class GettingList<out T>(
+    override val size: Int,
+    private val get: (Int) -> T,
+) : AbstractList<T>() {
+
+    override fun get(index: Int): T =
+        get.invoke(index)
+}
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt
index 96d6b838..d683f22f 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt
@@ -2,6 +2,7 @@ package com.arkivanov.decompose.router.children
 
 import com.arkivanov.decompose.Child
 import com.arkivanov.decompose.ComponentContext
+import com.arkivanov.decompose.ExperimentalDecomposeApi
 import com.arkivanov.decompose.backhandler.child
 import com.arkivanov.decompose.value.MutableValue
 import com.arkivanov.decompose.value.Value
@@ -52,6 +53,7 @@ import com.arkivanov.essenty.statekeeper.consume
  * @param childFactory a factory function that creates new child component instances.
  * @return an observable [Value] of the resulting children state.
  */
+@ExperimentalDecomposeApi
 fun <C : Any, T : Any, E : Any, N : NavState<C>, S : Any> ComponentContext.children(
     source: NavigationSource<E>,
     key: String,
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenNavigator.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenNavigator.kt
index eaeb0160..29307f4c 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenNavigator.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenNavigator.kt
@@ -111,9 +111,12 @@ internal class ChildrenNavigator<out C : Any, out T : Any, N : NavState<C>>(
     }
 
     private fun switch(newStates: List<ChildNavState<C>>) {
+        val newConfigurations = newStates.mapTo(HashSet(), ChildNavState<C>::configuration)
+        check(newConfigurations.size == newStates.size) { "Configurations must be unique" }
+
         val oldItems = items.associateBy(ChildItem<C, *>::configuration)
         val newItems = prepareNewItems(newStates = newStates, oldItems = oldItems)
-        destroyOldItems(newStates = newStates, oldItems = oldItems.values)
+        destroyOldItems(newConfigurations = newConfigurations, oldItems = oldItems.values)
         processNewItems(newItems = newItems)
     }
 
@@ -176,13 +179,12 @@ internal class ChildrenNavigator<out C : Any, out T : Any, N : NavState<C>>(
     }
 
     private fun destroyOldItems(
-        newStates: List<ChildNavState<C>>,
+        newConfigurations: Set<C>,
         oldItems: Collection<ChildItem<C, T>>,
     ) {
-        val newStatesSet = newStates.mapTo(HashSet(), ChildNavState<C>::configuration)
         for (item in oldItems) {
             val child = item as? Created ?: continue
-            if (item.configuration !in newStatesSet) {
+            if (item.configuration !in newConfigurations) {
                 child.backHandler.stop()
                 child.instanceKeeperDispatcher.destroy()
                 child.lifecycleRegistry.destroy()
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlayFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlayFactory.kt
index 04cd22f3..30e1d6eb 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlayFactory.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlayFactory.kt
@@ -2,17 +2,14 @@ package com.arkivanov.decompose.router.overlay
 
 import com.arkivanov.decompose.Child
 import com.arkivanov.decompose.ComponentContext
-import com.arkivanov.decompose.Optional
-import com.arkivanov.decompose.instancekeeper.attachTo
-import com.arkivanov.decompose.optionalOf
-import com.arkivanov.decompose.router.stack.StackNavigationSource
-import com.arkivanov.decompose.router.stack.childStack
+import com.arkivanov.decompose.ExperimentalDecomposeApi
+import com.arkivanov.decompose.router.children.ChildNavState.Status
+import com.arkivanov.decompose.router.children.NavState
+import com.arkivanov.decompose.router.children.SimpleChildNavState
+import com.arkivanov.decompose.router.children.children
 import com.arkivanov.decompose.value.Value
-import com.arkivanov.decompose.value.operator.map
-import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher
 import com.arkivanov.essenty.parcelable.Parcelable
 import com.arkivanov.essenty.parcelable.ParcelableContainer
-import com.arkivanov.essenty.statekeeper.StateKeeperDispatcher
 import kotlin.reflect.KClass
 
 /**
@@ -30,6 +27,7 @@ import kotlin.reflect.KClass
  * @param childFactory a factory function that creates new child instances.
  * @return an observable [Value] of [ChildOverlay].
  */
+@OptIn(ExperimentalDecomposeApi::class)
 fun <C : Parcelable, T : Any> ComponentContext.childOverlay(
     source: OverlayNavigationSource<C>,
     configurationClass: KClass<out C>,
@@ -39,25 +37,36 @@ fun <C : Parcelable, T : Any> ComponentContext.childOverlay(
     handleBackButton: Boolean = false,
     childFactory: (configuration: C, ComponentContext) -> T,
 ): Value<ChildOverlay<C, T>> =
-    childStack(
-        lifecycle = lifecycle,
-        stateKeeper = if (persistent) stateKeeper else StateKeeperDispatcher(),
-        instanceKeeper = if (persistent) instanceKeeper else InstanceKeeperDispatcher().attachTo(lifecycle),
-        backHandler = backHandler,
-        source = StackNavigationSourceImpl(source),
-        initialStack = { configurationStack(initialConfiguration()) },
-        saveConfiguration = { ParcelableContainer(it.value) },
-        restoreConfiguration = { optionalOf(it.consume(configurationClass)) },
+    children(
+        source = source,
         key = key,
-        handleBackButton = handleBackButton,
-        childFactory = { configuration, componentContext ->
-            if (configuration.value != null) {
-                optionalOf(childFactory(configuration.value, componentContext))
+        initialNavState = { OverlayNavState(configuration = initialConfiguration()) },
+        saveNavState = { navState -> ParcelableContainer(navState.configuration?.takeIf { persistent }) },
+        restoreNavState = { container -> OverlayNavState(container.consume(configurationClass)) },
+        navTransformer = { navState, event -> OverlayNavState(configuration = event.transformer(navState.configuration)) },
+        onEventComplete = { event, newNavState, oldNavState -> event.onComplete(newNavState.configuration, oldNavState.configuration) },
+        backTransformer = { navState ->
+            if (handleBackButton && (navState.configuration != null)) {
+                { OverlayNavState(configuration = null) }
             } else {
-                optionalOf()
+                null
             }
         },
-    ).map { it.active.toChildOverlay() }
+        stateMapper = { _, children -> ChildOverlay(overlay = children.firstOrNull() as? Child.Created?) },
+        childFactory = childFactory,
+    )
+
+private data class OverlayNavState<out C : Any>(
+    val configuration: C?,
+) : NavState<C> {
+
+    override val children: List<SimpleChildNavState<C>> =
+        if (configuration == null) {
+            emptyList()
+        } else {
+            listOf(SimpleChildNavState(configuration = configuration, status = Status.ACTIVE))
+        }
+}
 
 /**
  * A convenience extension function for [ComponentContext.childOverlay].
@@ -79,47 +88,3 @@ inline fun <reified C : Parcelable, T : Any> ComponentContext.childOverlay(
         handleBackButton = handleBackButton,
         childFactory = childFactory,
     )
-
-private fun <C : Parcelable, T : Any> Child.Created<Optional<C>, Optional<T>>.toChildOverlay(): ChildOverlay<C, T> =
-    if ((configuration.value != null) && (instance.value != null)) {
-        ChildOverlay(
-            overlay = Child.Created(
-                configuration = configuration.value,
-                instance = instance.value,
-            )
-        )
-    } else {
-        ChildOverlay()
-    }
-
-private fun <C : Any> configurationStack(configuration: C?): List<Optional<C>> =
-    listOfNotNull(optionalOf(), configuration?.let(::optionalOf))
-
-private class StackNavigationSourceImpl<C : Parcelable>(
-    private val delegate: OverlayNavigationSource<C>,
-) : StackNavigationSource<Optional<C>> {
-
-    private var map = HashMap<(StackNavigationSource.Event<Optional<C>>) -> Unit, (OverlayNavigationSource.Event<C>) -> Unit>()
-
-    override fun subscribe(observer: (StackNavigationSource.Event<Optional<C>>) -> Unit) {
-        check(observer !in map)
-
-        val sourceObserver: (OverlayNavigationSource.Event<C>) -> Unit = { observer(it.toStackEvent()) }
-        map += observer to sourceObserver
-        delegate.subscribe(sourceObserver)
-    }
-
-    private fun OverlayNavigationSource.Event<C>.toStackEvent(): StackNavigationSource.Event<Optional<C>> =
-        StackNavigationSource.Event(
-            transformer = { stack -> configurationStack(transformer(stack.last().value)) },
-            onComplete = { newStack, oldStack ->
-                onComplete(newStack.lastOrNull()?.value, oldStack.lastOrNull()?.value)
-            },
-        )
-
-    override fun unsubscribe(observer: (StackNavigationSource.Event<Optional<C>>) -> Unit) {
-        map.remove(observer)?.also {
-            delegate.unsubscribe(it)
-        }
-    }
-}
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigationSource.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigationSource.kt
index e7fe88b6..5063922c 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigationSource.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/OverlayNavigationSource.kt
@@ -1,15 +1,13 @@
 package com.arkivanov.decompose.router.overlay
 
+import com.arkivanov.decompose.router.children.NavigationSource
+
 /**
  * Represents a source of navigation events for `Child Overlay`.
  *
  * @see OverlayNavigator
  */
-interface OverlayNavigationSource<C : Any> {
-
-    fun subscribe(observer: (Event<C>) -> Unit)
-
-    fun unsubscribe(observer: (Event<C>) -> Unit)
+interface OverlayNavigationSource<C : Any> : NavigationSource<OverlayNavigationSource.Event<C>> {
 
     class Event<C : Any>(
         val transformer: (configuration: C?) -> C?,
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStack.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStack.kt
index 9ca14471..f0275485 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStack.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStack.kt
@@ -1,6 +1,7 @@
 package com.arkivanov.decompose.router.stack
 
 import com.arkivanov.decompose.Child
+import com.arkivanov.decompose.GettingList
 
 /**
  * A state holder for `Child Stack`.
@@ -20,19 +21,8 @@ data class ChildStack<out C : Any, out T : Any>(
     /**
      * Returns the full stack of component configurations, ordered from tail to head.
      */
-    val items: List<Child<C, T>> = Items(active = active, backStack = backStack)
-
-    private class Items<out C : Any, out T : Any>(
-        private val active: Child.Created<C, T>,
-        private val backStack: List<Child<C, T>> = emptyList(),
-    ) : AbstractList<Child<C, T>>() {
-        override val size: Int get() = backStack.size + 1
-
-        override fun get(index: Int): Child<C, T> =
-            when {
-                (index < 0) || (index >= size) -> throw IndexOutOfBoundsException("Index is out of bounds: index=$index, size=$size")
-                index < backStack.size -> backStack[index]
-                else -> active
-            }
-    }
+    val items: List<Child<C, T>> =
+        GettingList(size = backStack.size + 1) { index ->
+            backStack.getOrNull(index) ?: active
+        }
 }
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackController.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackController.kt
deleted file mode 100644
index 06797676..00000000
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackController.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-package com.arkivanov.decompose.router.stack
-
-import com.arkivanov.decompose.Child
-import com.arkivanov.decompose.SerializedQueue
-import com.arkivanov.decompose.ensureNeverFrozen
-import com.arkivanov.decompose.router.stack.StackNavigationSource.Event
-import com.arkivanov.decompose.value.MutableValue
-import com.arkivanov.decompose.value.Value
-import com.arkivanov.essenty.backhandler.BackCallback
-import com.arkivanov.essenty.backhandler.BackHandler
-import com.arkivanov.essenty.lifecycle.Lifecycle
-import com.arkivanov.essenty.lifecycle.doOnDestroy
-
-internal class ChildStackController<out C : Any, out T : Any>(
-    private val source: StackNavigationSource<C>,
-    lifecycle: Lifecycle,
-    private val backHandler: BackHandler?,
-    private val stackHolder: StackHolder<C, T>,
-    private val controller: StackController<C, T>,
-) {
-
-    init {
-        ensureNeverFrozen()
-    }
-
-    private val queue = SerializedQueue(::navigateActual)
-    private val _stack = MutableValue(stackHolder.stack.toState())
-    val stack: Value<ChildStack<C, T>> get() = _stack
-
-    private val backCallback: BackCallback =
-        BackCallback(isEnabled = _stack.value.backStack.isNotEmpty()) {
-            queue.offer(Event(transformer = { it.dropLast(1) }))
-        }
-
-    init {
-        backHandler?.register(backCallback)
-
-        val eventObserver = queue::offer
-        source.subscribe(eventObserver)
-        lifecycle.doOnDestroy { source.unsubscribe(eventObserver) }
-    }
-
-    private fun navigateActual(event: Event<C>) {
-        val oldStack = stackHolder.stack
-        val newStack = controller.navigate(oldStack = oldStack, transformer = event.transformer)
-        stackHolder.stack = newStack
-        backCallback.isEnabled = newStack.backStack.isNotEmpty()
-        _stack.value = newStack.toState()
-        event.onComplete(newStack.configurationStack, oldStack.configurationStack)
-    }
-
-    private fun RouterStack<C, T>.toState(): ChildStack<C, T> =
-        ChildStack(
-            active = Child.Created(configuration = active.configuration, instance = active.instance),
-            backStack = backStack.map { it.toChild() }
-        )
-
-    private fun RouterEntry<C, T>.toChild(): Child<C, T> =
-        when (this) {
-            is RouterEntry.Created -> Child.Created(configuration = configuration, instance = instance)
-            is RouterEntry.Destroyed -> Child.Destroyed(configuration = configuration)
-        }
-}
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt
index 39f88ac9..4375cd5a 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt
@@ -1,15 +1,18 @@
 package com.arkivanov.decompose.router.stack
 
+import com.arkivanov.decompose.Child
 import com.arkivanov.decompose.ComponentContext
-import com.arkivanov.decompose.backhandler.child
+import com.arkivanov.decompose.ExperimentalDecomposeApi
+import com.arkivanov.decompose.GettingList
+import com.arkivanov.decompose.router.children.ChildNavState.Status
+import com.arkivanov.decompose.router.children.NavState
+import com.arkivanov.decompose.router.children.SimpleChildNavState
+import com.arkivanov.decompose.router.children.children
 import com.arkivanov.decompose.value.Value
-import com.arkivanov.essenty.backhandler.BackHandler
-import com.arkivanov.essenty.instancekeeper.InstanceKeeper
-import com.arkivanov.essenty.lifecycle.Lifecycle
 import com.arkivanov.essenty.parcelable.Parcelable
 import com.arkivanov.essenty.parcelable.ParcelableContainer
+import com.arkivanov.essenty.parcelable.Parcelize
 import com.arkivanov.essenty.parcelable.consumeRequired
-import com.arkivanov.essenty.statekeeper.StateKeeper
 import kotlin.reflect.KClass
 
 /**
@@ -25,6 +28,7 @@ import kotlin.reflect.KClass
  * @param childFactory a factory function that creates new child instances.
  * @return an observable [Value] of [ChildStack].
  */
+@OptIn(ExperimentalDecomposeApi::class)
 fun <C : Parcelable, T : Any> ComponentContext.childStack(
     source: StackNavigationSource<C>,
     initialStack: () -> List<C>,
@@ -33,20 +37,112 @@ fun <C : Parcelable, T : Any> ComponentContext.childStack(
     handleBackButton: Boolean = false,
     childFactory: (configuration: C, ComponentContext) -> T,
 ): Value<ChildStack<C, T>> =
-    childStack(
-        lifecycle = lifecycle,
-        stateKeeper = stateKeeper,
-        instanceKeeper = instanceKeeper,
-        backHandler = backHandler,
+    children(
         source = source,
-        initialStack = initialStack,
-        saveConfiguration = { ParcelableContainer(it) },
-        restoreConfiguration = { it.consumeRequired(configurationClass) },
         key = key,
-        handleBackButton = handleBackButton,
+        initialNavState = {
+            val stack = initialStack()
+            check(stack.isNotEmpty()) { "Configuration stack must not be empty" }
+
+            StackNavState(
+                active = stack.last(),
+                backStack = stack.dropLast(1).map { configuration ->
+                    SimpleChildNavState(configuration = configuration, status = Status.DESTROYED)
+                },
+            )
+        },
+        saveNavState = { navState ->
+            ParcelableContainer(
+                StackSavedNavState(
+                    activeConfiguration = ParcelableContainer(navState.active),
+                    backStackConfigurations = navState.backStack.map { ParcelableContainer(it.configuration) },
+                )
+            )
+        },
+        restoreNavState = { container ->
+            val state = container.consumeRequired<StackSavedNavState>()
+
+            StackNavState(
+                active = state.activeConfiguration.consumeRequired(configurationClass),
+                backStack = state.backStackConfigurations.map {
+                    SimpleChildNavState(
+                        configuration = it.consumeRequired(configurationClass),
+                        status = Status.DESTROYED,
+                    )
+                }
+            )
+        },
+        navTransformer = { navState, event ->
+            val newStack = event.transformer(navState.configurations)
+            check(newStack.isNotEmpty()) { "Configuration stack must not be empty" }
+
+            val oldStatuses = navState.children.associateBy(keySelector = { it.configuration }, valueTransform = { it.status })
+
+            StackNavState(
+                active = newStack.last(),
+                backStack = newStack
+                    .dropLast(1)
+                    .map { configuration ->
+                        SimpleChildNavState(
+                            configuration = configuration,
+                            status = when (oldStatuses[configuration]) {
+                                Status.DESTROYED -> Status.DESTROYED
+                                Status.INACTIVE -> Status.INACTIVE
+                                Status.ACTIVE -> Status.INACTIVE
+                                null -> Status.DESTROYED
+                            },
+                        )
+                    },
+            )
+        },
+        onEventComplete = { event, newNavState, oldNavState ->
+            event.onComplete(newNavState.configurations, oldNavState.configurations)
+        },
+        backTransformer = { navState ->
+            if (handleBackButton && navState.backStack.isNotEmpty()) {
+                {
+                    StackNavState(
+                        active = navState.backStack.last().configuration,
+                        backStack = navState.backStack.dropLast(1),
+                    )
+                }
+            } else {
+                null
+            }
+        },
+        stateMapper = { _, children ->
+            ChildStack(
+                active = children.last() as Child.Created,
+                backStack = children.dropLast(1),
+            )
+        },
         childFactory = childFactory,
     )
 
+private data class StackNavState<out C : Any>(
+    val active: C,
+    val backStack: List<SimpleChildNavState<C>>,
+) : NavState<C> {
+
+    private val activeChildNavState = SimpleChildNavState(configuration = active, status = Status.ACTIVE)
+
+    override val children: List<SimpleChildNavState<C>> =
+        GettingList(size = backStack.size + 1) { index ->
+            backStack.getOrNull(index) ?: activeChildNavState
+        }
+
+    val configurations: List<C> =
+        GettingList(size = children.size) { index ->
+            children[index].configuration
+        }
+}
+
+@Parcelize
+private class StackSavedNavState(
+    val activeConfiguration: ParcelableContainer,
+    val backStackConfigurations: List<ParcelableContainer>,
+) : Parcelable
+
 /**
  * A convenience extension function for [ComponentContext.childStack].
  */
@@ -84,48 +180,3 @@ inline fun <reified C : Parcelable, T : Any> ComponentContext.childStack(
         handleBackButton = handleBackButton,
         childFactory = childFactory,
     )
-
-internal fun <C : Any, T : Any> childStack(
-    lifecycle: Lifecycle,
-    stateKeeper: StateKeeper,
-    instanceKeeper: InstanceKeeper,
-    backHandler: BackHandler,
-    source: StackNavigationSource<C>,
-    initialStack: () -> List<C>,
-    saveConfiguration: (C) -> ParcelableContainer,
-    restoreConfiguration: (ParcelableContainer) -> C,
-    key: String,
-    handleBackButton: Boolean = false,
-    childFactory: (configuration: C, ComponentContext) -> T,
-): Value<ChildStack<C, T>> {
-    val routerBackHandler = backHandler.takeIf { handleBackButton }?.child()
-
-    val routerEntryFactory =
-        RouterEntryFactoryImpl(
-            lifecycle = lifecycle,
-            backHandler = backHandler.child(),
-            childFactory = childFactory,
-        )
-
-    val controller =
-        ChildStackController(
-            lifecycle = lifecycle,
-            backHandler = routerBackHandler,
-            stackHolder = StackHolderImpl(
-                initialStack = initialStack,
-                lifecycle = lifecycle,
-                key = key,
-                stackSaver = StackSaverImpl(
-                    stateKeeper = stateKeeper,
-                    saveConfiguration = saveConfiguration,
-                    restoreConfiguration = restoreConfiguration,
-                ),
-                instanceKeeper = instanceKeeper,
-                routerEntryFactory = routerEntryFactory
-            ),
-            controller = StackControllerImpl(routerEntryFactory = routerEntryFactory),
-            source = source,
-        )
-
-    return controller.stack
-}
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterEntry.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterEntry.kt
deleted file mode 100644
index f731a2ad..00000000
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterEntry.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.arkivanov.decompose.router.stack
-
-import com.arkivanov.decompose.backhandler.ChildBackHandler
-import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher
-import com.arkivanov.essenty.lifecycle.LifecycleRegistry
-import com.arkivanov.essenty.parcelable.ParcelableContainer
-import com.arkivanov.essenty.statekeeper.StateKeeperDispatcher
-
-internal sealed class RouterEntry<out C : Any, out T : Any> {
-    abstract val configuration: C
-    abstract val savedState: ParcelableContainer?
-
-    data class Created<out C : Any, out T : Any>(
-        override val configuration: C,
-        override val savedState: ParcelableContainer? = null,
-        val instance: T,
-        val lifecycleRegistry: LifecycleRegistry,
-        val stateKeeperDispatcher: StateKeeperDispatcher,
-        val instanceKeeperDispatcher: InstanceKeeperDispatcher,
-        val backHandler: ChildBackHandler,
-    ) : RouterEntry<C, T>()
-
-    data class Destroyed<out C : Any>(
-        override val configuration: C,
-        override val savedState: ParcelableContainer? = null
-    ) : RouterEntry<C, Nothing>()
-}
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterEntryExt.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterEntryExt.kt
deleted file mode 100644
index c087f7d1..00000000
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterEntryExt.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.arkivanov.decompose.router.stack
-
-import com.arkivanov.essenty.lifecycle.destroy
-
-internal fun List<RouterEntry<*, *>>.destroy() {
-    asReversed().forEach { entry ->
-        when (entry) {
-            is RouterEntry.Created -> {
-                entry.instanceKeeperDispatcher.destroy()
-                entry.lifecycleRegistry.destroy()
-            }
-
-            is RouterEntry.Destroyed -> Unit
-        }.let {}
-    }
-}
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterEntryFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterEntryFactory.kt
deleted file mode 100644
index 9fa4f775..00000000
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterEntryFactory.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.arkivanov.decompose.router.stack
-
-import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher
-import com.arkivanov.essenty.parcelable.ParcelableContainer
-
-internal interface RouterEntryFactory<C : Any, out T : Any> {
-
-    operator fun invoke(
-        configuration: C,
-        savedState: ParcelableContainer? = null,
-        instanceKeeperDispatcher: InstanceKeeperDispatcher? = null
-    ): RouterEntry.Created<C, T>
-}
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterEntryFactoryImpl.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterEntryFactoryImpl.kt
deleted file mode 100644
index 011a8137..00000000
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterEntryFactoryImpl.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.arkivanov.decompose.router.stack
-
-import com.arkivanov.decompose.ComponentContext
-import com.arkivanov.decompose.DefaultComponentContext
-import com.arkivanov.decompose.backhandler.childBackHandler
-import com.arkivanov.decompose.lifecycle.MergedLifecycle
-import com.arkivanov.essenty.backhandler.BackHandler
-import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher
-import com.arkivanov.essenty.lifecycle.Lifecycle
-import com.arkivanov.essenty.lifecycle.LifecycleRegistry
-import com.arkivanov.essenty.parcelable.ParcelableContainer
-import com.arkivanov.essenty.statekeeper.StateKeeperDispatcher
-
-internal class RouterEntryFactoryImpl<C : Any, out T : Any>(
-    private val lifecycle: Lifecycle,
-    private val backHandler: BackHandler,
-    private val childFactory: (configuration: C, ComponentContext) -> T
-) : RouterEntryFactory<C, T> {
-
-    override fun invoke(
-        configuration: C,
-        savedState: ParcelableContainer?,
-        instanceKeeperDispatcher: InstanceKeeperDispatcher?
-    ): RouterEntry.Created<C, T> {
-        val componentLifecycleRegistry = LifecycleRegistry()
-        val mergedLifecycle = MergedLifecycle(lifecycle, componentLifecycleRegistry)
-        val stateKeeperDispatcher = StateKeeperDispatcher(savedState)
-        val instanceKeeperRegistry = instanceKeeperDispatcher ?: InstanceKeeperDispatcher()
-        val backHandler = backHandler.childBackHandler()
-
-        val component =
-            childFactory(
-                configuration,
-                DefaultComponentContext(
-                    lifecycle = mergedLifecycle,
-                    stateKeeper = stateKeeperDispatcher,
-                    instanceKeeper = instanceKeeperRegistry,
-                    backHandler = backHandler,
-                )
-            )
-
-        return RouterEntry.Created(
-            configuration = configuration,
-            instance = component,
-            lifecycleRegistry = componentLifecycleRegistry,
-            stateKeeperDispatcher = stateKeeperDispatcher,
-            instanceKeeperDispatcher = instanceKeeperRegistry,
-            backHandler = backHandler,
-        )
-    }
-}
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterStack.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterStack.kt
deleted file mode 100644
index 6d9222d8..00000000
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterStack.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.arkivanov.decompose.router.stack
-
-internal data class RouterStack<out C : Any, out T : Any>(
-    val active: RouterEntry.Created<C, T>,
-    val backStack: List<RouterEntry<C, T>> = emptyList(),
-)
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterStackExt.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterStackExt.kt
deleted file mode 100644
index dcb3fc12..00000000
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterStackExt.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.arkivanov.decompose.router.stack
-
-internal val <C : Any> RouterStack<C, *>.configurationBackStack: List<C>
-    get() =
-        object : AbstractList<C>() {
-            override val size: Int get() = backStack.size
-
-            override fun get(index: Int): C = backStack[index].configuration
-        }
-
-internal val <C : Any> RouterStack<C, *>.configurationStack: List<C>
-    get() =
-        object : AbstractList<C>() {
-            override val size: Int get() = backStack.size + 1
-
-            override fun get(index: Int): C = (backStack.getOrNull(index) ?: active).configuration
-        }
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackController.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackController.kt
deleted file mode 100644
index 3688f07f..00000000
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackController.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.arkivanov.decompose.router.stack
-
-internal interface StackController<C : Any, T : Any> {
-
-    fun navigate(
-        oldStack: RouterStack<C, T>,
-        transformer: (stack: List<C>) -> List<C>,
-    ): RouterStack<C, T>
-}
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackControllerImpl.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackControllerImpl.kt
deleted file mode 100644
index 65577adb..00000000
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackControllerImpl.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-package com.arkivanov.decompose.router.stack
-
-import com.arkivanov.essenty.lifecycle.destroy
-import com.arkivanov.essenty.lifecycle.pause
-import com.arkivanov.essenty.lifecycle.resume
-import com.arkivanov.essenty.lifecycle.stop
-
-internal class StackControllerImpl<C : Any, T : Any>(
-    private val routerEntryFactory: RouterEntryFactory<C, T>
-) : StackController<C, T> {
-
-    override fun navigate(
-        oldStack: RouterStack<C, T>,
-        transformer: (stack: List<C>) -> List<C>,
-    ): RouterStack<C, T> {
-        val oldConfigurationStack = oldStack.configurationStack
-        val newConfigurationStack = transformer(oldConfigurationStack)
-
-        if (newConfigurationStack == oldConfigurationStack) {
-            return oldStack
-        }
-
-        check(newConfigurationStack.isNotEmpty()) { "Configuration stack can not be empty" }
-
-        val newConfigurationStackSet = newConfigurationStack.toSet()
-        check(newConfigurationStackSet.size == newConfigurationStack.size) { "Configurations in the stack must be unique" }
-
-        val oldBackStackMap = oldStack.backStack.associateBy { it.configuration }
-
-        val newActiveConfiguration = newConfigurationStack.last()
-        val newActiveEntry: RouterEntry.Created<C, *>
-        if (newActiveConfiguration == oldStack.active.configuration) {
-            newActiveEntry = oldStack.active
-        } else {
-            newActiveEntry =
-                when (val entry = oldStack.backStack.find { it.configuration == newActiveConfiguration }) {
-                    is RouterEntry.Created -> entry.copy(savedState = null)
-                    is RouterEntry.Destroyed -> routerEntryFactory(entry.configuration, entry.savedState)
-                    null -> routerEntryFactory(newActiveConfiguration)
-                }
-
-            oldStack.active.backHandler.stop()
-            newActiveEntry.backHandler.start()
-            oldStack.active.lifecycleRegistry.pause()
-            newActiveEntry.lifecycleRegistry.resume()
-            oldStack.active.lifecycleRegistry.stop()
-            if (oldStack.active.configuration !in newConfigurationStackSet) {
-                oldStack.active.instanceKeeperDispatcher.destroy()
-                oldStack.active.lifecycleRegistry.destroy()
-            }
-        }
-
-        val newBackStackEntries =
-            newConfigurationStack.dropLast(1).map { configuration ->
-                if (oldStack.active.configuration == configuration) {
-                    oldStack.active.copy(savedState = oldStack.active.stateKeeperDispatcher.save())
-                } else {
-                    oldBackStackMap[configuration] ?: RouterEntry.Destroyed(configuration = configuration)
-                }
-            }
-
-        oldStack
-            .backStack
-            .filterNot { it.configuration in newConfigurationStackSet }
-            .destroy()
-
-        return RouterStack(active = newActiveEntry, backStack = newBackStackEntries)
-    }
-}
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackHolder.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackHolder.kt
deleted file mode 100644
index 3d199cb6..00000000
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackHolder.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.arkivanov.decompose.router.stack
-
-internal interface StackHolder<C : Any, T : Any> {
-
-    var stack: RouterStack<C, T>
-}
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackHolderImpl.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackHolderImpl.kt
deleted file mode 100644
index e02acb2b..00000000
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackHolderImpl.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-package com.arkivanov.decompose.router.stack
-
-import com.arkivanov.essenty.instancekeeper.InstanceKeeper
-import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher
-import com.arkivanov.essenty.instancekeeper.getOrCreate
-import com.arkivanov.essenty.lifecycle.Lifecycle
-import com.arkivanov.essenty.lifecycle.destroy
-import com.arkivanov.essenty.lifecycle.doOnDestroy
-import com.arkivanov.essenty.lifecycle.resume
-import kotlin.properties.Delegates.observable
-
-internal class StackHolderImpl<C : Any, T : Any>(
-    private val initialStack: () -> List<C>,
-    lifecycle: Lifecycle,
-    private val key: String,
-    private val stackSaver: StackSaver<C>,
-    instanceKeeper: InstanceKeeper,
-    private val routerEntryFactory: RouterEntryFactory<C, T>,
-) : StackHolder<C, T> {
-
-    private val retainedInstance: RetainedInstance<C, T> = instanceKeeper.getOrCreate(key, ::RetainedInstance)
-
-    override var stack: RouterStack<C, T>
-        by observable(restoreStack() ?: createInitialStack()) { _, _, newValue ->
-            retainedInstance.activeEntry = newValue.active
-        }
-
-    init {
-        stackSaver.register(key) { stack }
-        retainedInstance.activeEntry = stack.active
-        stack.active.backHandler.start()
-        stack.active.lifecycleRegistry.resume()
-
-        lifecycle.doOnDestroy(::destroy)
-    }
-
-    private fun destroy() {
-        stackSaver.unregister(key)
-
-        val stack = stack
-        stack.active.lifecycleRegistry.destroy()
-        stack.backStack.destroy()
-    }
-
-    private fun createInitialStack(): RouterStack<C, T> {
-        val initialStack = initialStack()
-
-        check(initialStack.isNotEmpty()) { "Initial stack can not be empty" }
-        check(initialStack.toSet().size == initialStack.size) { "Configurations in the initial stack must be unique" }
-
-        return RouterStack(
-            active = routerEntryFactory(initialStack.last()),
-            backStack = initialStack.dropLast(1).map { RouterEntry.Destroyed(configuration = it) }
-        )
-    }
-
-    private fun restoreStack(): RouterStack<C, T>? {
-        val savedStack: StackSaver.RestoredStack<C>? = stackSaver.restore(key)
-        val activeRetainedEntry: RouterEntry.Created<C, T>? = retainedInstance.activeEntry
-
-        var activeInstanceKeeperDispatcher: InstanceKeeperDispatcher? = null
-        if (activeRetainedEntry != null) {
-            if (savedStack != null) {
-                activeInstanceKeeperDispatcher = activeRetainedEntry.instanceKeeperDispatcher
-            } else {
-                retainedInstance.destroyActiveEntry()
-            }
-        }
-
-        return savedStack?.restore(activeInstanceKeeperDispatcher)
-    }
-
-    private fun StackSaver.RestoredStack<C>.restore(activeInstanceKeeperDispatcher: InstanceKeeperDispatcher?): RouterStack<C, T> =
-        RouterStack(
-            active = routerEntryFactory(
-                configuration = active.configuration,
-                savedState = active.savedState,
-                instanceKeeperDispatcher = activeInstanceKeeperDispatcher
-            ),
-            backStack = backStack
-        )
-
-    private class RetainedInstance<C : Any, T : Any> : InstanceKeeper.Instance {
-        var activeEntry: RouterEntry.Created<C, T>? = null
-
-        override fun onDestroy() {
-            destroyActiveEntry()
-        }
-
-        fun destroyActiveEntry() {
-            activeEntry?.instanceKeeperDispatcher?.destroy()
-            activeEntry = null
-        }
-    }
-}
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigationSource.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigationSource.kt
index eb9b6d6a..a4e09c0c 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigationSource.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigationSource.kt
@@ -1,15 +1,13 @@
 package com.arkivanov.decompose.router.stack
 
+import com.arkivanov.decompose.router.children.NavigationSource
+
 /**
  * Represents a source of navigation events for `Child Stack`.
  *
  * @see StackNavigator
  */
-interface StackNavigationSource<C : Any> {
-
-    fun subscribe(observer: (Event<C>) -> Unit)
-
-    fun unsubscribe(observer: (Event<C>) -> Unit)
+interface StackNavigationSource<C : Any> : NavigationSource<StackNavigationSource.Event<C>> {
 
     class Event<C : Any>(
         val transformer: (stack: List<C>) -> List<C>,
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackSaver.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackSaver.kt
deleted file mode 100644
index d3a93cb0..00000000
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackSaver.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.arkivanov.decompose.router.stack
-
-internal interface StackSaver<C : Any> {
-
-    fun register(key: String, supplier: () -> RouterStack<C, *>)
-
-    fun unregister(key: String)
-
-    fun restore(key: String): RestoredStack<C>?
-
-    class RestoredStack<out C : Any>(
-        val active: RouterEntry.Destroyed<C>,
-        val backStack: List<RouterEntry.Destroyed<C>>,
-    )
-}
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackSaverImpl.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackSaverImpl.kt
deleted file mode 100644
index b5fdf7a4..00000000
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackSaverImpl.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-package com.arkivanov.decompose.router.stack
-
-import com.arkivanov.decompose.router.stack.StackSaver.RestoredStack
-import com.arkivanov.essenty.parcelable.Parcelable
-import com.arkivanov.essenty.parcelable.ParcelableContainer
-import com.arkivanov.essenty.parcelable.Parcelize
-import com.arkivanov.essenty.statekeeper.StateKeeper
-import com.arkivanov.essenty.statekeeper.consume
-
-internal class StackSaverImpl<C : Any>(
-    private val stateKeeper: StateKeeper,
-    private val saveConfiguration: (C) -> ParcelableContainer,
-    private val restoreConfiguration: (ParcelableContainer) -> C,
-) : StackSaver<C> {
-
-    override fun register(key: String, supplier: () -> RouterStack<C, *>) {
-        check(!stateKeeper.isRegistered(key)) {
-            "The key \"$key\" is already in use. If there are multiple Child Stacks in one component, " +
-                "make sure you supplied different key for each Child Stack. Also make sure there is only instance " +
-                "of the root component at a time."
-        }
-
-        stateKeeper.register(key) { supplier().save() }
-    }
-
-    private fun RouterStack<C, *>.save(): SavedState =
-        SavedState(
-            active = SavedEntry(
-                configuration = saveConfiguration(active.configuration),
-                savedState = active.stateKeeperDispatcher.save()
-            ),
-            backStack = backStack.map {
-                SavedEntry(
-                    configuration = saveConfiguration(it.configuration),
-                    savedState = it.savedState
-                )
-            }
-        )
-
-    override fun unregister(key: String) {
-        stateKeeper.unregister(key)
-    }
-
-    override fun restore(key: String): RestoredStack<C>? =
-        stateKeeper
-            .consume<SavedState>(key)
-            ?.restore()
-
-    private fun SavedState.restore(): RestoredStack<C> =
-        RestoredStack(
-            active = active.restore(),
-            backStack = backStack.map { it.restore() }
-        )
-
-    private fun SavedEntry.restore(): RouterEntry.Destroyed<C> =
-        RouterEntry.Destroyed(
-            configuration = restoreConfiguration(configuration),
-            savedState = savedState
-        )
-
-    @Parcelize
-    private class SavedState(
-        val active: SavedEntry,
-        val backStack: List<SavedEntry>
-    ) : Parcelable
-
-    @Parcelize
-    private class SavedEntry(
-        val configuration: ParcelableContainer,
-        val savedState: ParcelableContainer?
-    ) : Parcelable
-}


Test patch:
diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenTestBase.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenTestBase.kt
index 800c1366..4ec37549 100644
--- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenTestBase.kt
+++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenTestBase.kt
@@ -3,6 +3,7 @@ package com.arkivanov.decompose.router.children
 import com.arkivanov.decompose.Child
 import com.arkivanov.decompose.ComponentContext
 import com.arkivanov.decompose.DefaultComponentContext
+import com.arkivanov.decompose.ExperimentalDecomposeApi
 import com.arkivanov.decompose.Relay
 import com.arkivanov.decompose.statekeeper.TestStateKeeperDispatcher
 import com.arkivanov.decompose.value.Value
@@ -17,6 +18,7 @@ import com.arkivanov.essenty.parcelable.consumeRequired
 import kotlin.test.BeforeTest
 import kotlin.test.assertContentEquals
 
+@OptIn(ExperimentalDecomposeApi::class)
 internal open class ChildrenTestBase {
 
     private val navigation = TestNavigation()
diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlayIntegrationTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlayIntegrationTest.kt
index 249f9700..3fbf902e 100644
--- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlayIntegrationTest.kt
+++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlayIntegrationTest.kt
@@ -1,10 +1,12 @@
 package com.arkivanov.decompose.router.overlay
 
+import com.arkivanov.decompose.Child
 import com.arkivanov.decompose.ComponentContext
 import com.arkivanov.decompose.DefaultComponentContext
 import com.arkivanov.decompose.router.TestInstance
 import com.arkivanov.decompose.statekeeper.TestStateKeeperDispatcher
 import com.arkivanov.decompose.value.Value
+import com.arkivanov.decompose.value.getValue
 import com.arkivanov.essenty.backhandler.BackDispatcher
 import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher
 import com.arkivanov.essenty.instancekeeper.getOrCreate
@@ -19,7 +21,6 @@ import kotlin.test.Test
 import kotlin.test.assertEquals
 import kotlin.test.assertFalse
 import kotlin.test.assertNotNull
-import kotlin.test.assertNull
 import kotlin.test.assertSame
 import kotlin.test.assertTrue
 
@@ -47,81 +48,81 @@ class ChildOverlayIntegrationTest {
 
     @Test
     fun WHEN_created_without_configuration_THEN_overlay_not_active() {
-        val overlay = context.childOverlay(initialConfiguration = null)
+        val overlay by context.childOverlay(initialConfiguration = null)
 
-        overlay.value.assertOverlay(null)
+        overlay.assertOverlay(null)
     }
 
     @Test
     fun WHEN_created_with_configuration_THEN_overlay_active() {
-        val overlay = context.childOverlay(initialConfiguration = Config(1))
+        val overlay by context.childOverlay(initialConfiguration = Config(1))
 
-        overlay.value.assertOverlay(1)
+        overlay.assertOverlay(1)
     }
 
     @Test
     fun GIVEN_not_active_WHEN_activate_THEN_overlay_active() {
-        val overlay = context.childOverlay(initialConfiguration = null)
+        val overlay by context.childOverlay(initialConfiguration = null)
 
         navigation.activate(Config(1))
 
-        overlay.value.assertOverlay(1)
+        overlay.assertOverlay(1)
     }
 
     @Test
     fun GIVEN_active_WHEN_dismiss_THEN_overlay_not_active() {
-        val overlay = context.childOverlay(initialConfiguration = Config(1))
+        val overlay by context.childOverlay(initialConfiguration = Config(1))
 
         navigation.dismiss()
 
-        overlay.value.assertOverlay(null)
+        overlay.assertOverlay(null)
     }
 
     @Test
     fun GIVEN_active_WHEN_activate_with_same_configuration_THEN_same_overlay_active() {
-        val overlay = context.childOverlay(initialConfiguration = Config(1))
+        val overlay by context.childOverlay(initialConfiguration = Config(1))
 
         navigation.activate(Config(1))
 
-        overlay.value.assertOverlay(1)
+        overlay.assertOverlay(1)
     }
 
     @Test
     fun GIVEN_active_WHEN_activate_with_same_configuration_THEN_same_instance_active() {
-        val overlay = context.childOverlay(initialConfiguration = Config(1))
-        val instance = overlay.value.overlay?.instance
+        val overlay by context.childOverlay(initialConfiguration = Config(1))
+        val instance = overlay.requireOverlay().instance
 
         navigation.activate(Config(1))
 
-        assertSame(instance, overlay.value.overlay?.instance)
+        assertSame(instance, overlay.overlay?.instance)
     }
 
     @Test
     fun GIVEN_active_WHEN_activate_with_other_configuration_THEN_other_overlay_active() {
-        val overlay = context.childOverlay(initialConfiguration = Config(1))
+        val overlay by context.childOverlay(initialConfiguration = Config(1))
 
         navigation.activate(Config(2))
 
-        overlay.value.assertOverlay(2)
+        overlay.assertOverlay(2)
     }
 
     @Test
     fun GIVEN_not_active_WHEN_activate_THEN_lifecycle_resumed() {
-        val overlay = context.childOverlay(initialConfiguration = null)
+        val overlay by context.childOverlay(initialConfiguration = null)
 
         navigation.activate(Config(1))
 
-        assertEquals(Lifecycle.State.RESUMED, overlay.value.overlay?.instance?.lifecycle?.state)
+        assertEquals(Lifecycle.State.RESUMED, overlay.requireOverlay().instance.lifecycle.state)
     }
 
     @Test
     fun GIVEN_active_WHEN_dismiss_THEN_lifecycle_destroyed() {
-        val overlay = context.childOverlay(initialConfiguration = Config(1))
-        val lifecycle = overlay.value.overlay?.instance?.lifecycle
+        val overlay by context.childOverlay(initialConfiguration = Config(1))
+        val lifecycle = overlay.requireOverlay().instance.lifecycle
 
         navigation.dismiss()
 
-        assertEquals(Lifecycle.State.DESTROYED, lifecycle?.state)
+        assertEquals(Lifecycle.State.DESTROYED, lifecycle.state)
     }
 
     @Test
@@ -140,11 +141,11 @@ class ChildOverlayIntegrationTest {
 
     @Test
     fun GIVEN_active_WHEN_back_pressed_THEN_overlay_not_active() {
-        val overlay = context.childOverlay(initialConfiguration = Config(1))
+        val overlay by context.childOverlay(initialConfiguration = Config(1))
 
         backDispatcher.back()
 
-        overlay.value.assertOverlay(null)
+        overlay.assertOverlay(null)
     }
 
     @Test
@@ -164,23 +165,23 @@ class ChildOverlayIntegrationTest {
         val savedState = oldStateKeeper.save()
         val newStateKeeper = TestStateKeeperDispatcher(savedState)
         val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper)
-        val newOverlay = newContext.childOverlay(initialConfiguration = null, persistent = true)
+        val newOverlay by newContext.childOverlay(initialConfiguration = null, persistent = true)
 
-        newOverlay.value.assertOverlay(1)
+        newOverlay.assertOverlay(1)
     }
 
     @Test
     fun GIVEN_persistent_WHEN_recreated_THEN_overlay_state_restored() {
         val oldStateKeeper = TestStateKeeperDispatcher()
         val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper)
-        val overlay = oldContext.childOverlay(initialConfiguration = Config(1), persistent = true)
-        overlay.value.overlay?.instance?.stateKeeper?.register("key") { Config(10) }
+        val overlay by oldContext.childOverlay(initialConfiguration = Config(1), persistent = true)
+        overlay.requireOverlay().instance.stateKeeper.register("key") { Config(10) }
 
         val savedState = oldStateKeeper.save()
         val newStateKeeper = TestStateKeeperDispatcher(savedState)
         val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper)
-        val newOverlay = newContext.childOverlay(initialConfiguration = null, persistent = true)
-        val restoredState = newOverlay.value.overlay?.instance?.stateKeeper?.consume<Config>("key")
+        val newOverlay by newContext.childOverlay(initialConfiguration = null, persistent = true)
+        val restoredState = newOverlay.requireOverlay().instance.stateKeeper.consume<Config>("key")
 
         assertEquals(Config(10), restoredState)
     }
@@ -194,9 +195,9 @@ class ChildOverlayIntegrationTest {
         val savedState = oldStateKeeper.save()
         val newStateKeeper = TestStateKeeperDispatcher(savedState)
         val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper)
-        val newOverlay = newContext.childOverlay(initialConfiguration = null, persistent = false)
+        val newOverlay by newContext.childOverlay(initialConfiguration = null, persistent = false)
 
-        newOverlay.value.assertOverlay(null)
+        newOverlay.assertOverlay(null)
     }
 
     @Test
@@ -207,27 +208,36 @@ class ChildOverlayIntegrationTest {
     }
 
     @Test
-    fun WHEN_created_not_persistent_THEN_not_registered_in_parent_StateKeeper() {
-        context.childOverlay(initialConfiguration = Config(1), persistent = false)
+    fun GIVEN_persistent_WHEN_recreated_THEN_overlay_instance_retained() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val instanceKeeper = InstanceKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper)
+        val oldOverlay by oldContext.childOverlay(initialConfiguration = Config(1), persistent = true)
+        val oldInstance = oldOverlay.requireOverlay().instance.instanceKeeper.getOrCreate(::TestInstance)
+
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper)
+        val newOverlay by newContext.childOverlay(initialConfiguration = null, persistent = true)
+        val retainedInstance = newOverlay.requireOverlay().instance.instanceKeeper.getOrCreate(::TestInstance)
 
-        stateKeeperDispatcher.assertSupplierNotRegistered(key = "key")
+        assertSame(oldInstance, retainedInstance)
     }
 
     @Test
-    fun GIVEN_persistent_WHEN_recreated_THEN_overlay_instance_retained() {
+    fun GIVEN_persistent_WHEN_recreated_THEN_overlay_instance_not_destroyed() {
         val oldStateKeeper = TestStateKeeperDispatcher()
         val instanceKeeper = InstanceKeeperDispatcher()
         val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper)
-        val overlay = oldContext.childOverlay(initialConfiguration = Config(1), persistent = true)
-        val oldInstance = overlay.value.overlay?.instance?.instanceKeeper?.getOrCreate(::TestInstance)
+        val oldOverlay by oldContext.childOverlay(initialConfiguration = Config(1), persistent = true)
+        val instance = oldOverlay.requireOverlay().instance.instanceKeeper.getOrCreate(::TestInstance)
 
         val savedState = oldStateKeeper.save()
         val newStateKeeper = TestStateKeeperDispatcher(savedState)
         val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper)
-        val newOverlay = newContext.childOverlay(initialConfiguration = null, persistent = true)
-        val retainedInstance = newOverlay.value.overlay?.instance?.instanceKeeper?.getOrCreate(::TestInstance)
+        newContext.childOverlay(initialConfiguration = null, persistent = true)
 
-        assertSame(oldInstance, retainedInstance)
+        assertFalse(instance.isDestroyed)
     }
 
     @Test
@@ -238,10 +248,19 @@ class ChildOverlayIntegrationTest {
     }
 
     @Test
-    fun WHEN_created_not_persistent_THEN_not_registered_in_parent_InstanceKeeper() {
-        context.childOverlay(initialConfiguration = Config(1), persistent = false)
+    fun GIVEN_not_persistent_WHEN_recreated_THEN_overlay_instance_destroyed() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val instanceKeeper = InstanceKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper)
+        val oldOverlay by oldContext.childOverlay(initialConfiguration = Config(1), persistent = false)
+        val instance = oldOverlay.requireOverlay().instance.instanceKeeper.getOrCreate(::TestInstance)
 
-        assertNull(instanceKeeperDispatcher.get(key = "key"))
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper)
+        newContext.childOverlay(initialConfiguration = null, persistent = false)
+
+        assertTrue(instance.isDestroyed)
     }
 
     private fun ComponentContext.childOverlay(
@@ -257,6 +276,9 @@ class ChildOverlayIntegrationTest {
             childFactory = ::Component,
         )
 
+    private fun ChildOverlay<Config, Component>.requireOverlay(): Child.Created<Config, Component> =
+        requireNotNull(overlay)
+
     private fun ChildOverlay<Config, Component>.assertOverlay(id: Int?) {
         assertEquals(id, overlay?.configuration?.id)
         assertEquals(id, overlay?.instance?.id)
diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/ChildStackControllerTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/ChildStackControllerTest.kt
deleted file mode 100644
index 73dfd1ec..00000000
--- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/ChildStackControllerTest.kt
+++ /dev/null
@@ -1,204 +0,0 @@
-package com.arkivanov.decompose.router.stack
-
-import com.arkivanov.decompose.backhandler.TestChildBackHandler
-import com.arkivanov.decompose.statekeeper.TestStateKeeperDispatcher
-import com.arkivanov.essenty.backhandler.BackDispatcher
-import com.arkivanov.essenty.backhandler.BackHandler
-import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher
-import com.arkivanov.essenty.lifecycle.LifecycleRegistry
-import com.arkivanov.essenty.parcelable.Parcelable
-import com.arkivanov.essenty.parcelable.Parcelize
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import kotlin.test.assertFalse
-import kotlin.test.assertTrue
-
-@Suppress("TestFunctionName")
-class ChildStackControllerTest {
-
-    @Test
-    fun GIVEN_initial_back_stack_empty_WHEN_created_THEN_back_handler_disabled() {
-        val backDispatcher = BackDispatcher()
-
-        controller(initialStack = listOf(Config()), backHandler = backDispatcher)
-
-        assertFalse(backDispatcher.isEnabled)
-    }
-
-    @Test
-    fun GIVEN_initial_back_stack_not_empty_WHEN_created_THEN_back_handler_enabled() {
-        val backDispatcher = BackDispatcher()
-
-        controller(initialStack = listOf(Config(), Config()), backHandler = backDispatcher)
-
-        assertTrue(backDispatcher.isEnabled)
-    }
-
-    @Test
-    fun WHEN_navigate_THEN_new_stack_applied() {
-        val config1 = Config()
-        val config2 = Config()
-        val config3 = Config()
-        val config4 = Config()
-        val navigation = StackNavigation<Config>()
-        val controller = controller(source = navigation, initialStack = listOf(config1, config2, config3))
-
-        navigation.navigate { listOf(config1, config3, config4) }
-
-        assertEquals(listOf(config1, config3, config4), controller.configurations)
-    }
-
-    @Test
-    fun WHEN_navigate_THEN_onComplete_called_synchronously() {
-        val config1 = Config()
-        val config2 = Config()
-        val config3 = Config()
-        val config4 = Config()
-        val navigation = StackNavigation<Config>()
-        controller(source = navigation, initialStack = listOf(config1, config2, config3))
-
-        var resultNewStack: List<Config>? = null
-        var resultOldStack: List<Config>? = null
-        navigation.navigate(transformer = { listOf(config1, config3, config4) }) { newStack, oldStack ->
-            resultNewStack = newStack
-            resultOldStack = oldStack
-        }
-
-        assertEquals(listOf(config1, config3, config4), resultNewStack)
-        assertEquals(listOf(config1, config2, config3), resultOldStack)
-    }
-
-    @Test
-    fun WHEN_navigate_recursively_THEN_last_stack_applied() {
-        val config1 = Config()
-        val config2 = Config()
-        val config3 = Config()
-        val navigation = StackNavigation<Config>()
-        val stackController = TestStackController()
-        val controller = controller(source = navigation, initialStack = listOf(config1), controller = stackController)
-
-        stackController.onNavigate =
-            { newConfigs ->
-                if (newConfigs == listOf(config2)) {
-                    navigation.navigate { listOf(config3) }
-                }
-            }
-
-        navigation.navigate { listOf(config2) }
-
-        assertEquals(listOf(config3), controller.configurations)
-    }
-
-
-    @Test
-    fun WHEN_navigate_recursively_THEN_onComplete_not_called_synchronously() {
-        val config1 = Config()
-        val config2 = Config()
-        val config3 = Config()
-        val navigation = StackNavigation<Config>()
-        val stackController = TestStackController()
-        controller(source = navigation, initialStack = listOf(config1), controller = stackController)
-
-        var isCalledSynchronously = false
-        stackController.onNavigate =
-            { newConfigs ->
-                if (newConfigs == listOf(config2)) {
-                    var isCalled = false
-                    navigation.navigate(transformer = { listOf(config3) }) { _, _ ->
-                        isCalled = true
-                    }
-                    isCalledSynchronously = isCalled
-                }
-            }
-
-        navigation.navigate { listOf(config2) }
-
-        assertFalse(isCalledSynchronously)
-    }
-
-    @Test
-    fun WHEN_navigate_recursively_THEN_onComplete_called() {
-        val config1 = Config()
-        val config2 = Config()
-        val config3 = Config()
-        val navigation = StackNavigation<Config>()
-        val stackController = TestStackController()
-        controller(source = navigation, initialStack = listOf(config1), controller = stackController)
-
-        var resultNewStack: List<Config>? = null
-        var resultOldStack: List<Config>? = null
-        stackController.onNavigate =
-            { newConfigs ->
-                if (newConfigs == listOf(config2)) {
-                    navigation.navigate(transformer = { listOf(config3) }) { newStack, oldStack ->
-                        resultNewStack = newStack
-                        resultOldStack = oldStack
-                    }
-                }
-            }
-
-        navigation.navigate { listOf(config2) }
-
-        assertEquals(listOf(config3), resultNewStack)
-        assertEquals(listOf(config2), resultOldStack)
-    }
-
-    private fun controller(
-        source: StackNavigationSource<Config> = StackNavigation(),
-        initialStack: List<Config>,
-        backHandler: BackHandler = BackDispatcher(),
-        controller: TestStackController = TestStackController(),
-    ): ChildStackController<Config, Config> =
-        ChildStackController(
-            source = source,
-            lifecycle = LifecycleRegistry(),
-            backHandler = backHandler,
-            stackHolder = TestStackHolder(routerStack(initialStack)),
-            controller = controller,
-        )
-
-    private companion object {
-        private fun routerStack(stack: List<Config>): RouterStack<Config, Config> =
-            RouterStack(
-                active = RouterEntry.Created(
-                    configuration = stack.last(),
-                    instance = stack.last(),
-                    lifecycleRegistry = LifecycleRegistry(),
-                    stateKeeperDispatcher = TestStateKeeperDispatcher(),
-                    instanceKeeperDispatcher = InstanceKeeperDispatcher(),
-                    backHandler = TestChildBackHandler(isEnabled = true),
-                ),
-                backStack = stack.dropLast(1).map {
-                    RouterEntry.Destroyed(configuration = it)
-                }
-            )
-
-        private val ChildStackController<Config, *>.configurations: List<Config>
-            get() = stack.value.configurations
-
-        private val ChildStack<Config, *>.configurations: List<Config>
-            get() = items.map { it.configuration }
-    }
-
-    @Parcelize
-    private class Config : Parcelable
-
-    private class TestStackHolder(
-        override var stack: RouterStack<Config, Config>
-    ) : StackHolder<Config, Config>
-
-    private class TestStackController : StackController<Config, Config> {
-        var onNavigate: (newConfigs: List<Config>) -> Unit = {}
-
-        override fun navigate(
-            oldStack: RouterStack<Config, Config>,
-            transformer: (stack: List<Config>) -> List<Config>
-        ): RouterStack<Config, Config> {
-            val oldConfigs = oldStack.configurationStack
-            val newConfigs = transformer(oldConfigs)
-            onNavigate(newConfigs)
-
-            return routerStack(newConfigs)
-        }
-    }
-}
diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/ChildStackIntegrationTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/ChildStackIntegrationTest.kt
index c119411b..0b30a53c 100644
--- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/ChildStackIntegrationTest.kt
+++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/ChildStackIntegrationTest.kt
@@ -3,17 +3,24 @@ package com.arkivanov.decompose.router.stack
 import com.arkivanov.decompose.Child
 import com.arkivanov.decompose.ComponentContext
 import com.arkivanov.decompose.DefaultComponentContext
+import com.arkivanov.decompose.router.TestInstance
+import com.arkivanov.decompose.statekeeper.TestStateKeeperDispatcher
 import com.arkivanov.decompose.value.Value
+import com.arkivanov.decompose.value.getValue
 import com.arkivanov.essenty.backhandler.BackCallback
 import com.arkivanov.essenty.backhandler.BackDispatcher
+import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher
+import com.arkivanov.essenty.instancekeeper.getOrCreate
 import com.arkivanov.essenty.lifecycle.LifecycleRegistry
 import com.arkivanov.essenty.lifecycle.resume
 import com.arkivanov.essenty.parcelable.Parcelable
 import com.arkivanov.essenty.parcelable.Parcelize
+import com.arkivanov.essenty.statekeeper.consume
 import kotlin.test.BeforeTest
 import kotlin.test.Test
 import kotlin.test.assertEquals
 import kotlin.test.assertFalse
+import kotlin.test.assertSame
 import kotlin.test.assertTrue
 
 @Suppress("TestFunctionName")
@@ -21,8 +28,17 @@ class ChildStackIntegrationTest {
 
     private val navigation = StackNavigation<Config>()
     private val lifecycle = LifecycleRegistry()
+    private val stateKeeper = TestStateKeeperDispatcher()
+    private val instanceKeeper = InstanceKeeperDispatcher()
     private val backDispatcher = BackDispatcher()
-    private val context = DefaultComponentContext(lifecycle = lifecycle, backHandler = backDispatcher)
+
+    private val context =
+        DefaultComponentContext(
+            lifecycle = lifecycle,
+            stateKeeper = stateKeeper,
+            instanceKeeper = instanceKeeper,
+            backHandler = backDispatcher,
+        )
 
     @BeforeTest
     fun before() {
@@ -31,85 +47,85 @@ class ChildStackIntegrationTest {
 
     @Test
     fun GIVEN_one_child_in_stack_WHEN_push_one_child_THEN_two_children_in_stack() {
-        val stack = childStack(initialConfiguration = Config(1))
+        val stack by context.childStack(initialStack = listOf(Config(1)))
 
         navigation.push(Config(2))
 
-        stack.value.assertStack(1 to 1, 2 to 2)
+        stack.assertStack(1 to 1, 2 to 2)
     }
 
     @Test
     fun GIVEN_one_child_in_stack_WHEN_push_two_children_and_pop_one_child_THEN_two_children_in_stack() {
-        val stack = childStack(initialConfiguration = Config(1))
+        val stack by context.childStack(initialStack = listOf(Config(1)))
 
         navigation.push(Config(2))
         navigation.push(Config(3))
         navigation.pop()
 
-        stack.value.assertStack(1 to 1, 2 to 2)
+        stack.assertStack(1 to 1, 2 to 2)
     }
 
     @Test
     fun GIVEN_three_children_in_stack_WHEN_pop_one_child_THEN_two_children_in_stack() {
-        val stack = childStack(initialStack = listOf(Config(1), Config(2), Config(3)))
+        val stack by context.childStack(initialStack = listOf(Config(1), Config(2), Config(3)))
 
         navigation.pop()
 
-        stack.value.assertStack(1 to null, 2 to 2)
+        stack.assertStack(1 to null, 2 to 2)
     }
 
     @Test
     fun GIVEN_one_child_in_stack_WHEN_push_two_children_and_back_pressed_THEN_two_children_in_stack() {
-        val stack = childStack(initialConfiguration = Config(1))
+        val stack by context.childStack(initialStack = listOf(Config(1)))
 
         navigation.push(Config(2))
         navigation.push(Config(3))
         backDispatcher.back()
 
-        stack.value.assertStack(1 to 1, 2 to 2)
+        stack.assertStack(1 to 1, 2 to 2)
     }
 
     @Test
     fun GIVEN_three_children_in_stack_WHEN_back_pressed_THEN_two_children_in_stack() {
-        val stack = childStack(initialStack = listOf(Config(1), Config(2), Config(3)))
+        val stack by context.childStack(initialStack = listOf(Config(1), Config(2), Config(3)))
 
         backDispatcher.back()
 
-        stack.value.assertStack(1 to null, 2 to 2)
+        stack.assertStack(1 to null, 2 to 2)
     }
 
     @Test
     fun GIVEN_two_children_stack_WHEN_replaceCurrent_THEN_two_children_in_stack_and_new_child_is_active() {
-        val stack = childStack(initialStack = listOf(Config(1), Config(2)))
+        val stack by context.childStack(initialStack = listOf(Config(1), Config(2)))
 
         navigation.replaceCurrent(Config(3))
 
-        stack.value.assertStack(1 to null, 3 to 3)
+        stack.assertStack(1 to null, 3 to 3)
     }
 
     @Test
     fun GIVEN_initial_three_children_in_stack_WHEN_reverse_THEN_three_children_reversed_in_stack() {
-        val stack = childStack(initialStack = listOf(Config(1), Config(2), Config(3)))
+        val stack by context.childStack(initialStack = listOf(Config(1), Config(2), Config(3)))
 
         navigation.navigate { it.reversed() }
 
-        stack.value.assertStack(3 to 3, 2 to null, 1 to 1)
+        stack.assertStack(3 to 3, 2 to null, 1 to 1)
     }
 
     @Test
     fun GIVEN_three_active_children_in_stack_WHEN_reverse_THEN_three_children_reversed_in_stack() {
-        val stack = childStack(initialConfiguration = Config(1))
+        val stack by context.childStack(initialStack = listOf(Config(1)))
         navigation.push(Config(2))
         navigation.push(Config(3))
 
         navigation.navigate { it.reversed() }
 
-        stack.value.assertStack(3 to 3, 2 to 2, 1 to 1)
+        stack.assertStack(3 to 3, 2 to 2, 1 to 1)
     }
 
     @Test
     fun GIVEN_nested_navigation_with_back_handling_and_back_stack_not_empty_WHEN_back_pressed_THEN_inner_stack_popped() {
-        val stack = childStack(initialConfiguration = Config(id = 1))
+        val stack by context.childStack(initialStack = listOf(Config(id = 1)))
 
         navigation.push(
             Config(
@@ -121,17 +137,17 @@ class ChildStackIntegrationTest {
             )
         )
 
-        stack.value.active.instance.navigation.push(Config(id = 2))
+        stack.active.instance.navigation.push(Config(id = 2))
 
         backDispatcher.back()
 
-        stack.value.assertStack(1 to 1, 2 to 2)
-        stack.value.active.instance.stack.value.assertStack(1 to 1)
+        stack.assertStack(1 to 1, 2 to 2)
+        stack.active.instance.stack.assertStack(1 to 1)
     }
 
     @Test
     fun GIVEN_nested_navigation_with_back_handling_and_back_stack_not_empty_and_back_pressed_WHEN_back_pressed_again_THEN_outer_stack_popped() {
-        val stack = childStack(initialConfiguration = Config(id = 1))
+        val stack by context.childStack(initialStack = listOf(Config(id = 1)))
 
         navigation.push(
             Config(
@@ -143,19 +159,19 @@ class ChildStackIntegrationTest {
             )
         )
 
-        stack.value.active.instance.navigation.push(Config(id = 2))
+        stack.active.instance.navigation.push(Config(id = 2))
         backDispatcher.back()
 
         backDispatcher.back()
 
-        stack.value.assertStack(1 to 1)
+        stack.assertStack(1 to 1)
     }
 
     @Test
     fun GIVEN_two_children_in_stack_and_last_child_has_enabled_back_callback_WHEN_back_pressed_THEN_back_callback_called() {
-        val stack = childStack(initialStack = listOf(Config(1), Config(2)))
+        val stack by context.childStack(initialStack = listOf(Config(1), Config(2)))
         var isCalled = false
-        stack.value.active.instance.backHandler.register(BackCallback { isCalled = true })
+        stack.active.instance.backHandler.register(BackCallback { isCalled = true })
 
         backDispatcher.back()
 
@@ -164,19 +180,19 @@ class ChildStackIntegrationTest {
 
     @Test
     fun GIVEN_two_children_in_stack_and_last_child_has_enabled_back_callback_WHEN_back_pressed_THEN_stack_not_popped() {
-        val stack = childStack(initialStack = listOf(Config(1), Config(2)))
-        stack.value.active.instance.backHandler.register(BackCallback {})
+        val stack by context.childStack(initialStack = listOf(Config(1), Config(2)))
+        stack.active.instance.backHandler.register(BackCallback {})
 
         backDispatcher.back()
 
-        stack.value.assertStack(1 to null, 2 to 2)
+        stack.assertStack(1 to null, 2 to 2)
     }
 
     @Test
     fun GIVEN_two_children_in_stack_and_last_child_has_disabled_back_callback_WHEN_back_pressed_THEN_back_callback_not_called() {
-        val stack = childStack(initialStack = listOf(Config(1), Config(2)))
+        val stack by context.childStack(initialStack = listOf(Config(1), Config(2)))
         var isCalled = false
-        stack.value.active.instance.backHandler.register(BackCallback(isEnabled = false) { isCalled = true })
+        stack.active.instance.backHandler.register(BackCallback(isEnabled = false) { isCalled = true })
 
         backDispatcher.back()
 
@@ -185,19 +201,138 @@ class ChildStackIntegrationTest {
 
     @Test
     fun GIVEN_two_children_in_stack_and_last_child_has_disabled_back_callback_WHEN_back_pressed_THEN_stack_popped() {
-        val stack = childStack(initialStack = listOf(Config(1), Config(2)))
-        stack.value.active.instance.backHandler.register(BackCallback(isEnabled = false) {})
+        val stack by context.childStack(initialStack = listOf(Config(1), Config(2)))
+        stack.active.instance.backHandler.register(BackCallback(isEnabled = false) {})
 
         backDispatcher.back()
 
-        stack.value.assertStack(1 to 1)
+        stack.assertStack(1 to 1)
+    }
+
+    @Test
+    fun WHEN_recreated_THEN_stack_restored() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper)
+        oldContext.childStack(initialStack = listOf(Config(1), Config(2)))
+
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper)
+        val newStack by newContext.childStack(initialStack = emptyList())
+
+        newStack.assertStack(1 to null, 2 to 2)
+    }
+
+    @Test
+    fun WHEN_child_pushed_THEN_state_saved_for_previous_child() {
+        val stack by context.childStack(initialStack = listOf(Config(1)))
+        var isCalled = false
+        stack.active.instance.stateKeeper.register(key = "key") {
+            isCalled = true
+            Config(10)
+        }
+
+        navigation.push(Config(2))
+
+        assertTrue(isCalled)
     }
 
-    private fun childStack(initialConfiguration: Config): Value<ChildStack<Config, Component>> =
-        childStack(initialStack = listOf(initialConfiguration))
+    @Test
+    fun WHEN_recreated_THEN_state_restored_for_active_child() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper)
+        val oldStack by oldContext.childStack(initialStack = listOf(Config(1)))
+        oldStack.active.instance.stateKeeper.register(key = "key") { Config(10) }
+
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper)
+        val newStack by newContext.childStack(initialStack = emptyList())
+        val restoredState = newStack.active.instance.stateKeeper.consume<Config>(key = "key")
+
+        assertEquals(Config(10), restoredState)
+    }
+
+    @Test
+    fun GIVEN_back_stack_not_empty_and_recreated_WHEN_pop_THEN_state_restored_for_previous_child() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper)
+        val oldStack by oldContext.childStack(initialStack = listOf(Config(1)))
+        oldStack.active.instance.stateKeeper.register(key = "key") { Config(10) }
+        navigation.push(Config(2))
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper)
+        val newStack by newContext.childStack(initialStack = emptyList())
+
+        navigation.pop()
+        val restoredState = newStack.active.instance.stateKeeper.consume<Config>(key = "key")
+
+        assertEquals(Config(10), restoredState)
+    }
+
+    @Test
+    fun WHEN_recreated_THEN_instance_retained_for_active_component() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val instanceKeeper = InstanceKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper)
+        val oldStack by oldContext.childStack(initialStack = listOf(Config(1)))
+        val oldInstance = oldStack.active.instance.instanceKeeper.getOrCreate(::TestInstance)
+
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper)
+        val newStack by newContext.childStack(initialStack = emptyList())
+        val retainedInstance = newStack.active.instance.instanceKeeper.getOrCreate(::TestInstance)
+
+        assertSame(oldInstance, retainedInstance)
+    }
+
+    @Test
+    fun WHEN_recreated_THEN_instance_not_destroyed_for_active_component() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val instanceKeeper = InstanceKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper)
+        val stack by oldContext.childStack(initialStack = listOf(Config(1)))
+        val instance = stack.active.instance.instanceKeeper.getOrCreate(::TestInstance)
+
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper)
+        newContext.childStack(initialStack = emptyList())
+
+        assertFalse(instance.isDestroyed)
+    }
+
+    @Test
+    fun WHEN_another_child_pushed_THEN_instance_not_destroyed_for_previous_child() {
+        val stack by context.childStack(initialStack = listOf(Config(1)))
+        val instance = stack.active.instance.instanceKeeper.getOrCreate(::TestInstance)
+
+        navigation.push(Config(2))
+
+        assertFalse(instance.isDestroyed)
+    }
+
+    @Test
+    fun GIVEN_another_child_pushed_WHEN_recreated_THEN_instance_destroyed_for_previous_child() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val instanceKeeper = InstanceKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper)
+        val stack by oldContext.childStack(initialStack = listOf(Config(1)))
+        val instance = stack.active.instance.instanceKeeper.getOrCreate(::TestInstance)
+        navigation.push(Config(2))
+
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper)
+        newContext.childStack(initialStack = emptyList())
+
+        assertTrue(instance.isDestroyed)
+    }
 
-    private fun childStack(initialStack: List<Config>): Value<ChildStack<Config, Component>> =
-        context.childStack(
+    private fun ComponentContext.childStack(initialStack: List<Config>): Value<ChildStack<Config, Component>> =
+        childStack(
             source = navigation,
             initialStack = { initialStack },
             handleBackButton = true,
@@ -247,6 +382,6 @@ class ChildStackIntegrationTest {
                 )
             }
 
-        val stack: Value<ChildStack<Config, Component>> get() = requireNotNull(_stack)
+        val stack: ChildStack<Config, Component> get() = requireNotNull(_stack).value
     }
 }
diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/StackControllerTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/StackControllerTest.kt
deleted file mode 100644
index cfb219ed..00000000
--- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/StackControllerTest.kt
+++ /dev/null
@@ -1,636 +0,0 @@
-package com.arkivanov.decompose.router.stack
-
-import com.arkivanov.decompose.backhandler.ChildBackHandler
-import com.arkivanov.decompose.backhandler.TestChildBackHandler
-import com.arkivanov.decompose.router.TestInstance
-import com.arkivanov.decompose.statekeeper.TestParcelableContainer
-import com.arkivanov.decompose.statekeeper.TestStateKeeperDispatcher
-import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher
-import com.arkivanov.essenty.lifecycle.Lifecycle
-import com.arkivanov.essenty.lifecycle.LifecycleRegistry
-import com.arkivanov.essenty.lifecycle.create
-import com.arkivanov.essenty.lifecycle.resume
-import com.arkivanov.essenty.parcelable.ParcelableContainer
-import com.arkivanov.essenty.statekeeper.StateKeeperDispatcher
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import kotlin.test.assertFalse
-import kotlin.test.assertNotNull
-import kotlin.test.assertNull
-import kotlin.test.assertSame
-import kotlin.test.assertTrue
-
-@Suppress("TestFunctionName")
-class StackControllerTest {
-
-    private val controller = StackControllerImpl(routerEntryFactory = TestRouterEntryFactory())
-
-    @Test
-    fun GIVEN_empty_back_stack_WHEN_push_THEN_active_entry_is_new() {
-        val newConfig = Config(id = 2)
-
-        val newStack =
-            controller.navigate(
-                oldStack = RouterStack(
-                    active = activeEntry(configuration = Config(id = 1)),
-                    backStack = emptyList()
-                ),
-                transformer = { it + newConfig }
-            )
-
-        assertEquals(newConfig, newStack.active.configuration)
-    }
-
-    @Test
-    fun GIVEN_empty_back_stack_WHEN_push_THEN_back_stack_equals_old() {
-        val oldConfig = Config(id = 1)
-
-        val newStack =
-            controller.navigate(
-                oldStack = RouterStack(
-                    active = activeEntry(configuration = oldConfig),
-                    backStack = emptyList()
-                ),
-                transformer = { it + Config(id = 2) }
-            )
-
-        assertEquals(listOf(oldConfig), newStack.configurationBackStack)
-    }
-
-    @Test
-    fun GIVEN_not_empty_back_stack_WHEN_push_THEN_back_stack_equals_old_back_stack_plus_old_active() {
-        val oldConfig1 = Config(id = 1)
-        val oldConfig2 = Config(id = 2)
-        val oldConfig3 = Config(id = 3)
-
-        val newStack =
-            controller.navigate(
-                oldStack = RouterStack(
-                    active = activeEntry(configuration = oldConfig3),
-                    backStack = listOf(createdEntry(configuration = oldConfig1), createdEntry(configuration = oldConfig2))
-                ),
-                transformer = { it + Config(id = 4) }
-            )
-
-        assertEquals(listOf(oldConfig1, oldConfig2, oldConfig3), newStack.configurationBackStack)
-    }
-
-    @Test
-    fun GIVEN_empty_back_stack_WHEN_push_THEN_old_component_not_recreated() {
-        val oldStack =
-            RouterStack(
-                active = activeEntry(configuration = Config(id = 1)),
-                backStack = emptyList()
-            )
-
-        val newStack = controller.navigate(oldStack = oldStack, transformer = { it + Config(id = 2) })
-
-        assertSame(newStack.backStack[0].asCreated().instance, oldStack.active.instance)
-    }
-
-    @Test
-    fun GIVEN_not_empty_back_stack_WHEN_push_THEN_old_components_not_recreated() {
-        val oldStack =
-            RouterStack(
-                active = activeEntry(configuration = Config(id = 3)),
-                backStack = listOf(createdEntry(configuration = Config(id = 1)), createdEntry(configuration = Config(id = 2)))
-            )
-
-        val newStack = controller.navigate(oldStack = oldStack, transformer = { it + Config(id = 4) })
-
-        assertSame(oldStack.backStack[0].asCreated().instance, newStack.backStack[0].asCreated().instance)
-        assertSame(oldStack.backStack[1].asCreated().instance, newStack.backStack[1].asCreated().instance)
-        assertSame(oldStack.active.instance, newStack.backStack[2].asCreated().instance)
-    }
-
-    @Test
-    fun WHEN_push_THEN_state_saved_for_old_active_component() {
-        val stateKeeperDispatcher = TestStateKeeperDispatcher()
-
-        val newStack =
-            controller.navigate(
-                oldStack = RouterStack(
-                    active = activeEntry(configuration = Config(id = 1), stateKeeperDispatcher = stateKeeperDispatcher),
-                    backStack = emptyList()
-                ),
-                transformer = { it + Config(id = 2) }
-            )
-
-        assertNotNull(stateKeeperDispatcher.lastSavedState)
-        assertSame(stateKeeperDispatcher.lastSavedState, newStack.backStack[0].savedState)
-    }
-
-    @Test
-    fun WHEN_push_THEN_new_active_component_resumed() {
-        val newStack =
-            controller.navigate(
-                oldStack = RouterStack(
-                    active = activeEntry(configuration = Config(id = 1)),
-                    backStack = emptyList()
-                ),
-                transformer = { it + Config(id = 2) }
-            )
-
-        assertSame(Lifecycle.State.RESUMED, newStack.active.lifecycleRegistry.state)
-    }
-
-    @Test
-    fun WHEN_push_THEN_new_active_component_back_handler_started() {
-        val newStack =
-            controller.navigate(
-                oldStack = RouterStack(
-                    active = activeEntry(configuration = Config(id = 1)),
-                    backStack = emptyList()
-                ),
-                transformer = { it + Config(id = 2) }
-            )
-
-        assertTrue(newStack.active.backHandler.asTest().isStarted)
-    }
-
-    @Test
-    fun WHEN_push_THEN_new_active_component_back_handler_enabled() {
-        val newStack =
-            controller.navigate(
-                oldStack = RouterStack(
-                    active = activeEntry(configuration = Config(id = 1)),
-                    backStack = emptyList()
-                ),
-                transformer = { it + Config(id = 2) }
-            )
-
-        assertTrue(newStack.active.backHandler.asTest().isEnabled)
-    }
-
-    @Test
-    fun WHEN_push_THEN_old_active_component_stopped() {
-        val newStack =
-            controller.navigate(
-                oldStack = RouterStack(
-                    active = activeEntry(configuration = Config(id = 1)),
-                    backStack = emptyList()
-                ),
-                transformer = { it + Config(id = 2) }
-            )
-
-        assertSame(Lifecycle.State.CREATED, newStack.backStack.first().asCreated().lifecycleRegistry.state)
-    }
-
-    @Test
-    fun WHEN_push_THEN_old_active_component_back_handler_stopped() {
-        val newStack =
-            controller.navigate(
-                oldStack = RouterStack(
-                    active = activeEntry(configuration = Config(id = 1)),
-                    backStack = emptyList()
-                ),
-                transformer = { it + Config(id = 2) }
-            )
-
-        assertFalse(newStack.backStack.first().asCreated().backHandler.asTest().isStarted)
-    }
-
-    @Test
-    fun WHEN_push_THEN_old_active_component_back_handler_enabled() {
-        val newStack =
-            controller.navigate(
-                oldStack = RouterStack(
-                    active = activeEntry(configuration = Config(id = 1)),
-                    backStack = emptyList()
-                ),
-                transformer = { it + Config(id = 2) }
-            )
-
-        assertTrue(newStack.backStack.first().asCreated().backHandler.asTest().isEnabled)
-    }
-
-    @Test
-    fun WHEN_push_THEN_old_components_not_recreated() {
-        val newStack =
-            controller.navigate(
-                oldStack = RouterStack(
-                    active = activeEntry(configuration = Config(id = 1)),
-                    backStack = emptyList()
-                ),
-                transformer = { it + Config(id = 2) }
-            )
-
-        assertSame(Lifecycle.State.CREATED, (newStack.backStack[0] as RouterEntry.Created).lifecycleRegistry.state)
-    }
-
-    @Test
-    fun GIVEN_back_stack_WHEN_pop_THEN_active_entry_is_back() {
-        val backConfig = Config(id = 1)
-
-        val newStack =
-            controller.navigate(
-                oldStack = RouterStack(
-                    active = activeEntry(configuration = Config(id = 2)),
-                    backStack = listOf(createdEntry(configuration = backConfig))
-                ),
-                transformer = { it.dropLast(1) }
-            )
-
-        assertEquals(backConfig, newStack.active.configuration)
-    }
-
-    @Test
-    fun GIVEN_back_stack_with_one_entry_WHEN_pop_THEN_back_stack_is_empty() {
-        val newStack =
-            controller.navigate(
-                oldStack = RouterStack(
-                    active = activeEntry(configuration = Config(id = 2)),
-                    backStack = listOf(createdEntry(configuration = Config(id = 1)))
-                ),
-                transformer = { it.dropLast(1) }
-            )
-
-        assertTrue(newStack.backStack.isEmpty())
-    }
-
-    @Test
-    fun GIVEN_back_stack_WHEN_pop_THEN_old_active_component_destroyed() {
-        val lifecycleRegistry = LifecycleRegistry().apply { resume() }
-
-        controller.navigate(
-            oldStack = RouterStack(
-                active = activeEntry(configuration = Config(id = 2), lifecycleRegistry = lifecycleRegistry),
-                backStack = listOf(createdEntry(configuration = Config(id = 1)))
-            ),
-            transformer = { it.dropLast(1) }
-        )
-
-        assertEquals(Lifecycle.State.DESTROYED, lifecycleRegistry.state)
-    }
-
-    @Test
-    fun GIVEN_back_stack_WHEN_pop_THEN_old_active_component_retained_instance_destroyed() {
-        val instanceKeeperDispatcher = InstanceKeeperDispatcher()
-        val instance = TestInstance()
-        instanceKeeperDispatcher.put("key", instance)
-
-        controller.navigate(
-            oldStack = RouterStack(
-                active = activeEntry(configuration = Config(id = 2), instanceKeeperDispatcher = instanceKeeperDispatcher),
-                backStack = listOf(createdEntry(configuration = Config(id = 1)))
-            ),
-            transformer = { it.dropLast(1) }
-        )
-
-        assertTrue(instance.isDestroyed)
-    }
-
-    @Test
-    fun GIVEN_back_stack_WHEN_pop_THEN_new_active_component_resumed() {
-        val newStack =
-            controller.navigate(
-                oldStack = RouterStack(
-                    active = activeEntry(configuration = Config(id = 2)),
-                    backStack = listOf(createdEntry(configuration = Config(id = 1)))
-                ),
-                transformer = { it.dropLast(1) }
-            )
-
-        assertEquals(Lifecycle.State.RESUMED, newStack.active.lifecycleRegistry.state)
-    }
-
-    @Test
-    fun GIVEN_back_stack_WHEN_pop_THEN_old_components_not_recreated() {
-        val oldStack =
-            RouterStack(
-                active = activeEntry(configuration = Config(id = 3)),
-                backStack = listOf(createdEntry(configuration = Config(id = 1)), createdEntry(configuration = Config(id = 2)))
-            )
-
-        val newStack = controller.navigate(oldStack = oldStack, transformer = { it.dropLast(1) })
-
-        assertSame(oldStack.backStack[0].asCreated().instance, newStack.backStack[0].asCreated().instance)
-        assertSame(oldStack.backStack[1].asCreated().instance, newStack.active.instance)
-    }
-
-    @Test
-    fun GIVEN_back_stack_with_created_entry_and_saved_state_WHEN_pop_THEN_new_active_component_without_saved_state() {
-        val newStack =
-            controller.navigate(
-                oldStack = RouterStack(
-                    active = activeEntry(configuration = Config(id = 2)),
-                    backStack = listOf(createdEntry(configuration = Config(id = 1), savedState = TestParcelableContainer()))
-                ),
-                transformer = { it.dropLast(1) }
-            )
-
-        assertNull(newStack.active.savedState)
-    }
-
-    @Test
-    fun GIVEN_back_stack_with_destroyed_entry_and_saved_state_WHEN_pop_THEN_state_restored_for_new_active_component() {
-        val savedState = TestParcelableContainer()
-
-        val newStack =
-            controller.navigate(
-                oldStack = RouterStack(
-                    active = activeEntry(configuration = Config(id = 2)),
-                    backStack = listOf(destroyedEntry(configuration = Config(id = 1), savedState = savedState))
-                ),
-                transformer = { it.dropLast(1) }
-            )
-
-        assertSame(savedState, (newStack.active.stateKeeperDispatcher as TestStateKeeperDispatcher).initialSavedState)
-    }
-
-    @Test
-    fun GIVEN_back_stack_with_destroyed_entry_WHEN_pop_THEN_new_active_component_without_saved_state() {
-        val newStack =
-            controller.navigate(
-                oldStack = RouterStack(
-                    active = activeEntry(configuration = Config(id = 2)),
-                    backStack = listOf(destroyedEntry(configuration = Config(id = 1)))
-                ),
-                transformer = { it.dropLast(1) }
-            )
-
-        assertNull(newStack.active.savedState)
-    }
-
-    @Test
-    fun WHEN_navigate_to_different_stack_THEN_old_active_component_destroyed() {
-        val lifecycleRegistry = LifecycleRegistry().apply { resume() }
-
-        controller.navigate(
-            oldStack = RouterStack(
-                active = activeEntry(configuration = Config(id = 2), lifecycleRegistry = lifecycleRegistry),
-                backStack = listOf(createdEntry(configuration = Config(id = 1)))
-            ),
-            transformer = { listOf(Config(id = 3), Config(id = 4)) }
-        )
-
-        assertEquals(Lifecycle.State.DESTROYED, lifecycleRegistry.state)
-    }
-
-    @Test
-    fun WHEN_navigate_to_different_stack_THEN_old_back_stack_components_destroyed() {
-        val lifecycleRegistry = LifecycleRegistry().apply { create() }
-
-        controller.navigate(
-            oldStack = RouterStack(
-                active = activeEntry(configuration = Config(id = 2)),
-                backStack = listOf(createdEntry(configuration = Config(id = 1), lifecycleRegistry = lifecycleRegistry))
-            ),
-            transformer = { listOf(Config(id = 3), Config(id = 4)) }
-        )
-
-        assertEquals(Lifecycle.State.DESTROYED, lifecycleRegistry.state)
-    }
-
-    @Test
-    fun WHEN_navigate_to_different_stack_THEN_new_stack_contains_only_new_configurations() {
-        val lifecycleRegistry = LifecycleRegistry().apply { resume() }
-        val newConfigurationStack = listOf(Config(id = 3), Config(id = 4))
-
-        val newStack =
-            controller.navigate(
-                oldStack = RouterStack(
-                    active = activeEntry(configuration = Config(id = 2), lifecycleRegistry = lifecycleRegistry),
-                    backStack = listOf(createdEntry(configuration = Config(id = 1)))
-                ),
-                transformer = { newConfigurationStack }
-            )
-
-        assertEquals(newConfigurationStack, newStack.configurationStack)
-    }
-
-    @Test
-    fun WHEN_navigate_to_different_stack_THEN_new_active_component_resumed() {
-        val newStack =
-            controller.navigate(
-                oldStack = RouterStack(
-                    active = activeEntry(configuration = Config(id = 2)),
-                    backStack = listOf(createdEntry(configuration = Config(id = 1)))
-                ),
-                transformer = { listOf(Config(id = 3), Config(id = 4)) }
-            )
-
-        assertEquals(Lifecycle.State.RESUMED, newStack.active.lifecycleRegistry.state)
-    }
-
-    @Test
-    fun WHEN_navigate_to_different_stack_THEN_new_back_stack_components_not_created() {
-        val newStack =
-            controller.navigate(
-                oldStack = RouterStack(
-                    active = activeEntry(configuration = Config(id = 2)),
-                    backStack = listOf(createdEntry(configuration = Config(id = 1)))
-                ),
-                transformer = { listOf(Config(id = 3), Config(id = 4)) }
-            )
-
-        assertTrue(newStack.backStack.all { it is RouterEntry.Destroyed })
-    }
-
-    @Test
-    fun WHEN_navigate_to_partially_different_stack_with_new_active_component_THEN_new_stack_contains_same_old_configurations_and_new_configurations() {
-        val newConfigurationStack = listOf(Config(id = 6), Config(id = 2), Config(id = 4), Config(id = 7), Config(id = 8))
-
-        val newStack =
-            controller.navigate(
-                oldStack = RouterStack(
-                    active = activeEntry(configuration = Config(id = 5)),
-                    backStack = listOf(
-                        createdEntry(configuration = Config(id = 1)),
-                        createdEntry(configuration = Config(id = 2)),
-                        createdEntry(configuration = Config(id = 3)),
-                        createdEntry(configuration = Config(id = 4))
-                    )
-                ),
-                transformer = { newConfigurationStack }
-            )
-
-        assertEquals(newConfigurationStack, newStack.configurationStack)
-    }
-
-    @Test
-    fun WHEN_navigate_to_partially_different_stack_with_new_active_component_THEN_same_old_components_not_recreated() {
-        val oldStack =
-            RouterStack(
-                active = activeEntry(configuration = Config(id = 5)),
-                backStack = listOf(
-                    createdEntry(configuration = Config(id = 1)),
-                    createdEntry(configuration = Config(id = 2)),
-                    createdEntry(configuration = Config(id = 3)),
-                    createdEntry(configuration = Config(id = 4))
-                )
-            )
-
-        val newStack =
-            controller.navigate(
-                oldStack = oldStack,
-                transformer = { listOf(Config(id = 6), Config(id = 2), Config(id = 4), Config(id = 7), Config(id = 8)) }
-            )
-
-        assertEquals(oldStack.backStack[1].asCreated().instance, newStack.backStack[1].asCreated().instance)
-        assertEquals(oldStack.backStack[3].asCreated().instance, newStack.backStack[2].asCreated().instance)
-    }
-
-    @Test
-    fun WHEN_navigate_to_partially_different_stack_with_same_active_component_THEN_new_stack_contains_same_old_configurations_and_new_configurations() {
-        val newConfigurationStack = listOf(Config(id = 6), Config(id = 2), Config(id = 4), Config(id = 7), Config(id = 5))
-
-        val newStack =
-            controller.navigate(
-                oldStack = RouterStack(
-                    active = activeEntry(configuration = Config(id = 5)),
-                    backStack = listOf(
-                        createdEntry(configuration = Config(id = 1)),
-                        createdEntry(configuration = Config(id = 2)),
-                        createdEntry(configuration = Config(id = 3)),
-                        createdEntry(configuration = Config(id = 4))
-                    )
-                ),
-                transformer = { newConfigurationStack }
-            )
-
-        assertEquals(newConfigurationStack, newStack.configurationStack)
-    }
-
-    @Test
-    fun WHEN_navigate_to_partially_different_stack_with_same_active_component_THEN_same_old_components_not_recreated() {
-        val oldStack =
-            RouterStack(
-                active = activeEntry(configuration = Config(id = 5)),
-                backStack = listOf(
-                    createdEntry(configuration = Config(id = 1)),
-                    createdEntry(configuration = Config(id = 2)),
-                    createdEntry(configuration = Config(id = 3)),
-                    createdEntry(configuration = Config(id = 4))
-                )
-            )
-
-        val newStack =
-            controller.navigate(
-                oldStack = oldStack,
-                transformer = { listOf(Config(id = 6), Config(id = 2), Config(id = 4), Config(id = 7), Config(id = 5)) }
-            )
-
-        assertSame(oldStack.backStack[1].asCreated().instance, newStack.backStack[1].asCreated().instance)
-        assertSame(oldStack.backStack[3].asCreated().instance, newStack.backStack[2].asCreated().instance)
-        assertSame(oldStack.active.instance, newStack.active.instance)
-    }
-
-    @Test
-    fun WHEN_navigate_to_partially_different_stack_with_same_active_component_from_back_stack_THEN_same_old_components_not_recreated() {
-        val oldStack =
-            RouterStack(
-                active = activeEntry(configuration = Config(id = 5)),
-                backStack = listOf(
-                    createdEntry(configuration = Config(id = 1)),
-                    createdEntry(configuration = Config(id = 2)),
-                    createdEntry(configuration = Config(id = 3)),
-                    createdEntry(configuration = Config(id = 4))
-                )
-            )
-
-        val newStack =
-            controller.navigate(
-                oldStack = oldStack,
-                transformer = { listOf(Config(id = 6), Config(id = 2), Config(id = 4), Config(id = 7), Config(id = 3)) }
-            )
-
-        assertSame(oldStack.backStack[1].asCreated().instance, newStack.backStack[1].asCreated().instance)
-        assertSame(oldStack.backStack[3].asCreated().instance, newStack.backStack[2].asCreated().instance)
-        assertSame(oldStack.backStack[2].asCreated().instance, newStack.active.instance)
-    }
-
-    @Test
-    fun WHEN_navigate_to_equal_stack_THEN_stack_not_changed() {
-        val oldStack =
-            RouterStack(
-                active = activeEntry(configuration = Config(id = 2)),
-                backStack = listOf(createdEntry(configuration = Config(id = 1)))
-            )
-
-        val newStack = controller.navigate(oldStack = oldStack, transformer = { listOf(Config(id = 1), Config(id = 2)) })
-
-        assertEquals(oldStack, newStack)
-    }
-
-    @Test
-    fun WHEN_navigate_to_same_stack_THEN_stack_not_changed() {
-        val oldStack =
-            RouterStack(
-                active = activeEntry(configuration = Config(id = 1)),
-                backStack = listOf(createdEntry(configuration = Config(id = 2)))
-            )
-
-        val newStack = controller.navigate(oldStack = oldStack, transformer = { it })
-
-        assertEquals(oldStack, newStack)
-    }
-
-    private fun <C : Any, T : Any> RouterEntry<C, T>.asCreated(): RouterEntry.Created<C, T> =
-        this as RouterEntry.Created<C, T>
-
-    private fun ChildBackHandler.asTest(): TestChildBackHandler = this as TestChildBackHandler
-
-    private companion object {
-        private fun activeEntry(
-            configuration: Config,
-            component: Component = Component(),
-            lifecycleRegistry: LifecycleRegistry = LifecycleRegistry().apply { resume() },
-            stateKeeperDispatcher: StateKeeperDispatcher = TestStateKeeperDispatcher(),
-            instanceKeeperDispatcher: InstanceKeeperDispatcher = InstanceKeeperDispatcher(),
-            backHandler: ChildBackHandler = TestChildBackHandler(isStarted = true, isEnabled = true),
-        ): RouterEntry.Created<Config, Component> =
-            RouterEntry.Created(
-                configuration = configuration,
-                instance = component,
-                lifecycleRegistry = lifecycleRegistry,
-                stateKeeperDispatcher = stateKeeperDispatcher,
-                instanceKeeperDispatcher = instanceKeeperDispatcher,
-                backHandler = backHandler,
-            )
-
-        private fun createdEntry(
-            configuration: Config,
-            component: Component = Component(),
-            savedState: ParcelableContainer? = null,
-            lifecycleRegistry: LifecycleRegistry = LifecycleRegistry().apply { create() },
-            stateKeeperDispatcher: StateKeeperDispatcher = TestStateKeeperDispatcher(savedState),
-            backHandler: ChildBackHandler = TestChildBackHandler(isEnabled = true),
-        ): RouterEntry.Created<Config, Component> =
-            RouterEntry.Created(
-                configuration = configuration,
-                savedState = savedState,
-                instance = component,
-                lifecycleRegistry = lifecycleRegistry,
-                stateKeeperDispatcher = stateKeeperDispatcher,
-                instanceKeeperDispatcher = InstanceKeeperDispatcher(),
-                backHandler = backHandler,
-            )
-
-        private fun destroyedEntry(
-            configuration: Config,
-            savedState: ParcelableContainer? = null
-        ): RouterEntry.Destroyed<Config> =
-            RouterEntry.Destroyed(
-                configuration = configuration,
-                savedState = savedState
-            )
-    }
-
-    private data class Config(val id: Int)
-
-    private class Component
-
-    private class TestRouterEntryFactory : RouterEntryFactory<Config, Component> {
-        override fun invoke(
-            configuration: Config,
-            savedState: ParcelableContainer?,
-            instanceKeeperDispatcher: InstanceKeeperDispatcher?
-        ): RouterEntry.Created<Config, Component> =
-            createdEntry(
-                configuration = configuration,
-                component = Component(),
-                stateKeeperDispatcher = TestStateKeeperDispatcher(savedState)
-            )
-    }
-}
diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/StackHolderImplTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/StackHolderImplTest.kt
deleted file mode 100644
index cc868098..00000000
--- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/StackHolderImplTest.kt
+++ /dev/null
@@ -1,190 +0,0 @@
-package com.arkivanov.decompose.router.stack
-
-import com.arkivanov.decompose.backhandler.ChildBackHandler
-import com.arkivanov.decompose.backhandler.TestChildBackHandler
-import com.arkivanov.decompose.router.TestInstance
-import com.arkivanov.decompose.statekeeper.ParcelableStub
-import com.arkivanov.decompose.statekeeper.TestStateKeeperDispatcher
-import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher
-import com.arkivanov.essenty.lifecycle.Lifecycle
-import com.arkivanov.essenty.lifecycle.LifecycleRegistry
-import com.arkivanov.essenty.parcelable.ParcelableContainer
-import com.arkivanov.essenty.statekeeper.consume
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import kotlin.test.assertSame
-import kotlin.test.assertTrue
-
-@Suppress("TestFunctionName")
-class StackHolderImplTest {
-
-    @Test
-    fun WHEN_created_THEN_active_component_resumed() {
-        val holder = stackHolder()
-
-        assertEquals(Lifecycle.State.RESUMED, holder.stack.active.lifecycleRegistry.state)
-    }
-
-    @Test
-    fun WHEN_created_THEN_active_component_back_handler_started() {
-        val holder = stackHolder()
-
-        assertTrue(holder.stack.active.backHandler.asTest().isStarted)
-    }
-
-    @Test
-    fun WHEN_created_THEN_active_component_back_handler_enabled() {
-        val holder = stackHolder()
-
-        assertTrue(holder.stack.active.backHandler.asTest().isEnabled)
-    }
-
-    @Test
-    fun WHEN_recreated_THEN_instances_retained_in_active_component() {
-        val instanceKeeperDispatcher = InstanceKeeperDispatcher()
-        val stackSaver = stackSaver()
-        val oldHolder = stackHolder(instanceKeeperDispatcher = instanceKeeperDispatcher, stackSaver = stackSaver)
-        val instance = TestInstance()
-        oldHolder.stack.active.instanceKeeperDispatcher.put("key", instance)
-        val savedState = stackSaver.save()
-
-        val newHolder = stackHolder(instanceKeeperDispatcher = instanceKeeperDispatcher, stackSaver = stackSaver(savedState = savedState))
-
-        assertSame(instance, newHolder.stack.active.instanceKeeperDispatcher.get("key"))
-    }
-
-    @Test
-    fun WHEN_recreated_without_saved_state_THEN_previous_retained_instance_destroyed() {
-        val instanceKeeperDispatcher = InstanceKeeperDispatcher()
-        val oldHolder = stackHolder(instanceKeeperDispatcher = instanceKeeperDispatcher)
-        val instance = TestInstance()
-        oldHolder.stack.active.instanceKeeperDispatcher.put("key", instance)
-
-        stackHolder(instanceKeeperDispatcher = instanceKeeperDispatcher)
-
-        assertTrue(instance.isDestroyed)
-    }
-
-    @Test
-    fun GIVEN_created_WHEN_InstanceKeeper_destroyed_THEN_active_InstanceKeeper_destroyed() {
-        val instanceKeeperDispatcher = InstanceKeeperDispatcher()
-        val holder = stackHolder(instanceKeeperDispatcher = instanceKeeperDispatcher)
-        val instance = TestInstance()
-        holder.stack.active.instanceKeeperDispatcher.put("key", instance)
-
-        instanceKeeperDispatcher.destroy()
-
-        assertTrue(instance.isDestroyed)
-    }
-
-    @Test
-    fun WHEN_recreated_THEN_active_component_restored() {
-        val stackSaver = stackSaver()
-        val oldHolder = stackHolder(stackSaver = stackSaver)
-        val originalStack = oldHolder.stack
-        val savedState = stackSaver.save()
-
-        val newHolder = stackHolder(stackSaver = stackSaver(savedState = savedState))
-
-        assertEquals(originalStack.active.configuration, newHolder.stack.active.configuration)
-    }
-
-    @Test
-    fun WHEN_recreated_THEN_back_stack_restored() {
-        val stackSaver = stackSaver()
-        val oldHolder = stackHolder(stackSaver = stackSaver)
-        val originalStack = oldHolder.stack
-        val savedState = stackSaver.save()
-
-        val newHolder = stackHolder(stackSaver = stackSaver(savedState = savedState))
-
-        assertEquals(originalStack.backStack.map { it.configuration }, newHolder.stack.backStack.map { it.configuration })
-    }
-
-    @Test
-    fun WHEN_recreated_THEN_state_restored_for_active_component() {
-        val stackSaver = stackSaver()
-        val oldHolder = stackHolder(stackSaver = stackSaver)
-        val originalStack = oldHolder.stack
-        val savedComponentState = ParcelableStub()
-        originalStack.active.stateKeeperDispatcher.register("key") { savedComponentState }
-        val savedState = stackSaver.save()
-
-        val newHolder = stackHolder(stackSaver = stackSaver(savedState = savedState))
-
-        val restoredComponentState = newHolder.stack.active.stateKeeperDispatcher.consume<ParcelableStub>("key")
-        assertSame(savedComponentState, restoredComponentState)
-    }
-
-    @Test
-    fun WHEN_recreated_THEN_state_restored_for_back_stack() {
-        val stackSaver = stackSaver()
-        val oldHolder = stackHolder(stackSaver = stackSaver)
-        val savedState1 = ParcelableContainer()
-        val savedState2 = ParcelableContainer()
-
-        val originalStack =
-            RouterStack(
-                active = TestRouterEntryFactory.invoke(configuration = Config("3")),
-                backStack = listOf(
-                    RouterEntry.Destroyed(configuration = Config("1"), savedState = savedState1),
-                    RouterEntry.Destroyed(configuration = Config("2"), savedState = savedState2)
-                )
-            )
-
-        oldHolder.stack = originalStack
-        val savedState = stackSaver.save()
-
-        val newHolder = stackHolder(stackSaver = stackSaver(savedState = savedState))
-
-        assertEquals(listOf(savedState1, savedState2), newHolder.stack.backStack.map { it.savedState })
-    }
-
-    private fun stackHolder(
-        initialConfiguration: Config = Config("1"),
-        stackSaver: StackSaver<Config> = stackSaver(),
-        instanceKeeperDispatcher: InstanceKeeperDispatcher = InstanceKeeperDispatcher(),
-        routerEntryFactory: RouterEntryFactory<Config, Component> = TestRouterEntryFactory
-    ): StackHolderImpl<Config, Component> =
-        StackHolderImpl(
-            initialStack = { listOf(initialConfiguration) },
-            lifecycle = LifecycleRegistry(),
-            key = "key",
-            stackSaver = stackSaver,
-            instanceKeeper = instanceKeeperDispatcher,
-            routerEntryFactory = routerEntryFactory
-        )
-
-    private fun stackSaver(savedState: TestStackSaver.SavedState<Config>? = null): TestStackSaver<Config> =
-        TestStackSaver(
-            copyConfiguration = Config::copy,
-            savedState = savedState
-        )
-
-    private fun ChildBackHandler.asTest(): TestChildBackHandler = this as TestChildBackHandler
-
-    private data class Config(val key: String)
-
-    private class Component
-
-    private object TestRouterEntryFactory : RouterEntryFactory<Config, Component> {
-        override fun invoke(
-            configuration: Config,
-            savedState: ParcelableContainer?,
-            instanceKeeperDispatcher: InstanceKeeperDispatcher?
-        ): RouterEntry.Created<Config, Component> {
-            val instanceKeeper = instanceKeeperDispatcher ?: InstanceKeeperDispatcher()
-            val stateKeeper = TestStateKeeperDispatcher(savedState)
-
-            return RouterEntry.Created(
-                configuration = configuration,
-                savedState = null,
-                instance = Component(),
-                lifecycleRegistry = LifecycleRegistry(),
-                stateKeeperDispatcher = stateKeeper,
-                instanceKeeperDispatcher = instanceKeeper,
-                backHandler = TestChildBackHandler(isEnabled = true),
-            )
-        }
-    }
-}
diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/TestStackSaver.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/TestStackSaver.kt
deleted file mode 100644
index a917900a..00000000
--- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/TestStackSaver.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.arkivanov.decompose.router.stack
-
-import com.arkivanov.decompose.router.stack.StackSaver.RestoredStack
-
-internal class TestStackSaver<C : Any>(
-    private val copyConfiguration: (C) -> C,
-    savedState: SavedState<C>? = null
-) : StackSaver<C> {
-
-    private val map: MutableMap<String, RestoredStack<C>> = savedState?.map ?: HashMap()
-    private val suppliers = HashMap<String, () -> RouterStack<C, *>>()
-
-    override fun register(key: String, supplier: () -> RouterStack<C, *>) {
-        check(key !in suppliers)
-        suppliers[key] = supplier
-    }
-
-    override fun unregister(key: String) {
-        check(key in suppliers)
-        suppliers -= key
-    }
-
-    override fun restore(key: String): RestoredStack<C>? =
-        map.remove(key)
-
-    fun save(): SavedState<C> =
-        SavedState(
-            suppliers.mapValuesTo(HashMap()) { (_, suppliers) ->
-                suppliers().save()
-            }
-        )
-
-    private fun RouterStack<C, *>.save(): RestoredStack<C> =
-        RestoredStack(
-            active = RouterEntry.Destroyed(
-                configuration = copyConfiguration(active.configuration),
-                savedState = active.stateKeeperDispatcher.save()
-            ),
-            backStack = backStack.map {
-                RouterEntry.Destroyed(
-                    configuration = copyConfiguration(it.configuration),
-                    savedState = it.savedState
-                )
-            }
-        )
-
-    class SavedState<C : Any>(
-        val map: MutableMap<String, RestoredStack<C>>
-    )
-}