Repository: arkivanov/Decompose
Pull request #294 (2023-01-02 22:45:04)

Added persistent argument to Child Stack


Patch:
diff --git a/decompose/api/android/decompose.api b/decompose/api/android/decompose.api
index 0cbc4876..976f37d3 100644
--- a/decompose/api/android/decompose.api
+++ b/decompose/api/android/decompose.api
@@ -195,8 +195,8 @@ public final class com/arkivanov/decompose/router/stack/ChildStack {
 }
 
 public final class com/arkivanov/decompose/router/stack/ChildStackFactoryKt {
-	public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/reflect/KClass;Ljava/lang/String;IZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value;
-	public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/reflect/KClass;Ljava/lang/String;IZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value;
+	public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/reflect/KClass;Ljava/lang/String;ZIZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value;
+	public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/reflect/KClass;Ljava/lang/String;ZIZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value;
 }
 
 public abstract interface class com/arkivanov/decompose/router/stack/StackNavigation : com/arkivanov/decompose/router/stack/StackNavigationSource, com/arkivanov/decompose/router/stack/StackNavigator {
diff --git a/decompose/api/jvm/decompose.api b/decompose/api/jvm/decompose.api
index 07104d11..7feeac7e 100644
--- a/decompose/api/jvm/decompose.api
+++ b/decompose/api/jvm/decompose.api
@@ -174,8 +174,8 @@ public final class com/arkivanov/decompose/router/stack/ChildStack {
 }
 
 public final class com/arkivanov/decompose/router/stack/ChildStackFactoryKt {
-	public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/reflect/KClass;Ljava/lang/String;IZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value;
-	public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/reflect/KClass;Ljava/lang/String;IZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value;
+	public static final fun childStack (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/reflect/KClass;Ljava/lang/String;ZIZLkotlin/jvm/functions/Function2;)Lcom/arkivanov/decompose/value/Value;
+	public static synthetic fun childStack$default (Lcom/arkivanov/decompose/ComponentContext;Lcom/arkivanov/decompose/router/stack/StackNavigationSource;Lkotlin/jvm/functions/Function0;Lkotlin/reflect/KClass;Ljava/lang/String;ZIZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/arkivanov/decompose/value/Value;
 }
 
 public abstract interface class com/arkivanov/decompose/router/stack/StackNavigation : com/arkivanov/decompose/router/stack/StackNavigationSource, com/arkivanov/decompose/router/stack/StackNavigator {
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt
index 6bcf563b..bc686be0 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/children/ChildrenFactory.kt
@@ -60,7 +60,7 @@ fun <C : Any, T : Any, E : Any, N : NavState<C>, S : Any> ComponentContext.child
     source: NavigationSource<E>,
     key: String,
     initialNavState: () -> N,
-    saveNavState: (navState: N) -> ParcelableContainer,
+    saveNavState: (navState: N) -> ParcelableContainer?,
     restoreNavState: (container: ParcelableContainer) -> N,
     navTransformer: (navState: N, event: E) -> N,
     onEventComplete: (event: E, newNavState: N, oldNavState: N) -> Unit,
@@ -86,10 +86,12 @@ fun <C : Any, T : Any, E : Any, N : NavState<C>, S : Any> ComponentContext.child
         )
 
     stateKeeper.register(key = key) {
-        SavedState(
-            navState = saveNavState(navigator.navState),
-            childState = navigator.saveChildState(),
-        )
+        saveNavState(navigator.navState)?.let { savedNavState ->
+            SavedState(
+                navState = savedNavState,
+                childState = navigator.saveChildState(),
+            )
+        }
     }
 
     val state = MutableValue(stateMapper(navigator.navState, navigator.children))
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 85a075d0..0d2a59a0 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
@@ -41,7 +41,7 @@ fun <C : Parcelable, T : Any> ComponentContext.childOverlay(
         source = source,
         key = key,
         initialNavState = { OverlayNavState(configuration = initialConfiguration()) },
-        saveNavState = { navState -> ParcelableContainer(navState.configuration?.takeIf { persistent }) },
+        saveNavState = { navState -> if (persistent) ParcelableContainer(navState.configuration) else null },
         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) },
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 5e54fb11..9699d09c 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
@@ -24,6 +24,8 @@ import kotlin.reflect.KClass
  * if there is no saved state, must be not empty and unique.
  * @param configurationClass a [KClass] of the component configurations.
  * @param key a key of the stack, must be unique if there are multiple stacks in the same component.
+ * @param persistent determines whether the navigation state should pre preserved or not,
+ * default is `true`.
  * @param backStackCreateDepth automatically creates the specified amount of components
  * in the back stack, e.g. when initialized, during navigation or after process death.
  * Default value is `0`, which means that a component is only created when it becomes active.
@@ -40,6 +42,7 @@ fun <C : Parcelable, T : Any> ComponentContext.childStack(
     initialStack: () -> List<C>,
     configurationClass: KClass<out C>,
     key: String = "DefaultChildStack",
+    persistent: Boolean = true,
     backStackCreateDepth: Int = 0,
     handleBackButton: Boolean = false,
     childFactory: (configuration: C, ComponentContext) -> T,
@@ -60,12 +63,16 @@ fun <C : Parcelable, T : Any> ComponentContext.childStack(
             )
         },
         saveNavState = { navState ->
-            ParcelableContainer(
-                StackSavedNavState(
-                    activeConfiguration = ParcelableContainer(navState.active),
-                    backStackConfigurations = navState.backStack.map { ParcelableContainer(it.configuration) },
+            if (persistent) {
+                ParcelableContainer(
+                    StackSavedNavState(
+                        activeConfiguration = ParcelableContainer(navState.active),
+                        backStackConfigurations = navState.backStack.map { ParcelableContainer(it.configuration) },
+                    )
                 )
-            )
+            } else {
+                null
+            }
         },
         restoreNavState = { container ->
             val state = container.consumeRequired<StackSavedNavState>()
@@ -187,6 +194,7 @@ inline fun <reified C : Parcelable, T : Any> ComponentContext.childStack(
     source: StackNavigationSource<C>,
     noinline initialStack: () -> List<C>,
     key: String = "DefaultChildStack",
+    persistent: Boolean = true,
     backStackCreateDepth: Int = 0,
     handleBackButton: Boolean = false,
     noinline childFactory: (configuration: C, ComponentContext) -> T
@@ -196,6 +204,7 @@ inline fun <reified C : Parcelable, T : Any> ComponentContext.childStack(
         initialStack = initialStack,
         configurationClass = C::class,
         key = key,
+        persistent = persistent,
         handleBackButton = handleBackButton,
         backStackCreateDepth = backStackCreateDepth,
         childFactory = childFactory,
@@ -208,6 +217,7 @@ inline fun <reified C : Parcelable, T : Any> ComponentContext.childStack(
     source: StackNavigationSource<C>,
     initialConfiguration: C,
     key: String = "DefaultChildStack",
+    persistent: Boolean = true,
     backStackCreateDepth: Int = 0,
     handleBackButton: Boolean = false,
     noinline childFactory: (configuration: C, ComponentContext) -> T
@@ -217,6 +227,7 @@ inline fun <reified C : Parcelable, T : Any> ComponentContext.childStack(
         initialStack = { listOf(initialConfiguration) },
         configurationClass = C::class,
         key = key,
+        persistent = persistent,
         handleBackButton = handleBackButton,
         backStackCreateDepth = backStackCreateDepth,
         childFactory = childFactory,
diff --git a/docs/navigation/stack/overview.md b/docs/navigation/stack/overview.md
index da30a966..2fc60aa2 100644
--- a/docs/navigation/stack/overview.md
+++ b/docs/navigation/stack/overview.md
@@ -138,10 +138,12 @@ class DefaultRootComponent(
 
 Use the `backStackCreateDepth` argument to control how many components in the back stack are automatically created. E.g. use `1` to always create the last component in the back stack (in addition to the active component which is always created), use `Int.MAX_VALUE` to create all components in the back stack. Default value is `0`, which means that a component is only created when it becomes active. The automatic creation of components in the back stack may be useful with swipe-back navigation, e.g. in SwiftUI.
 
-## Configuration changes and process death on (not only) Android
+## Screen recreation and process death on (not only) Android
 
 `Child Stack` automatically preserves the stack when a configuration change or process death occurs. By default, only the active component is recreated, components in the back stack remain destroyed and are created on demand.
 
+Use the `persistent` argument to disable stack preservation completely. When disabled, the stack is reset to the initial state when a configuration change or process death occurs.
+
 ## Delivering a result when navigating back
 
 To deliver a result from one component to another:


Test patch:
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
index d8af0ca1..334a0734 100644
--- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenSavedStateTest.kt
+++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenSavedStateTest.kt
@@ -12,6 +12,7 @@ import com.arkivanov.essenty.statekeeper.consume
 import kotlin.test.Test
 import kotlin.test.assertEquals
 import kotlin.test.assertFalse
+import kotlin.test.assertNull
 import kotlin.test.assertTrue
 
 @Suppress("TestFunctionName")
@@ -136,7 +137,7 @@ internal class ChildrenSavedStateTest : ChildrenTestBase() {
     }
 
     @Test
-    fun WHEN_recreated_THEN_navigation_state_restored() {
+    fun GIVEN_saving_state_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))
@@ -150,7 +151,21 @@ internal class ChildrenSavedStateTest : ChildrenTestBase() {
     }
 
     @Test
-    fun WHEN_recreated_THEN_child_states_restored() {
+    fun GIVEN_not_saving_state_WHEN_recreated_THEN_navigation_state_not_restored() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper)
+        oldContext.children(initialNavState = listOf(1 by DESTROYED, 2 by INACTIVE, 3 by ACTIVE), saveNavState = { null })
+
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper)
+        val newChildren by newContext.children(initialNavState = listOf(1 by ACTIVE))
+
+        newChildren.assertChildren(1 to 1)
+    }
+
+    @Test
+    fun GIVEN_saving_state_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))
@@ -165,6 +180,22 @@ internal class ChildrenSavedStateTest : ChildrenTestBase() {
         assertEquals(Config(id = 30), restoredState3)
     }
 
+    @Test
+    fun GIVEN_not_saving_state_WHEN_recreated_THEN_child_states_not_restored() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper)
+        val oldChildren by oldContext.children(initialNavState = listOf(1 by ACTIVE), saveNavState = { null })
+        oldChildren.getById(id = 1).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(initialNavState = listOf(1 by ACTIVE))
+        val restoredState3 = newChildren.getById(id = 1).requireInstance().stateKeeper.consume<Config>(key = "key")
+
+        assertNull(restoredState3)
+    }
+
     @Test
     fun GIVEN_recreated_and_child_restored_as_destroyed_WHEN_child_switched_to_inactive_THEN_state_restored() {
         val oldStateKeeper = TestStateKeeperDispatcher()
diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenTestBase.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenTestBase.kt
index 4ec37549..16341d4f 100644
--- a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenTestBase.kt
+++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/children/ChildrenTestBase.kt
@@ -40,6 +40,14 @@ internal open class ChildrenTestBase {
 
     protected fun ComponentContext.children(
         initialNavState: List<SimpleChildNavState<Config>> = emptyList(),
+        saveNavState: (navState: List<SimpleChildNavState<Config>>) -> ParcelableContainer? = { navState ->
+            ParcelableContainer(
+                SavedNavState(
+                    configurations = navState.map { it.configuration },
+                    statuses = navState.map { it.status },
+                )
+            )
+        },
         restoreNavState: (container: ParcelableContainer) -> List<SimpleChildNavState<Config>> = { container ->
             val savedState = container.consumeRequired<SavedNavState>()
             savedState.configurations.zip(savedState.statuses).map { (configuration, status) ->
@@ -56,14 +64,7 @@ internal open class ChildrenTestBase {
             source = navigation,
             key = "Key",
             initialNavState = { TestNavState(children = initialNavState) },
-            saveNavState = { navState ->
-                ParcelableContainer(
-                    SavedNavState(
-                        configurations = navState.children.map { it.configuration },
-                        statuses = navState.children.map { it.status },
-                    )
-                )
-            },
+            saveNavState = { navState -> saveNavState(navState.children) },
             restoreNavState = { container -> TestNavState(children = restoreNavState(container)) },
             navTransformer = { navState, event -> event.transformer(navState) },
             onEventComplete = { event, newNavState, oldNavState -> event.onComplete(newNavState, oldNavState) },
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 3fbf902e..5dccc8e2 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
@@ -21,6 +21,7 @@ import kotlin.test.Test
 import kotlin.test.assertEquals
 import kotlin.test.assertFalse
 import kotlin.test.assertNotNull
+import kotlin.test.assertNotSame
 import kotlin.test.assertSame
 import kotlin.test.assertTrue
 
@@ -247,6 +248,23 @@ class ChildOverlayIntegrationTest {
         assertNotNull(instanceKeeperDispatcher.get(key = "key"))
     }
 
+    @Test
+    fun GIVEN_not_persistent_WHEN_recreated_THEN_overlay_instance_not_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 = false)
+        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 = Config(1), persistent = false)
+        val newInstance = newOverlay.requireOverlay().instance.instanceKeeper.getOrCreate(::TestInstance)
+
+        assertNotSame(oldInstance, newInstance)
+    }
+
     @Test
     fun GIVEN_not_persistent_WHEN_recreated_THEN_overlay_instance_destroyed() {
         val oldStateKeeper = TestStateKeeperDispatcher()
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 c7ee1e34..4dd77cc7 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
@@ -21,6 +21,8 @@ import kotlin.test.BeforeTest
 import kotlin.test.Test
 import kotlin.test.assertEquals
 import kotlin.test.assertFalse
+import kotlin.test.assertNotSame
+import kotlin.test.assertNull
 import kotlin.test.assertSame
 import kotlin.test.assertTrue
 
@@ -211,10 +213,10 @@ class ChildStackIntegrationTest {
     }
 
     @Test
-    fun WHEN_recreated_THEN_stack_restored() {
+    fun GIVEN_persistent_WHEN_recreated_THEN_stack_restored() {
         val oldStateKeeper = TestStateKeeperDispatcher()
         val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper)
-        oldContext.childStack(initialStack = listOf(Config(1), Config(2)))
+        oldContext.childStack(initialStack = listOf(Config(1), Config(2)), persistent = true)
 
         val savedState = oldStateKeeper.save()
         val newStateKeeper = TestStateKeeperDispatcher(savedState)
@@ -224,6 +226,20 @@ class ChildStackIntegrationTest {
         newStack.assertStack(1 to null, 2 to 2)
     }
 
+    @Test
+    fun GIVEN_not_persistent_WHEN_recreated_THEN_stack_not_restored() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper)
+        oldContext.childStack(initialStack = listOf(Config(1), Config(2)), persistent = false)
+
+        val savedState = oldStateKeeper.save()
+        val newStateKeeper = TestStateKeeperDispatcher(savedState)
+        val newContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = newStateKeeper)
+        val newStack by newContext.childStack(initialStack = listOf(Config(1)))
+
+        newStack.assertStack(1 to 1)
+    }
+
     @Test
     fun WHEN_child_pushed_THEN_state_saved_for_previous_child() {
         val stack by context.childStack(initialStack = listOf(Config(1)))
@@ -239,10 +255,10 @@ class ChildStackIntegrationTest {
     }
 
     @Test
-    fun WHEN_recreated_THEN_state_restored_for_active_child() {
+    fun GIVEN_persistent_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)))
+        val oldStack by oldContext.childStack(initialStack = listOf(Config(1)), persistent = true)
         oldStack.active.instance.stateKeeper.register(key = "key") { Config(10) }
 
         val savedState = oldStateKeeper.save()
@@ -254,6 +270,22 @@ class ChildStackIntegrationTest {
         assertEquals(Config(10), restoredState)
     }
 
+    @Test
+    fun GIVEN_not_persistent_WHEN_recreated_THEN_state_not_restored_for_active_child() {
+        val oldStateKeeper = TestStateKeeperDispatcher()
+        val oldContext = DefaultComponentContext(lifecycle = lifecycle, stateKeeper = oldStateKeeper)
+        val oldStack by oldContext.childStack(initialStack = listOf(Config(1)), persistent = false)
+        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 = listOf(Config(1)))
+        val restoredState = newStack.active.instance.stateKeeper.consume<Config>(key = "key")
+
+        assertNull(restoredState)
+    }
+
     @Test
     fun GIVEN_back_stack_not_empty_and_recreated_WHEN_pop_THEN_state_restored_for_previous_child() {
         val oldStateKeeper = TestStateKeeperDispatcher()
@@ -273,11 +305,11 @@ class ChildStackIntegrationTest {
     }
 
     @Test
-    fun WHEN_recreated_THEN_instance_retained_for_active_component() {
+    fun GIVEN_persistent_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 oldStack by oldContext.childStack(initialStack = listOf(Config(1)), persistent = true)
         val oldInstance = oldStack.active.instance.instanceKeeper.getOrCreate(::TestInstance)
 
         val savedState = oldStateKeeper.save()
@@ -290,11 +322,28 @@ class ChildStackIntegrationTest {
     }
 
     @Test
-    fun WHEN_recreated_THEN_instance_not_destroyed_for_active_component() {
+    fun GIVEN_not_persistent_WHEN_recreated_THEN_instance_not_retained_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 oldStack by oldContext.childStack(initialStack = listOf(Config(1)), persistent = false)
+        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 = listOf(Config(1)))
+        val newInstance = newStack.active.instance.instanceKeeper.getOrCreate(::TestInstance)
+
+        assertNotSame(oldInstance, newInstance)
+    }
+
+    @Test
+    fun GIVEN_persistent_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)), persistent = true)
         val instance = stack.active.instance.instanceKeeper.getOrCreate(::TestInstance)
 
         val savedState = oldStateKeeper.save()
@@ -305,6 +354,22 @@ class ChildStackIntegrationTest {
         assertFalse(instance.isDestroyed)
     }
 
+    @Test
+    fun GIVEN_not_persistent_WHEN_recreated_THEN_instance_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)), persistent = false)
+        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 = listOf(Config(1)))
+
+        assertTrue(instance.isDestroyed)
+    }
+
     @Test
     fun WHEN_another_child_pushed_THEN_instance_not_destroyed_for_previous_child() {
         val stack by context.childStack(initialStack = listOf(Config(1)))
@@ -458,11 +523,13 @@ class ChildStackIntegrationTest {
 
     private fun ComponentContext.childStack(
         initialStack: List<Config>,
+        persistent: Boolean = true,
         backStackCreateDepth: Int = 0,
     ): Value<ChildStack<Config, Component>> =
         childStack(
             source = navigation,
             initialStack = { initialStack },
+            persistent = persistent,
             backStackCreateDepth = backStackCreateDepth,
             handleBackButton = true,
             childFactory = ::Component,