Repository: arkivanov/Decompose
Pull request #244 (2022-10-24 20:27:20)

Update compose-darwin branch


Patch:
diff --git a/decompose/api/android/decompose.api b/decompose/api/android/decompose.api
index 7bdc249e..f626bc80 100644
--- a/decompose/api/android/decompose.api
+++ b/decompose/api/android/decompose.api
@@ -7,6 +7,7 @@ public final class com/arkivanov/decompose/BuildConfig {
 
 public abstract class com/arkivanov/decompose/Child {
 	public abstract fun getConfiguration ()Ljava/lang/Object;
+	public abstract fun getInstance ()Ljava/lang/Object;
 }
 
 public final class com/arkivanov/decompose/Child$Created : com/arkivanov/decompose/Child {
@@ -17,7 +18,7 @@ public final class com/arkivanov/decompose/Child$Created : com/arkivanov/decompo
 	public static synthetic fun copy$default (Lcom/arkivanov/decompose/Child$Created;Ljava/lang/Object;Ljava/lang/Object;ILjava/lang/Object;)Lcom/arkivanov/decompose/Child$Created;
 	public fun equals (Ljava/lang/Object;)Z
 	public fun getConfiguration ()Ljava/lang/Object;
-	public final fun getInstance ()Ljava/lang/Object;
+	public fun getInstance ()Ljava/lang/Object;
 	public fun hashCode ()I
 	public fun toString ()Ljava/lang/String;
 }
@@ -29,6 +30,8 @@ public final class com/arkivanov/decompose/Child$Destroyed : com/arkivanov/decom
 	public static synthetic fun copy$default (Lcom/arkivanov/decompose/Child$Destroyed;Ljava/lang/Object;ILjava/lang/Object;)Lcom/arkivanov/decompose/Child$Destroyed;
 	public fun equals (Ljava/lang/Object;)Z
 	public fun getConfiguration ()Ljava/lang/Object;
+	public synthetic fun getInstance ()Ljava/lang/Object;
+	public fun getInstance ()Ljava/lang/Void;
 	public fun hashCode ()I
 	public fun toString ()Ljava/lang/String;
 }
@@ -70,6 +73,40 @@ public final class com/arkivanov/decompose/lifecycle/MergedLifecycle : com/arkiv
 	public fun unsubscribe (Lcom/arkivanov/essenty/lifecycle/Lifecycle$Callbacks;)V
 }
 
+public abstract interface class com/arkivanov/decompose/router/children/ChildNavState {
+	public abstract fun getConfiguration ()Ljava/lang/Object;
+	public abstract fun getStatus ()Lcom/arkivanov/decompose/router/children/ChildNavState$Status;
+}
+
+public final class com/arkivanov/decompose/router/children/ChildNavState$Status : java/lang/Enum {
+	public static final field ACTIVE Lcom/arkivanov/decompose/router/children/ChildNavState$Status;
+	public static final field DESTROYED Lcom/arkivanov/decompose/router/children/ChildNavState$Status;
+	public static final field INACTIVE Lcom/arkivanov/decompose/router/children/ChildNavState$Status;
+	public static fun valueOf (Ljava/lang/String;)Lcom/arkivanov/decompose/router/children/ChildNavState$Status;
+	public static fun values ()[Lcom/arkivanov/decompose/router/children/ChildNavState$Status;
+}
+
+public final class com/arkivanov/decompose/router/children/ChildrenFactoryKt {
+	public static final fun children (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Ljava/lang/String;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value;
+}
+
+public abstract interface class com/arkivanov/decompose/router/children/NavState {
+	public abstract fun getChildren ()Ljava/util/List;
+}
+
+public abstract interface class com/arkivanov/decompose/router/children/NavigationSource {
+	public abstract fun subscribe (Lkotlin/jvm/functions/Function1;)V
+	public abstract fun unsubscribe (Lkotlin/jvm/functions/Function1;)V
+}
+
+public final class com/arkivanov/decompose/router/children/SavedState$Creator : android/os/Parcelable$Creator {
+	public fun <init> ()V
+	public final fun createFromParcel (Landroid/os/Parcel;)Lcom/arkivanov/decompose/router/children/SavedState;
+	public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object;
+	public final fun newArray (I)[Lcom/arkivanov/decompose/router/children/SavedState;
+	public synthetic fun newArray (I)[Ljava/lang/Object;
+}
+
 public final class com/arkivanov/decompose/router/overlay/ChildOverlay {
 	public fun <init> ()V
 	public fun <init> (Lcom/arkivanov/decompose/Child$Created;)V
@@ -95,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 {
@@ -151,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 {
@@ -183,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 55cd07ca..9ab45558 100644
--- a/decompose/api/jvm/decompose.api
+++ b/decompose/api/jvm/decompose.api
@@ -1,5 +1,6 @@
 public abstract class com/arkivanov/decompose/Child {
 	public abstract fun getConfiguration ()Ljava/lang/Object;
+	public abstract fun getInstance ()Ljava/lang/Object;
 }
 
 public final class com/arkivanov/decompose/Child$Created : com/arkivanov/decompose/Child {
@@ -10,7 +11,7 @@ public final class com/arkivanov/decompose/Child$Created : com/arkivanov/decompo
 	public static synthetic fun copy$default (Lcom/arkivanov/decompose/Child$Created;Ljava/lang/Object;Ljava/lang/Object;ILjava/lang/Object;)Lcom/arkivanov/decompose/Child$Created;
 	public fun equals (Ljava/lang/Object;)Z
 	public fun getConfiguration ()Ljava/lang/Object;
-	public final fun getInstance ()Ljava/lang/Object;
+	public fun getInstance ()Ljava/lang/Object;
 	public fun hashCode ()I
 	public fun toString ()Ljava/lang/String;
 }
@@ -22,6 +23,8 @@ public final class com/arkivanov/decompose/Child$Destroyed : com/arkivanov/decom
 	public static synthetic fun copy$default (Lcom/arkivanov/decompose/Child$Destroyed;Ljava/lang/Object;ILjava/lang/Object;)Lcom/arkivanov/decompose/Child$Destroyed;
 	public fun equals (Ljava/lang/Object;)Z
 	public fun getConfiguration ()Ljava/lang/Object;
+	public synthetic fun getInstance ()Ljava/lang/Object;
+	public fun getInstance ()Ljava/lang/Void;
 	public fun hashCode ()I
 	public fun toString ()Ljava/lang/String;
 }
@@ -57,6 +60,32 @@ public final class com/arkivanov/decompose/lifecycle/MergedLifecycle : com/arkiv
 	public fun unsubscribe (Lcom/arkivanov/essenty/lifecycle/Lifecycle$Callbacks;)V
 }
 
+public abstract interface class com/arkivanov/decompose/router/children/ChildNavState {
+	public abstract fun getConfiguration ()Ljava/lang/Object;
+	public abstract fun getStatus ()Lcom/arkivanov/decompose/router/children/ChildNavState$Status;
+}
+
+public final class com/arkivanov/decompose/router/children/ChildNavState$Status : java/lang/Enum {
+	public static final field ACTIVE Lcom/arkivanov/decompose/router/children/ChildNavState$Status;
+	public static final field DESTROYED Lcom/arkivanov/decompose/router/children/ChildNavState$Status;
+	public static final field INACTIVE Lcom/arkivanov/decompose/router/children/ChildNavState$Status;
+	public static fun valueOf (Ljava/lang/String;)Lcom/arkivanov/decompose/router/children/ChildNavState$Status;
+	public static fun values ()[Lcom/arkivanov/decompose/router/children/ChildNavState$Status;
+}
+
+public final class com/arkivanov/decompose/router/children/ChildrenFactoryKt {
+	public static final fun children (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/children/NavigationSource;Ljava/lang/String;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value;
+}
+
+public abstract interface class com/arkivanov/decompose/router/children/NavState {
+	public abstract fun getChildren ()Ljava/util/List;
+}
+
+public abstract interface class com/arkivanov/decompose/router/children/NavigationSource {
+	public abstract fun subscribe (Lkotlin/jvm/functions/Function1;)V
+	public abstract fun unsubscribe (Lkotlin/jvm/functions/Function1;)V
+}
+
 public final class com/arkivanov/decompose/router/overlay/ChildOverlay {
 	public fun <init> ()V
 	public fun <init> (Lcom/arkivanov/decompose/Child$Created;)V
@@ -82,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 {
@@ -138,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/Child.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/Child.kt
index 44c4652d..8d14111a 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/Child.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/Child.kt
@@ -3,13 +3,16 @@ package com.arkivanov.decompose
 sealed class Child<out C : Any, out T : Any> {
 
     abstract val configuration: C
+    abstract val instance: T?
 
     data class Created<out C : Any, out T : Any>(
         override val configuration: C,
-        val instance: T
+        override val instance: T,
     ) : Child<C, T>()
 
     data class Destroyed<out C : Any>(
-        override val configuration: C
-    ) : Child<C, Nothing>()
+        override val configuration: C,
+    ) : Child<C, Nothing>() {
+        override val instance: Nothing? = null
+    }
 }
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/backhandler/DefaultChildBackHandler.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/backhandler/DefaultChildBackHandler.kt
index 10d87aa4..96b05bb7 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/backhandler/DefaultChildBackHandler.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/backhandler/DefaultChildBackHandler.kt
@@ -14,13 +14,20 @@ internal class DefaultChildBackHandler(
     private val enabledChangedListener: (Boolean) -> Unit = { updateParentCallbackEnabledState() }
 
     override var isEnabled: Boolean by observable(isEnabled) { _, _, _ -> updateParentCallbackEnabledState() }
+    private var isStarted = false
 
     override fun start() {
-        parent.register(parentCallback)
+        if (!isStarted) {
+            isStarted = true
+            parent.register(parentCallback)
+        }
     }
 
     override fun stop() {
-        parent.unregister(parentCallback)
+        if (isStarted) {
+            isStarted = false
+            parent.unregister(parentCallback)
+        }
     }
 
     override fun register(callback: BackCallback) {
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterEntry.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildItem.kt
similarity index 70%
rename from decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterEntry.kt
rename to decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildItem.kt
index f731a2ad..392cb0c7 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterEntry.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildItem.kt
@@ -1,4 +1,4 @@
-package com.arkivanov.decompose.router.stack
+package com.arkivanov.decompose.router.children
 
 import com.arkivanov.decompose.backhandler.ChildBackHandler
 import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher
@@ -6,22 +6,26 @@ 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?
+internal sealed interface ChildItem<out C : Any, out T : Any> {
+
+    val configuration: C
+    val instance: T?
+    val savedState: ParcelableContainer?
 
     data class Created<out C : Any, out T : Any>(
         override val configuration: C,
         override val savedState: ParcelableContainer? = null,
-        val instance: T,
+        override val instance: T,
         val lifecycleRegistry: LifecycleRegistry,
         val stateKeeperDispatcher: StateKeeperDispatcher,
         val instanceKeeperDispatcher: InstanceKeeperDispatcher,
         val backHandler: ChildBackHandler,
-    ) : RouterEntry<C, T>()
+    ) : ChildItem<C, T>
 
     data class Destroyed<out C : Any>(
         override val configuration: C,
         override val savedState: ParcelableContainer? = null
-    ) : RouterEntry<C, Nothing>()
+    ) : ChildItem<C, Nothing> {
+        override val instance: Nothing? = null
+    }
 }
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterEntryFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildItemFactory.kt
similarity index 67%
rename from decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterEntryFactory.kt
rename to decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildItemFactory.kt
index 9fa4f775..e7d2d0ea 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterEntryFactory.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildItemFactory.kt
@@ -1,13 +1,13 @@
-package com.arkivanov.decompose.router.stack
+package com.arkivanov.decompose.router.children
 
 import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher
 import com.arkivanov.essenty.parcelable.ParcelableContainer
 
-internal interface RouterEntryFactory<C : Any, out T : Any> {
+internal interface ChildItemFactory<C : Any, out T : Any> {
 
     operator fun invoke(
         configuration: C,
         savedState: ParcelableContainer? = null,
-        instanceKeeperDispatcher: InstanceKeeperDispatcher? = null
-    ): RouterEntry.Created<C, T>
+        instanceKeeperDispatcher: InstanceKeeperDispatcher? = null,
+    ): ChildItem.Created<C, T>
 }
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildNavState.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildNavState.kt
new file mode 100644
index 00000000..fc91e951
--- /dev/null
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildNavState.kt
@@ -0,0 +1,44 @@
+package com.arkivanov.decompose.router.children
+
+import com.arkivanov.decompose.ExperimentalDecomposeApi
+
+/**
+ * Represents a child navigation state.
+ */
+@ExperimentalDecomposeApi
+interface ChildNavState<out C : Any> {
+
+    /**
+     * A configuration of the child. Must be unique within the [NavState].
+     */
+    val configuration: C
+
+    /**
+     * Required lifecycle status of the child.
+     */
+    val status: Status
+
+    /**
+     * Enumerates all possible child lifecycle statuses.
+     */
+    enum class Status {
+        /**
+         * The child component is instantiated and active. Its maximum lifecycle state is `RESUMED`,
+         * depending on the parent's lifecycle state. An active component can handle back button presses.
+         * The state of the component is saved when it switches from `ACTIVE` to any other status.
+         */
+        DESTROYED,
+
+        /**
+         * The child component is instantiated and inactive. Its maximum lifecycle state is `CREATED`,
+         * depending on the parent's lifecycle state. An inactive component cannot handle back button presses.
+         */
+        INACTIVE,
+
+        /**
+         * The child is created and may be resumed, depending on the lifecycle of the hosting component.
+         * Back button handling is enabled.
+         */
+        ACTIVE,
+    }
+}
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
new file mode 100644
index 00000000..5a3bce3b
--- /dev/null
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt
@@ -0,0 +1,140 @@
+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
+import com.arkivanov.essenty.backhandler.BackCallback
+import com.arkivanov.essenty.instancekeeper.getOrCreate
+import com.arkivanov.essenty.lifecycle.doOnDestroy
+import com.arkivanov.essenty.parcelable.Parcelable
+import com.arkivanov.essenty.parcelable.ParcelableContainer
+import com.arkivanov.essenty.parcelable.Parcelize
+import com.arkivanov.essenty.statekeeper.consume
+
+/**
+ * Initialised and manages a generic list of components. This is an API for custom navigation models.
+ * Please consider the existing navigation models first:
+ * [childStack][com.arkivanov.decompose.router.stack.childStack],
+ * [childOverlay][com.arkivanov.decompose.router.overlay.childOverlay].
+ *
+ * The API is based around [NavState] and [ChildNavState] interfaces that should be implemented by
+ * clients. [NavState] represents a persistent state of the navigation. It also holds a navigation
+ * state for each child - [ChildNavState]. Both [NavState] and [ChildNavState] must be immutable, and
+ * correctly implement `equals` and `hashCode` methods (or just be data classes). There must be no
+ * duplicated (by equality) [ChildNavState.configuration] within a [NavState].
+ *
+ * The navigation is performed by transforming the current [NavState] to a new one. The implementation
+ * calculates diffs between the old list of [ChildNavState] and the new one, and manipulates child
+ * components as needed.
+ *
+ * @param C a type of component configurations.
+ * @param T a type of components.
+ * @param E a type of navigation events.
+ * @param N a type of navigation state, must implement [NavState] interface.
+ * @param S a type of the resulting children state.
+ * @param source a source of navigation events.
+ * @param key a key of this `children` collection, must be unique if there are multiple
+ * `children` used in the same component.
+ * @param initialNavState an initial navigation state that should be used if there is no previously saved state.
+ * @param saveNavState a function that saves the provided navigation state into [ParcelableContainer].
+ * @param restoreNavState a function that restores the navigation state from the provided [ParcelableContainer].
+ * The restored navigation state must have the same amount of child configurations and in the same order.
+ * The restored child [Statuses][ChildNavState.Status] can be any, e.g. a previously active child may become
+ * destroyed, etc.
+ * @param navTransformer a function that transforms the current navigation state to a new one using the provided
+ * navigation event.
+ * @param onEventComplete called when a navigation event is processed and the navigation completed.
+ * @param backTransformer a function that checks the provided navigation state, and either returns another function
+ * transforming the navigation state to a new one, or `null` if back button handling should be disabled. Called
+ * during the initialisation and after each navigation event.
+ * @param stateMapper combines the provided navigation state and list of child components to a resulting state.
+ * @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,
+    initialNavState: () -> N,
+    saveNavState: (navState: N) -> ParcelableContainer,
+    restoreNavState: (container: ParcelableContainer) -> N,
+    navTransformer: (navState: N, event: E) -> N,
+    onEventComplete: (event: E, newNavState: N, oldNavState: N) -> Unit,
+    backTransformer: (navState: N) -> (() -> N)?,
+    stateMapper: (navState: N, children: List<Child<C, T>>) -> S,
+    childFactory: (configuration: C, componentContext: ComponentContext) -> T,
+): Value<S> {
+    val mainBackHandler = backHandler.child()
+
+    val savedState = stateKeeper.consume<SavedState>(key = key)
+
+    val navigator =
+        ChildrenNavigator(
+            lifecycle = lifecycle,
+            retainedInstanceSupplier = { factory -> instanceKeeper.getOrCreate(key = key, factory = factory) },
+            childItemFactory = DefaultChildItemFactory(
+                lifecycle = lifecycle,
+                backHandler = backHandler.child(),
+                childFactory = childFactory,
+            ),
+            navState = savedState?.navState?.let(restoreNavState) ?: initialNavState(),
+            savedChildState = savedState?.childState,
+        )
+
+    stateKeeper.register(key = key) {
+        SavedState(
+            navState = saveNavState(navigator.navState),
+            childState = navigator.saveChildState(),
+        )
+    }
+
+    val state = MutableValue(stateMapper(navigator.navState, navigator.children))
+
+    var bt: (() -> N)? = backTransformer(navigator.navState)
+
+    lateinit var backCallback: BackCallback
+
+    fun onAfterNavigate(navState: N) {
+        bt = backTransformer(navState)
+        backCallback.isEnabled = bt != null
+        state.value = stateMapper(navState, navigator.children)
+    }
+
+    backCallback =
+        BackCallback(isEnabled = bt != null) {
+            bt?.invoke()?.also { navState ->
+                navigator.navigate(navState = navState)
+                onAfterNavigate(navState)
+            }
+        }
+
+    mainBackHandler.register(backCallback)
+
+    val eventObserver: (E) -> Unit =
+        { event ->
+            val oldNavState = navigator.navState
+            navigator.navigate(navState = navTransformer(navigator.navState, event))
+            val newNavState = navigator.navState
+            onAfterNavigate(newNavState)
+            onEventComplete(event, newNavState, oldNavState)
+        }
+
+    source.subscribe(eventObserver)
+
+    lifecycle.doOnDestroy {
+        source.unsubscribe(eventObserver)
+        stateKeeper.unregister(key = key)
+        mainBackHandler.unregister(backCallback)
+    }
+
+    return state
+}
+
+@Parcelize
+private class SavedState(
+    val navState: ParcelableContainer,
+    val childState: List<ParcelableContainer?>,
+) : Parcelable
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
new file mode 100644
index 00000000..709791bf
--- /dev/null
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenNavigator.kt
@@ -0,0 +1,263 @@
+package com.arkivanov.decompose.router.children
+
+import com.arkivanov.decompose.Child
+import com.arkivanov.decompose.ExperimentalDecomposeApi
+import com.arkivanov.decompose.router.children.ChildItem.Created
+import com.arkivanov.decompose.router.children.ChildItem.Created.*
+import com.arkivanov.decompose.router.children.ChildNavState.Status
+import com.arkivanov.essenty.instancekeeper.InstanceKeeper
+import com.arkivanov.essenty.lifecycle.Lifecycle
+import com.arkivanov.essenty.lifecycle.create
+import com.arkivanov.essenty.lifecycle.destroy
+import com.arkivanov.essenty.lifecycle.doOnDestroy
+import com.arkivanov.essenty.lifecycle.resume
+import com.arkivanov.essenty.lifecycle.stop
+import com.arkivanov.essenty.parcelable.ParcelableContainer
+
+@OptIn(ExperimentalDecomposeApi::class)
+internal class ChildrenNavigator<out C : Any, out T : Any, N : NavState<C>>(
+    lifecycle: Lifecycle,
+    retainedInstanceSupplier: (factory: () -> InstanceKeeper.Instance) -> InstanceKeeper.Instance,
+    private val childItemFactory: ChildItemFactory<C, T>,
+    navState: N,
+    savedChildState: List<ParcelableContainer?>?,
+) {
+    var navState: N = navState
+        private set
+
+    private val items = ArrayList<ChildItem<C, T>>()
+
+    val children: List<Child<C, T>>
+        get() =
+            items.map { item ->
+                val instance = item.instance
+                if (instance != null) {
+                    Child.Created(configuration = item.configuration, instance = instance)
+                } else {
+                    Child.Destroyed(configuration = item.configuration)
+                }
+            }
+
+    @Suppress("UNCHECKED_CAST")
+    private val retainedInstance = retainedInstanceSupplier { RetainedInstance<C, T>() } as RetainedInstance<C, T>
+
+    init {
+        if (savedChildState == null) {
+            retainedInstance.onDestroy()
+            switch(newStates = navState.children)
+        } else {
+            restore(navState = navState, savedStates = savedChildState)
+        }
+
+        lifecycle.doOnDestroy {
+            items.forEach { item ->
+                when (item) {
+                    is Created -> {
+                        item.backHandler.stop()
+                        item.lifecycleRegistry.destroy()
+                    }
+
+                    is ChildItem.Destroyed -> Unit
+                }
+            }
+        }
+    }
+
+    private fun restore(navState: N, savedStates: List<ParcelableContainer?>) {
+        val retainedChildren = retainedInstance.items.associateByTo(HashMap(), Created<C, *>::configuration)
+        retainedInstance.items.clear()
+
+        navState.children.zip(savedStates).forEach { (childNavState, savedState) ->
+            items +=
+                when (childNavState.status) {
+                    Status.DESTROYED -> ChildItem.Destroyed(configuration = childNavState.configuration, savedState = savedState)
+
+                    Status.INACTIVE -> {
+                        childItemFactory(
+                            configuration = childNavState.configuration,
+                            savedState = savedState,
+                            instanceKeeperDispatcher = retainedChildren.remove(childNavState.configuration)?.instanceKeeperDispatcher,
+                        ).also {
+                            retainedInstance.items += it
+                            it.lifecycleRegistry.create()
+                        }
+                    }
+
+                    Status.ACTIVE ->
+                        childItemFactory(
+                            configuration = childNavState.configuration,
+                            savedState = savedState,
+                            instanceKeeperDispatcher = retainedChildren.remove(childNavState.configuration)?.instanceKeeperDispatcher,
+                        ).also {
+                            it.backHandler.start()
+                            retainedInstance.items += it
+                            it.lifecycleRegistry.resume()
+                        }
+                }
+        }
+
+        retainedChildren.values.forEach { it.instanceKeeperDispatcher.destroy() }
+    }
+
+    fun saveChildState(): List<ParcelableContainer?> =
+        items.map { item ->
+            when (item) {
+                is Created -> item.saveStateIfNeeded()
+                is ChildItem.Destroyed -> item.savedState
+            }
+        }
+
+    fun navigate(navState: N) {
+        switch(newStates = navState.children)
+        this.navState = navState
+    }
+
+    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(newConfigurations = newConfigurations, oldItems = oldItems.values)
+        processNewItems(newItems = newItems)
+    }
+
+    private fun prepareNewItems(
+        newStates: List<ChildNavState<C>>,
+        oldItems: Map<C, ChildItem<C, T>>,
+    ): List<Pair<ChildItem<C, T>, Status>> {
+        val newItems = ArrayList<Pair<ChildItem<C, T>, Status>>(newStates.size)
+
+        newStates.forEach { state ->
+            newItems +=
+                when (val child = oldItems[state.configuration]) {
+                    is Created -> child to state.status
+
+                    is ChildItem.Destroyed ->
+                        when (state.status) {
+                            Status.DESTROYED -> child to state.status
+
+                            Status.INACTIVE ->
+                                Pair(
+                                    first = childItemFactory(
+                                        configuration = state.configuration,
+                                        savedState = child.savedState
+                                    ).apply { lifecycleRegistry.create() },
+                                    second = state.status,
+                                )
+
+                            Status.ACTIVE ->
+                                Pair(
+                                    first = childItemFactory(
+                                        configuration = state.configuration,
+                                        savedState = child.savedState,
+                                    ).apply { lifecycleRegistry.create() },
+                                    second = state.status,
+                                )
+                        }
+
+                    null ->
+                        when (state.status) {
+                            Status.DESTROYED -> ChildItem.Destroyed(configuration = state.configuration) to state.status
+
+                            Status.INACTIVE ->
+                                Pair(
+                                    first = childItemFactory(configuration = state.configuration)
+                                        .apply { lifecycleRegistry.create() },
+                                    second = state.status,
+                                )
+
+                            Status.ACTIVE ->
+                                Pair(
+                                    first = childItemFactory(configuration = state.configuration)
+                                        .apply { lifecycleRegistry.create() },
+                                    second = state.status,
+                                )
+                        }
+                }
+        }
+
+        return newItems
+    }
+
+    private fun destroyOldItems(
+        newConfigurations: Set<C>,
+        oldItems: Collection<ChildItem<C, T>>,
+    ) {
+        for (item in oldItems) {
+            val child = item as? Created ?: continue
+            if (item.configuration !in newConfigurations) {
+                child.backHandler.stop()
+                child.instanceKeeperDispatcher.destroy()
+                child.lifecycleRegistry.destroy()
+            }
+        }
+    }
+
+    private fun Created<*, *>.saveStateIfNeeded(): ParcelableContainer? =
+        if (lifecycleRegistry.state > Lifecycle.State.CREATED) {
+            stateKeeperDispatcher.save()
+        } else {
+            savedState
+        }
+
+    private fun processNewItems(newItems: List<Pair<ChildItem<C, T>, Status>>) {
+        items.clear()
+        retainedInstance.items.clear()
+
+        newItems.forEach { (item, status) ->
+            items +=
+                when (item) {
+                    is Created -> {
+                        when (status) {
+                            Status.DESTROYED -> {
+                                val savedState = item.saveStateIfNeeded()
+                                item.backHandler.stop()
+                                item.instanceKeeperDispatcher.destroy()
+                                item.lifecycleRegistry.destroy()
+
+                                ChildItem.Destroyed(configuration = item.configuration, savedState = savedState)
+                            }
+
+                            Status.INACTIVE -> {
+                                retainedInstance.items += item
+
+                                item
+                                    .takeIf { it.lifecycleRegistry.state != Lifecycle.State.CREATED }
+                                    ?.copy(savedState = item.saveStateIfNeeded())
+                                    ?.apply {
+                                        backHandler.stop()
+                                        lifecycleRegistry.stop()
+                                    }
+                                    ?: item
+                            }
+
+                            Status.ACTIVE -> {
+                                retainedInstance.items += item
+
+                                item
+                                    .takeIf { it.lifecycleRegistry.state != Lifecycle.State.RESUMED }
+                                    ?.copy(savedState = null)
+                                    ?.apply {
+                                        backHandler.start()
+                                        lifecycleRegistry.resume()
+                                    }
+                                    ?: item
+                            }
+                        }
+                    }
+
+                    is ChildItem.Destroyed -> item
+                }
+        }
+    }
+
+    private class RetainedInstance<C : Any, T : Any> : InstanceKeeper.Instance {
+        val items: MutableList<Created<C, T>> = ArrayList()
+
+        override fun onDestroy() {
+            items.forEach { it.instanceKeeperDispatcher.destroy() }
+            items.clear()
+        }
+    }
+}
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterEntryFactoryImpl.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/DefaultChildItemFactory.kt
similarity index 89%
rename from decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterEntryFactoryImpl.kt
rename to decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/DefaultChildItemFactory.kt
index 011a8137..0bdf7e85 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/RouterEntryFactoryImpl.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/DefaultChildItemFactory.kt
@@ -1,4 +1,4 @@
-package com.arkivanov.decompose.router.stack
+package com.arkivanov.decompose.router.children
 
 import com.arkivanov.decompose.ComponentContext
 import com.arkivanov.decompose.DefaultComponentContext
@@ -11,17 +11,17 @@ 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>(
+internal class DefaultChildItemFactory<C : Any, out T : Any>(
     private val lifecycle: Lifecycle,
     private val backHandler: BackHandler,
-    private val childFactory: (configuration: C, ComponentContext) -> T
-) : RouterEntryFactory<C, T> {
+    private val childFactory: (configuration: C, ComponentContext) -> T,
+) : ChildItemFactory<C, T> {
 
     override fun invoke(
         configuration: C,
         savedState: ParcelableContainer?,
         instanceKeeperDispatcher: InstanceKeeperDispatcher?
-    ): RouterEntry.Created<C, T> {
+    ): ChildItem.Created<C, T> {
         val componentLifecycleRegistry = LifecycleRegistry()
         val mergedLifecycle = MergedLifecycle(lifecycle, componentLifecycleRegistry)
         val stateKeeperDispatcher = StateKeeperDispatcher(savedState)
@@ -39,7 +39,7 @@ internal class RouterEntryFactoryImpl<C : Any, out T : Any>(
                 )
             )
 
-        return RouterEntry.Created(
+        return ChildItem.Created(
             configuration = configuration,
             instance = component,
             lifecycleRegistry = componentLifecycleRegistry,
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/NavState.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/NavState.kt
new file mode 100644
index 00000000..3b5a1f17
--- /dev/null
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/NavState.kt
@@ -0,0 +1,15 @@
+package com.arkivanov.decompose.router.children
+
+import com.arkivanov.decompose.ExperimentalDecomposeApi
+
+/**
+ * Represents an entire navigation state.
+ */
+@ExperimentalDecomposeApi
+interface NavState<out C : Any> {
+
+    /**
+     * A list of child navigation states. Every [ChildNavState.configuration] must be unique by equality.
+     */
+    val children: List<ChildNavState<C>>
+}
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/NavigationSource.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/NavigationSource.kt
new file mode 100644
index 00000000..e29ac492
--- /dev/null
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/NavigationSource.kt
@@ -0,0 +1,13 @@
+package com.arkivanov.decompose.router.children
+
+/**
+ * Represents a generic source of navigation events.
+ *
+ * @see [children]
+ */
+interface NavigationSource<out T : Any> {
+
+    fun subscribe(observer: (T) -> Unit)
+
+    fun unsubscribe(observer: (T) -> Unit)
+}
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/SimpleChildNavState.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/SimpleChildNavState.kt
new file mode 100644
index 00000000..40fcb500
--- /dev/null
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/SimpleChildNavState.kt
@@ -0,0 +1,12 @@
+package com.arkivanov.decompose.router.children
+
+import com.arkivanov.decompose.ExperimentalDecomposeApi
+
+/**
+ * A simple implementation of the [ChildNavState] interface.
+ */
+@OptIn(ExperimentalDecomposeApi::class)
+internal data class SimpleChildNavState<out C : Any>(
+    override val configuration: C,
+    override val status: ChildNavState.Status,
+) : ChildNavState<C>
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..85a075d0 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,37 @@ 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,
+    )
+
+@OptIn(ExperimentalDecomposeApi::class)
+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 +89,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..1e442d9c 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,113 @@ 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,
     )
 
+@OptIn(ExperimentalDecomposeApi::class)
+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 +181,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/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/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
-}
diff --git a/deps.versions.toml b/deps.versions.toml
index 7d89938a..e89314e9 100644
--- a/deps.versions.toml
+++ b/deps.versions.toml
@@ -1,6 +1,6 @@
 [versions]
 
-decompose = "1.0.0-alpha-07-native-compose"
+decompose = "1.0.0-alpha-08-native-compose"
 kotlin = "1.7.20"
 essenty = "0.6.0"
 reaktive = "1.2.1"
diff --git a/docs/navigation/children/overview.md b/docs/navigation/children/overview.md
new file mode 100644
index 00000000..8ad8263d
--- /dev/null
+++ b/docs/navigation/children/overview.md
@@ -0,0 +1,67 @@
+# Generic Navigation
+
+The `Generic Navigation` can be used to create custom navigation models, when none of the predefined models fit your needs. It offers a flexible API and allows you to create almost any kind of navigation. Please check out [Child Stack](../stack/overview.md) and [Child Overlay](../overlay/overview.md) before using the `Generic Navigation`.
+
+The API is based around [NavState](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/NavState.kt) and [ChildNavState](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildNavState.kt) interfaces that should be implemented by clients. `NavState` represents a persistent state of the navigation. It also holds a navigation state for each child - `ChildNavState`. Both `NavState` and `ChildNavState` must be immutable, and correctly implement `equals` and `hashCode` methods (or just be data classes). There must be no duplicated (by equality) `ChildNavState.configuration` within a `NavState`.
+
+The navigation is performed by transforming the current `NavState` to a new one. The `Generic Navigation` implementation calculates diffs between the old list of `ChildNavState` and the new one, and manipulates child components as needed.
+
+## ChildNavState
+
+`ChildNavState` represents a state of a child component. It holds a `Configuration` that works as a key of the child component, and a `Status` that represents the required lifecycle status of the child component. As mentioned earlier, the `Configuration` must unique within the `NavState`.
+
+The `Status` can be one of the following:
+
+* `ACTIVE` - the child component is instantiated and active. Its maximum lifecycle state is `RESUMED`, depending on the parent's lifecycle state. An active component can handle back button presses. The state of the component is saved when it switches from `ACTIVE` to any other status.
+
+* `INACTIVE` - the child component is instantiated and inactive. Its maximum lifecycle state is `CREATED`, depending on the parent's lifecycle state. An inactive component cannot handle back button presses.
+
+* `DESTROYED` - the child component is destroyed but still managed, e.g. it's state may be saved and restored later.
+
+If you want to completely remove the child component from the navigation, you should remove its `ChildNavState` from the `NavState` altogether.
+
+## Using the Generic Navigation
+
+Using the `Generic Navigation` is pretty similar to any other navigation model, there is [ComponentContext.children(...)](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt) extension function.
+
+```kotlin
+fun <C : Any, T : Any, E : Any, N : NavState<C>, S : Any> ComponentContext.children(
+    source: NavigationSource<E>,
+    key: String,
+    initialNavState: () -> N,
+    saveNavState: (navState: N) -> ParcelableContainer,
+    restoreNavState: (container: ParcelableContainer) -> N,
+    navTransformer: (navState: N, event: E) -> N,
+    onEventComplete: (event: E, newNavState: N, oldNavState: N) -> Unit,
+    backTransformer: (navState: N) -> (() -> N)?,
+    stateMapper: (navState: N, children: List<Child<C, T>>) -> S,
+    childFactory: (configuration: C, componentContext: ComponentContext) -> T,
+): Value<S>
+```
+
+The `children` function has the following type parameters:
+
+- `C` - a type of component configurations.
+- `T` - a type of components.
+- `E` - a type of navigation events.
+- `N` - a type of navigation state, must implement `NavState` interface.
+- `S` - a type of the resulting children state.
+
+The `children` function accepts the following arguments:
+
+- `source: NavigationSource<E>` - an observable source of navigation events, the `Generic Navigation` subscribes to the source and performs the navigation.
+- `key: String` - a key of the navigation, must be unique if there are multiple `children` used in the same component.
+- `initialNavState: () -> N` - an initial navigation state that should be used if there is no previously saved state.
+- `saveNavState: (navState: N) -> ParcelableContainer` - a function that saves the provided navigation state into `ParcelableContainer`, called when the hosting component goes to background.
+- `restoreNavState: (container: ParcelableContainer) -> N` - a function that restores the navigation state from the provided `ParcelableContainer`. The restored navigation state must have the same amount of child configurations and in the same order. The restored child `Statuses` can be any, e.g. a previously active child may become destroyed, etc.
+- `navTransformer: (navState: N, event: E) -> N` - a function that transforms the current navigation state to a new one using the provided navigation event. The implementation diffs both navigation states and manipulates child components as needed.
+- `onEventComplete: (event: E, newNavState: N, oldNavState: N) -> Unit` - called when a navigation event is processed and the navigation completed.
+- `backTransformer: (navState: N) -> (() -> N)?` - a function that checks the provided navigation state, and either returns another function transforming the navigation state to a new one, or `null` if back button handling should be disabled. Called during the initialisation and after each navigation event.
+- `stateMapper: (navState: N, children: List<Child<C, T>>) -> S` - combines the provided navigation state and list of child components to a resulting custom state.
+- `childFactory: (configuration: C, componentContext: ComponentContext) -> T` - childFactory a factory function that creates new child component instances.
+
+The `children` function returns an observable `Value` of the resulting children state.
+
+## Examples
+
+Both [Child Stack](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/ChildStackFactory.kt) and [Child Overlay](https://github.com/arkivanov/Decompose/blob/master/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/overlay/ChildOverlayFactory.kt) are implemented using the `Generic Navigation`. Please refer to their source code for implementation details.
diff --git a/mkdocs.yml b/mkdocs.yml
index 4fa4b4da..ac287c3d 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -42,6 +42,8 @@ nav:
       - Overview: navigation/overlay/overview.md
       - Navigation: navigation/overlay/navigation.md
       - Custom ComponentContext: navigation/overlay/component-context.md
+    - Generic Navigation:
+        - Overview: navigation/children/overview.md
 
   - Extensions:
     - Overview: extensions/overview.md


Test patch:
diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBackPressedTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBackPressedTest.kt
new file mode 100644
index 00000000..99f07c90
--- /dev/null
+++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBackPressedTest.kt
@@ -0,0 +1,198 @@
+package com.arkivanov.decompose.router.children
+
+import com.arkivanov.decompose.router.children.ChildNavState.Status.ACTIVE
+import com.arkivanov.decompose.router.children.ChildNavState.Status.DESTROYED
+import com.arkivanov.decompose.router.children.ChildNavState.Status.INACTIVE
+import com.arkivanov.decompose.value.getValue
+import com.arkivanov.essenty.backhandler.BackCallback
+import kotlin.test.Test
+import kotlin.test.assertContentEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+@Suppress("TestFunctionName")
+internal class ChildrenBackPressedTest : ChildrenTestBase() {
+
+    @Test
+    fun GIVEN_children_created_with_active_child_and_enabled_callback_registered_WHEN_back_THEN_callback_called() {
+        var isCalled = false
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        children.getById(id = 3).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true })
+
+        backDispatcher.back()
+
+        assertTrue(isCalled)
+    }
+
+    @Test
+    fun GIVEN_children_created_with_active_child_and_disabled_callback_registered_WHEN_back_THEN_callback_not_called() {
+        var isCalled = false
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        children.getById(id = 3).requireInstance().backHandler.register(BackCallback(isEnabled = false) { isCalled = true })
+
+        backDispatcher.back()
+
+        assertFalse(isCalled)
+    }
+
+    @Test
+    fun GIVEN_children_created_with_inactive_child_and_enabled_callback_registered_WHEN_back_THEN_callback_not_called() {
+        var isCalled = false
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        children.getById(id = 2).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true })
+
+        backDispatcher.back()
+
+        assertFalse(isCalled)
+    }
+
+    @Test
+    fun GIVEN_children_created_with_inactive_child_and_disabled_callback_registered_WHEN_back_THEN_callback_not_called() {
+        var isCalled = false
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        children.getById(id = 2).requireInstance().backHandler.register(BackCallback(isEnabled = false) { isCalled = true })
+
+        backDispatcher.back()
+
+        assertFalse(isCalled)
+    }
+
+    @Test
+    fun GIVEN_child_switched_from_destroyed_to_inactive_and_enabled_callback_registered_WHEN_back_THEN_callback_not_called() {
+        var isCalled = false
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        navigate { listOf(1 by INACTIVE, 2 by INACTIVE, 3 by ACTIVE) }
+        children.getById(id = 1).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true })
+
+        backDispatcher.back()
+
+        assertFalse(isCalled)
+    }
+
+    @Test
+    fun GIVEN_child_switched_from_destroyed_to_active_and_enabled_callback_registered_WHEN_back_THEN_callback_called() {
+        var isCalled = false
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        navigate { listOf(1 by ACTIVE, 2 by INACTIVE, 3 by ACTIVE) }
+        children.getById(id = 1).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true })
+
+        backDispatcher.back()
+
+        assertTrue(isCalled)
+    }
+
+    @Test
+    fun GIVEN_child_switched_from_inactive_to_active_and_enabled_callback_registered_WHEN_back_THEN_callback_called() {
+        var isCalled = false
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        navigate { listOf(1 by DESTROYED, 2 by ACTIVE, 3 by ACTIVE) }
+        children.getById(id = 2).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true })
+
+        backDispatcher.back()
+
+        assertTrue(isCalled)
+    }
+
+    @Test
+    fun GIVEN_child_switched_from_active_to_inactive_and_enabled_callback_registered_WHEN_back_THEN_callback_not_called() {
+        var isCalled = false
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by INACTIVE) }
+        children.getById(id = 3).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true })
+
+        backDispatcher.back()
+
+        assertFalse(isCalled)
+    }
+
+    @Test
+    fun GIVEN_enabled_callback_registered_in_inactive_child_and_switched_to_active_WHEN_back_THEN_callback_called() {
+        var isCalled = false
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        children.getById(id = 2).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true })
+        navigate { listOf(1 by DESTROYED, 2 by ACTIVE, 3 by ACTIVE) }
+
+        backDispatcher.back()
+
+        assertTrue(isCalled)
+    }
+
+    @Test
+    fun GIVEN_enabled_callback_registered_in_inactive_child_and_switched_to_destroyed_WHEN_back_THEN_callback_not_called() {
+        var isCalled = false
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        children.getById(id = 2).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true })
+        navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by ACTIVE) }
+
+        backDispatcher.back()
+
+        assertFalse(isCalled)
+    }
+
+    @Test
+    fun GIVEN_enabled_callback_registered_in_active_child_and_switched_to_inactive_WHEN_back_THEN_callback_not_called() {
+        var isCalled = false
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        children.getById(id = 3).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true })
+        navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by INACTIVE) }
+
+        backDispatcher.back()
+
+        assertFalse(isCalled)
+    }
+
+    @Test
+    fun GIVEN_enabled_callback_registered_in_active_child_and_switched_to_destroyed_WHEN_back_THEN_callback_not_called() {
+        var isCalled = false
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        children.getById(id = 3).requireInstance().backHandler.register(BackCallback(isEnabled = true) { isCalled = true })
+        navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) }
+
+        backDispatcher.back()
+
+        assertFalse(isCalled)
+    }
+
+    @Test
+    fun GIVEN_backTransformer_returns_null_WHEN_back_THEN_returned_false() {
+        context.children(
+            initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE),
+            backTransformer = { null },
+        )
+
+        val result = backDispatcher.back()
+
+        assertFalse(result)
+    }
+
+    @Test
+    fun GIVEN_backTransformer_returns_not_null_WHEN_back_THEN_navigated() {
+        val children by context.children(
+            initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE),
+            backTransformer = { { emptyList() } },
+        )
+
+        backDispatcher.back()
+
+        assertContentEquals(emptyList(), children)
+    }
+
+    @Test
+    fun GIVEN_navigated_and_backTransformer_returns_null_WHEN_back_THEN_returned_false() {
+        context.children(
+            initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE),
+            backTransformer = { navState ->
+                if (navState.isNotEmpty()) {
+                    { emptyList() }
+                } else {
+                    null
+                }
+            },
+        )
+        navigate { emptyList() }
+
+        val result = backDispatcher.back()
+
+        assertFalse(result)
+    }
+}
diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBasicTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBasicTest.kt
new file mode 100644
index 00000000..a9a7566c
--- /dev/null
+++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenBasicTest.kt
@@ -0,0 +1,22 @@
+package com.arkivanov.decompose.router.children
+
+import com.arkivanov.decompose.router.children.ChildNavState.Status.*
+import kotlin.test.Test
+import kotlin.test.assertContentEquals
+
+@Suppress("TestFunctionName")
+internal class ChildrenBasicTest : ChildrenTestBase() {
+
+    @Test
+    fun WHEN_navigate_THEN_onComplete_called() {
+        val results = ArrayList<Pair<List<SimpleChildNavState<Config>>, List<SimpleChildNavState<Config>>>>()
+        context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+
+        navigate(transformer = { emptyList() }, onComplete = { newNavState, oldNavState -> results += newNavState to oldNavState })
+
+        assertContentEquals(
+            listOf(emptyList<SimpleChildNavState<Config>>() to listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)),
+            results,
+        )
+    }
+}
diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenLifecycleTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenLifecycleTest.kt
new file mode 100644
index 00000000..7b763c70
--- /dev/null
+++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenLifecycleTest.kt
@@ -0,0 +1,190 @@
+package com.arkivanov.decompose.router.children
+
+import com.arkivanov.decompose.router.children.ChildNavState.Status.ACTIVE
+import com.arkivanov.decompose.router.children.ChildNavState.Status.DESTROYED
+import com.arkivanov.decompose.router.children.ChildNavState.Status.INACTIVE
+import com.arkivanov.decompose.value.getValue
+import com.arkivanov.essenty.lifecycle.Lifecycle
+import com.arkivanov.essenty.lifecycle.destroy
+import com.arkivanov.essenty.lifecycle.stop
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+@Suppress("TestFunctionName")
+internal class ChildrenLifecycleTest : ChildrenTestBase() {
+
+    @Test
+    fun WHEN_created_THEN_initial_state() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+
+        children.assertChildren(1 to null, 2 to 2, 3 to 3)
+    }
+
+    @Test
+    fun WHEN_created_THEN_lifecycles_correct() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+
+        assertEquals(Lifecycle.State.CREATED, children.getById(id = 2).requireInstance().lifecycle.state)
+        assertEquals(Lifecycle.State.RESUMED, children.getById(id = 3).requireInstance().lifecycle.state)
+    }
+
+    @Test
+    fun WHEN_children_added_THEN_state_updated() {
+        val children by context.children()
+
+        navigate { it + (1 by DESTROYED) }
+        navigate { it + (2 by INACTIVE) }
+        navigate { it + (3 by ACTIVE) }
+
+        children.assertChildren(1 to null, 2 to 2, 3 to 3)
+    }
+
+    @Test
+    fun WHEN_children_added_THEN_lifecycles_correct() {
+        val children by context.children()
+
+        navigate { it + (1 by DESTROYED) }
+        navigate { it + (2 by INACTIVE) }
+        navigate { it + (3 by ACTIVE) }
+
+        assertEquals(Lifecycle.State.CREATED, children.getById(id = 2).requireInstance().lifecycle.state)
+        assertEquals(Lifecycle.State.RESUMED, children.getById(id = 3).requireInstance().lifecycle.state)
+    }
+
+    @Test
+    fun WHEN_destroyed_child_removed_THEN_state_updated() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+
+        navigate { navState -> navState.filterNot { it.configuration.id == 1 } }
+
+        children.assertChildren(2 to 2, 3 to 3)
+    }
+
+    @Test
+    fun WHEN_inactive_child_removed_THEN_state_updated() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+
+        navigate { navState -> navState.filterNot { it.configuration.id == 2 } }
+
+        children.assertChildren(1 to null, 3 to 3)
+    }
+
+    @Test
+    fun WHEN_inactive_child_removed_THEN_lifecycle_destroyed() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        val component = children.getById(id = 2).requireInstance()
+
+        navigate { navState -> navState.filterNot { it.configuration.id == 2 } }
+
+        assertEquals(Lifecycle.State.DESTROYED, component.lifecycle.state)
+    }
+
+    @Test
+    fun WHEN_active_child_removed_THEN_state_updated() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+
+        navigate { navState -> navState.filterNot { it.configuration.id == 3 } }
+
+        children.assertChildren(1 to null, 2 to 2)
+    }
+
+    @Test
+    fun WHEN_active_child_removed_THEN_lifecycle_destroyed() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        val component = children.getById(id = 3).requireInstance()
+
+        navigate { navState -> navState.filterNot { it.configuration.id == 3 } }
+
+        assertEquals(Lifecycle.State.DESTROYED, component.lifecycle.state)
+    }
+
+    @Test
+    fun WHEN_all_children_removed_THEN_state_updated() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+
+        navigate { emptyList() }
+
+        children.assertChildren()
+    }
+
+    @Test
+    fun WHEN_all_children_removed_THEN_lifecycles_destroyed() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        val component2 = children.getById(id = 2).requireInstance()
+        val component3 = children.getById(id = 3).requireInstance()
+
+        navigate { emptyList() }
+
+        assertEquals(Lifecycle.State.DESTROYED, component2.lifecycle.state)
+        assertEquals(Lifecycle.State.DESTROYED, component3.lifecycle.state)
+    }
+
+    @Test
+    fun WHEN_child_switched_from_destroyed_to_inactive_THEN_lifecycle_created() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+
+        navigate { listOf(1 by INACTIVE, 2 by INACTIVE, 3 by ACTIVE) }
+
+        assertEquals(Lifecycle.State.CREATED, children.getById(id = 1).requireInstance().lifecycle.state)
+    }
+
+    @Test
+    fun WHEN_child_switched_from_inactive_to_active_THEN_lifecycle_resumed() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+
+        navigate { listOf(1 by DESTROYED, 2 by ACTIVE, 3 by ACTIVE) }
+
+        assertEquals(Lifecycle.State.RESUMED, children.getById(id = 2).requireInstance().lifecycle.state)
+    }
+
+    @Test
+    fun WHEN_child_switched_from_inactive_to_destroyed_THEN_lifecycle_destroyed() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        val component = children.getById(id = 2).requireInstance()
+
+        navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by ACTIVE) }
+
+        assertEquals(Lifecycle.State.DESTROYED, component.lifecycle.state)
+    }
+
+    @Test
+    fun WHEN_child_switched_from_active_to_inactive_THEN_lifecycle_stopped() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+
+        navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by INACTIVE) }
+
+        assertEquals(Lifecycle.State.CREATED, children.getById(id = 3).requireInstance().lifecycle.state)
+    }
+
+    @Test
+    fun WHEN_child_switched_from_active_to_destroyed_THEN_lifecycle_destroyed() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        val component = children.getById(id = 3).requireInstance()
+
+        navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) }
+
+        assertEquals(Lifecycle.State.DESTROYED, component.lifecycle.state)
+    }
+
+    @Test
+    fun WHEN_context_lifecycle_stopped_THEN_all_component_lifecycles_stopped() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+
+        lifecycle.stop()
+
+        assertEquals(Lifecycle.State.CREATED, children.getById(id = 2).requireInstance().lifecycle.state)
+        assertEquals(Lifecycle.State.CREATED, children.getById(id = 3).requireInstance().lifecycle.state)
+    }
+
+    @Test
+    fun WHEN_context_lifecycle_destroyed_THEN_all_component_lifecycles_destroyed() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        val component2 = children.getById(id = 2).requireInstance()
+        val component3 = children.getById(id = 3).requireInstance()
+
+        lifecycle.destroy()
+
+        assertEquals(Lifecycle.State.DESTROYED, component2.lifecycle.state)
+        assertEquals(Lifecycle.State.DESTROYED, component3.lifecycle.state)
+    }
+}
diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenRetainedInstanceTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenRetainedInstanceTest.kt
new file mode 100644
index 00000000..7bf0789b
--- /dev/null
+++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenRetainedInstanceTest.kt
@@ -0,0 +1,158 @@
+package com.arkivanov.decompose.router.children
+
+import com.arkivanov.decompose.DefaultComponentContext
+import com.arkivanov.decompose.router.TestInstance
+import com.arkivanov.decompose.router.children.ChildNavState.Status.ACTIVE
+import com.arkivanov.decompose.router.children.ChildNavState.Status.DESTROYED
+import com.arkivanov.decompose.router.children.ChildNavState.Status.INACTIVE
+import com.arkivanov.decompose.statekeeper.TestStateKeeperDispatcher
+import com.arkivanov.decompose.value.getValue
+import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher
+import com.arkivanov.essenty.instancekeeper.getOrCreate
+import kotlin.test.Test
+import kotlin.test.assertFalse
+import kotlin.test.assertNotSame
+import kotlin.test.assertSame
+import kotlin.test.assertTrue
+
+@Suppress("TestFunctionName")
+internal class ChildrenRetainedInstanceTest : ChildrenTestBase() {
+
+    @Test
+    fun WHEN_child_switched_from_inactive_to_destroyed_THEN_instance_destroyed() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        val component = children.getById(id = 2).requireInstance()
+        val instance = component.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance)
+
+        navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by ACTIVE) }
+
+        assertTrue(instance.isDestroyed)
+    }
+
+    @Test
+    fun WHEN_child_switched_from_active_to_destroyed_THEN_instance_destroyed() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        val component = children.getById(id = 3).requireInstance()
+        val instance = component.instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance)
+
+        navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) }
+
+        assertTrue(instance.isDestroyed)
+    }
+
+    @Test
+    fun WHEN_child_switched_from_active_to_inactive_THEN_instance_not_destroyed() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        val instance = children.getById(id = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance)
+
+        navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by INACTIVE) }
+
+        assertFalse(instance.isDestroyed)
+    }
+
+    @Test
+    fun GIVEN_child_switched_from_inactive_to_destroyed_WHEN_child_switched_to_inactive_THEN_instance_not_retained() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        val oldInstance = children.getById(id = 2).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance)
+        navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by ACTIVE) }
+
+        navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE) }
+        val newInstance = children.getById(id = 2).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance)
+
+        assertNotSame(oldInstance, newInstance)
+    }
+
+    @Test
+    fun GIVEN_child_switched_from_active_to_destroyed_WHEN_child_switched_to_active_THEN_instance_not_retained() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        val oldInstance = children.getById(id = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance)
+        navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) }
+
+        navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE) }
+        val newInstance = children.getById(id = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance)
+
+        assertNotSame(oldInstance, newInstance)
+    }
+
+    @Test
+    fun WHEN_inactive_child_recreated_THEN_instance_retained() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val instanceKeeper = InstanceKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper)
+        val oldChildren by oldContext.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        val oldInstance = oldChildren.getById(id = 2).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance)
+
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper)
+        val newChildren by newContext.children()
+        val newInstance = newChildren.getById(id = 2).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance)
+
+        assertSame(oldInstance, newInstance)
+    }
+
+    @Test
+    fun WHEN_inactive_child_recreated_THEN_instance_not_destroyed() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val instanceKeeper = InstanceKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper)
+        val oldChildren by oldContext.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        val instance = oldChildren.getById(id = 2).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance)
+
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper)
+        newContext.children()
+
+        assertFalse(instance.isDestroyed)
+    }
+
+    @Test
+    fun WHEN_active_child_recreated_THEN_instance_retained() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val instanceKeeper = InstanceKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper)
+        val oldChildren by oldContext.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        val oldInstance = oldChildren.getById(id = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance)
+
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper)
+        val newChildren by newContext.children()
+        val newInstance = newChildren.getById(id = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance)
+
+        assertSame(oldInstance, newInstance)
+    }
+
+    @Test
+    fun WHEN_active_child_recreated_THEN_instance_not_destroyed() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val instanceKeeper = InstanceKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper)
+        val oldChildren by oldContext.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        val instance = oldChildren.getById(id = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance)
+
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper)
+        newContext.children()
+
+        assertFalse(instance.isDestroyed)
+    }
+
+    @Test
+    fun WHEN_recreated_and_child_restored_as_destroyed_THEN_instance_destroyed() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val instanceKeeper = InstanceKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper, instanceKeeper = instanceKeeper)
+        val oldChildren by oldContext.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        val instance = oldChildren.getById(id = 3).requireInstance().instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance)
+
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper, instanceKeeper = instanceKeeper)
+        newContext.children(restoreNavState = { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) })
+
+        assertTrue(instance.isDestroyed)
+    }
+}
diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenSavedStateTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenSavedStateTest.kt
new file mode 100644
index 00000000..d8af0ca1
--- /dev/null
+++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenSavedStateTest.kt
@@ -0,0 +1,201 @@
+package com.arkivanov.decompose.router.children
+
+import com.arkivanov.decompose.DefaultComponentContext
+import com.arkivanov.decompose.router.children.ChildNavState.Status.ACTIVE
+import com.arkivanov.decompose.router.children.ChildNavState.Status.DESTROYED
+import com.arkivanov.decompose.router.children.ChildNavState.Status.INACTIVE
+import com.arkivanov.decompose.statekeeper.ParcelableStub
+import com.arkivanov.decompose.statekeeper.TestStateKeeperDispatcher
+import com.arkivanov.decompose.value.getValue
+import com.arkivanov.essenty.parcelable.Parcelable
+import com.arkivanov.essenty.statekeeper.consume
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+@Suppress("TestFunctionName")
+internal class ChildrenSavedStateTest : ChildrenTestBase() {
+
+    @Test
+    fun WHEN_child_switched_from_active_to_inactive_THEN_state_saved() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        var isCalled = false
+        children.getById(id = 3).requireInstance().stateKeeper.register(key = "key") {
+            isCalled = true
+            ParcelableStub()
+        }
+
+        navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by INACTIVE) }
+
+        assertTrue(isCalled)
+    }
+
+    @Test
+    fun WHEN_child_switched_from_active_to_destroyed_THEN_state_saved() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        var isCalled = false
+        children.getById(id = 3).requireInstance().stateKeeper.register(key = "key") {
+            isCalled = true
+            ParcelableStub()
+        }
+
+        navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) }
+
+        assertTrue(isCalled)
+    }
+
+    @Test
+    fun WHEN_child_switched_from_inactive_to_destroyed_THEN_state_not_saved() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        var isCalled = false
+        children.getById(id = 2).requireInstance().stateKeeper.register(key = "key") {
+            isCalled = true
+            ParcelableStub()
+        }
+
+        navigate { listOf(1 by DESTROYED, 2 by DESTROYED, 3 by ACTIVE) }
+
+        assertFalse(isCalled)
+    }
+
+    @Test
+    fun WHEN_child_switched_from_inactive_to_active_THEN_state_not_saved() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        var isCalled = false
+        children.getById(id = 2).requireInstance().stateKeeper.register(key = "key") {
+            isCalled = true
+            ParcelableStub()
+        }
+
+        navigate { listOf(1 by DESTROYED, 2 by ACTIVE, 3 by ACTIVE) }
+
+        assertFalse(isCalled)
+    }
+
+    @Test
+    fun WHEN_child_switched_from_destroyed_to_inactive_THEN_state_not_saved() {
+        var isCalled = false
+        context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) { config, componentContext ->
+            val component = Component(config, componentContext)
+            if (config.id == 1) {
+                component.stateKeeper.register(key = "key") {
+                    isCalled = true
+                    ParcelableStub()
+                }
+            }
+            component
+        }
+
+        navigate { listOf(1 by INACTIVE, 2 by INACTIVE, 3 by ACTIVE) }
+
+        assertFalse(isCalled)
+    }
+
+    @Test
+    fun WHEN_child_switched_from_destroyed_to_active_THEN_state_not_saved() {
+        var isCalled = false
+        context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE)) { config, componentContext ->
+            val component = Component(config, componentContext)
+            if (config.id == 1) {
+                component.stateKeeper.register(key = "key") {
+                    isCalled = true
+                    ParcelableStub()
+                }
+            }
+            component
+        }
+
+        navigate { listOf(1 by ACTIVE, 2 by INACTIVE, 3 by ACTIVE) }
+
+        assertFalse(isCalled)
+    }
+
+    @Test
+    fun GIVEN_child_switched_from_active_to_destroyed_WHEN_child_switched_to_active_THEN_state_restored() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        children.getById(id = 3).requireInstance().stateKeeper.register(key = "key") { Config(id = 30) }
+        navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) }
+
+        navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE) }
+        val restoredState = children.getById(id = 3).requireInstance().stateKeeper.consume<Parcelable>(key = "key")
+
+        assertEquals(Config(id = 30), restoredState)
+    }
+
+    @Test
+    fun GIVEN_child_switched_from_active_to_destroyed_WHEN_child_switched_to_inactive_THEN_state_restored() {
+        val children by context.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        children.getById(id = 3).requireInstance().stateKeeper.register(key = "key") { Config(id = 30) }
+        navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) }
+
+        navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by INACTIVE) }
+        val restoredState = children.getById(id = 3).requireInstance().stateKeeper.consume<Parcelable>(key = "key")
+
+        assertEquals(Config(id = 30), restoredState)
+    }
+
+    @Test
+    fun WHEN_recreated_THEN_navigation_state_restored() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper)
+        oldContext.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper)
+        val newChildren by newContext.children()
+
+        newChildren.assertChildren(1 to null, 2 to 2, 3 to 3)
+    }
+
+    @Test
+    fun WHEN_recreated_THEN_child_states_restored() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper)
+        val oldChildren by oldContext.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        oldChildren.getById(id = 3).requireInstance().stateKeeper.register(key = "key") { Config(id = 30) }
+
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper)
+        val newChildren by newContext.children()
+        val restoredState3 = newChildren.getById(id = 3).requireInstance().stateKeeper.consume<Config>(key = "key")
+
+        assertEquals(Config(id = 30), restoredState3)
+    }
+
+    @Test
+    fun GIVEN_recreated_and_child_restored_as_destroyed_WHEN_child_switched_to_inactive_THEN_state_restored() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper)
+        val oldChildren by oldContext.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        oldChildren.getById(id = 3).requireInstance().stateKeeper.register(key = "key") { Config(id = 30) }
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper)
+        val newChildren by newContext.children(restoreNavState = { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) })
+
+        navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by INACTIVE) }
+        val restoredState3 = newChildren.getById(id = 3).requireInstance().stateKeeper.consume<Config>(key = "key")
+
+        assertEquals(Config(id = 30), restoredState3)
+    }
+
+    @Test
+    fun GIVEN_recreated_and_child_restored_as_destroyed_WHEN_child_switched_to_active_THEN_state_restored() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper)
+        val oldChildren by oldContext.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE))
+        oldChildren.getById(id = 3).requireInstance().stateKeeper.register(key = "key") { Config(id = 30) }
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper)
+        val newChildren by newContext.children(restoreNavState = { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by DESTROYED) })
+
+        navigate { listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE) }
+        val restoredState3 = newChildren.getById(id = 3).requireInstance().stateKeeper.consume<Config>(key = "key")
+
+        assertEquals(Config(id = 30), restoredState3)
+    }
+}
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
new file mode 100644
index 00000000..4ec37549
--- /dev/null
+++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenTestBase.kt
@@ -0,0 +1,158 @@
+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
+import com.arkivanov.essenty.backhandler.BackDispatcher
+import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher
+import com.arkivanov.essenty.lifecycle.LifecycleRegistry
+import com.arkivanov.essenty.lifecycle.resume
+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 kotlin.test.BeforeTest
+import kotlin.test.assertContentEquals
+
+@OptIn(ExperimentalDecomposeApi::class)
+internal open class ChildrenTestBase {
+
+    private val navigation = TestNavigation()
+    protected val lifecycle = LifecycleRegistry()
+    protected val backDispatcher = BackDispatcher()
+
+    protected val context =
+        DefaultComponentContext(
+            lifecycle = lifecycle,
+            stateKeeper = TestStateKeeperDispatcher(),
+            instanceKeeper = InstanceKeeperDispatcher(),
+            backHandler = backDispatcher,
+        )
+
+    @BeforeTest
+    fun before() {
+        lifecycle.resume()
+    }
+
+    protected fun ComponentContext.children(
+        initialNavState: List<SimpleChildNavState<Config>> = emptyList(),
+        restoreNavState: (container: ParcelableContainer) -> List<SimpleChildNavState<Config>> = { container ->
+            val savedState = container.consumeRequired<SavedNavState>()
+            savedState.configurations.zip(savedState.statuses).map { (configuration, status) ->
+                SimpleChildNavState(
+                    configuration = configuration,
+                    status = status,
+                )
+            }
+        },
+        backTransformer: (navState: List<SimpleChildNavState<Config>>) -> (() -> List<SimpleChildNavState<Config>>)? = { null },
+        childFactory: (Config, ComponentContext) -> Component = ::Component,
+    ): Value<List<Child<Config, Component>>> =
+        children(
+            source = navigation,
+            key = "Key",
+            initialNavState = { TestNavState(children = initialNavState) },
+            saveNavState = { navState ->
+                ParcelableContainer(
+                    SavedNavState(
+                        configurations = navState.children.map { it.configuration },
+                        statuses = navState.children.map { it.status },
+                    )
+                )
+            },
+            restoreNavState = { container -> TestNavState(children = restoreNavState(container)) },
+            navTransformer = { navState, event -> event.transformer(navState) },
+            onEventComplete = { event, newNavState, oldNavState -> event.onComplete(newNavState, oldNavState) },
+            backTransformer = {
+                backTransformer(it.children)?.let { transformer ->
+                    { TestNavState(children = transformer()) }
+                }
+            },
+            stateMapper = { _, children -> children },
+            childFactory = childFactory,
+        )
+
+    protected fun navigate(transformer: (List<SimpleChildNavState<Config>>) -> List<SimpleChildNavState<Config>>) {
+        navigate(transformer = transformer, onComplete = null)
+    }
+
+    protected fun navigate(
+        transformer: (List<SimpleChildNavState<Config>>) -> List<SimpleChildNavState<Config>>,
+        onComplete: ((newNavState: List<SimpleChildNavState<Config>>, oldNavState: List<SimpleChildNavState<Config>>) -> Unit)?,
+    ) {
+        navigation.navigate(
+            Event(
+                transformer = { TestNavState(children = transformer(it.children)) },
+                onComplete = { newNavState, oldNavState -> onComplete?.invoke(newNavState.children, oldNavState.children) },
+            )
+        )
+    }
+
+    protected infix fun Int.by(status: ChildNavState.Status): SimpleChildNavState<Config> =
+        SimpleChildNavState(configuration = Config(id = this), status = status)
+
+    protected fun List<Child<Config, Component>>.assertChildren(vararg children: Pair<Int, Int?>) {
+        assertContentEquals(
+            children.toList(),
+            map { child ->
+                when (child) {
+                    is Child.Created -> child.configuration.id to child.instance.id
+                    is Child.Destroyed -> child.configuration.id to null
+                }
+            }
+        )
+    }
+
+    protected fun List<Child<Config, Component>>.getById(id: Int): Child<Config, Component> =
+        first { it.configuration.id == id }
+
+    protected fun Child<*, Component>.requireInstance(): Component =
+        requireNotNull(instance)
+
+    protected data class TestNavState(
+        override val children: List<SimpleChildNavState<Config>> = emptyList(),
+    ) : NavState<Config>
+
+    @Parcelize
+    protected data class Config(
+        val id: Int,
+    ) : Parcelable
+
+    protected class Component(
+        config: Config,
+        componentContext: ComponentContext,
+    ) : ComponentContext by componentContext {
+        val id: Int = config.id
+    }
+
+    @Parcelize
+    protected class SavedNavState(
+        val configurations: List<Config>,
+        val statuses: List<ChildNavState.Status>,
+    ) : Parcelable
+
+    protected class Event(
+        val transformer: (TestNavState) -> TestNavState,
+        val onComplete: (newNavState: TestNavState, oldNavState: TestNavState) -> Unit,
+    )
+
+    protected class TestNavigation : NavigationSource<Event> {
+        private val relay = Relay<Event>()
+
+        override fun subscribe(observer: (Event) -> Unit) {
+            relay.subscribe(observer)
+        }
+
+        override fun unsubscribe(observer: (Event) -> Unit) {
+            relay.unsubscribe(observer)
+        }
+
+        fun navigate(event: Event) {
+            relay.accept(event)
+        }
+    }
+}
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>>
-    )
-}