diff --git a/CHANGELOG.md b/CHANGELOG.md index 58e713769..4fff6bc45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## Pending changes -– +- [#375](https://github.com/bumble-tech/appyx/issues/375) – **Fixed**: SaveableStateHolder does no longer save state for destroyed elements --- diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4cac0dc55..52060ae2a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -77,6 +77,7 @@ plugin-detekt = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", v plugin-android = "com.android.tools.build:gradle:7.3.1" detekt-compose = "com.twitter.compose.rules:detekt:0.0.26" +toolargetool = "com.gu.android:toolargetool:0.3.0" [plugins] detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } diff --git a/libraries/core/src/main/kotlin/com/bumble/appyx/core/composable/Child.kt b/libraries/core/src/main/kotlin/com/bumble/appyx/core/composable/Child.kt index 39f9ff9ca..da743771f 100644 --- a/libraries/core/src/main/kotlin/com/bumble/appyx/core/composable/Child.kt +++ b/libraries/core/src/main/kotlin/com/bumble/appyx/core/composable/Child.kt @@ -19,8 +19,6 @@ import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntSize -import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.node.ParentNode import com.bumble.appyx.core.navigation.NavElement import com.bumble.appyx.core.navigation.NavElements import com.bumble.appyx.core.navigation.NavModel @@ -29,6 +27,8 @@ import com.bumble.appyx.core.navigation.transition.TransitionBounds import com.bumble.appyx.core.navigation.transition.TransitionDescriptor import com.bumble.appyx.core.navigation.transition.TransitionHandler import com.bumble.appyx.core.navigation.transition.TransitionParams +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.node.ParentNode import kotlinx.coroutines.flow.map @Composable diff --git a/libraries/core/src/main/kotlin/com/bumble/appyx/core/composable/Children.kt b/libraries/core/src/main/kotlin/com/bumble/appyx/core/composable/Children.kt index 55937df26..9b27ff37e 100644 --- a/libraries/core/src/main/kotlin/com/bumble/appyx/core/composable/Children.kt +++ b/libraries/core/src/main/kotlin/com/bumble/appyx/core/composable/Children.kt @@ -2,6 +2,7 @@ package com.bumble.appyx.core.composable import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue @@ -15,13 +16,14 @@ import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntSize -import com.bumble.appyx.core.node.ParentNode import com.bumble.appyx.core.navigation.NavModel +import com.bumble.appyx.core.navigation.removedElementKeys import com.bumble.appyx.core.navigation.transition.JumpToEndTransitionHandler import com.bumble.appyx.core.navigation.transition.TransitionBounds import com.bumble.appyx.core.navigation.transition.TransitionDescriptor import com.bumble.appyx.core.navigation.transition.TransitionHandler import com.bumble.appyx.core.navigation.transition.TransitionParams +import com.bumble.appyx.core.node.ParentNode import kotlinx.coroutines.flow.map import kotlin.reflect.KClass @@ -123,6 +125,21 @@ class ChildrenTransitionScope( transitionDescriptor: TransitionDescriptor ) -> Unit ) { + val saveableStateHolder = rememberSaveableStateHolder() + + LaunchedEffect(navModel) { + navModel + .removedElementKeys() + .map { list -> + list.filter { clazz.isInstance(it.navTarget) } + } + .collect { deletedKeys -> + deletedKeys.forEach { navKey -> + saveableStateHolder.removeState(navKey) + } + } + } + val visibleElementsFlow = remember { this@ChildrenTransitionScope .navModel @@ -135,7 +152,7 @@ class ChildrenTransitionScope( } val children by visibleElementsFlow.collectAsState(emptyList()) - val saveableStateHolder = rememberSaveableStateHolder() + children.forEach { navElement -> key(navElement.key.id) { Child( diff --git a/libraries/core/src/main/kotlin/com/bumble/appyx/core/navigation/NavModelExt.kt b/libraries/core/src/main/kotlin/com/bumble/appyx/core/navigation/NavModelExt.kt new file mode 100644 index 000000000..8acae1a16 --- /dev/null +++ b/libraries/core/src/main/kotlin/com/bumble/appyx/core/navigation/NavModelExt.kt @@ -0,0 +1,16 @@ +package com.bumble.appyx.core.navigation + +import com.bumble.appyx.core.withPrevious +import kotlinx.coroutines.flow.map + +internal fun NavModel.removedElementKeys() = + this + .elements + .withPrevious() + .map { values -> + val previousKeys = values.previous?.map { it.key }.orEmpty() + val currentKeys = values.current.map { it.key } + previousKeys.filter { element -> + !currentKeys.contains(element) + } + } diff --git a/samples/sandbox/build.gradle.kts b/samples/sandbox/build.gradle.kts index b8aad851b..5fe2421ee 100644 --- a/samples/sandbox/build.gradle.kts +++ b/samples/sandbox/build.gradle.kts @@ -74,6 +74,7 @@ dependencies { implementation(libs.rxjava2) implementation(libs.rxandroid2) implementation(libs.rxrelay2) + implementation(libs.toolargetool) testImplementation(libs.androidx.arch.core.testing) testImplementation(libs.junit) diff --git a/samples/sandbox/src/main/AndroidManifest.xml b/samples/sandbox/src/main/AndroidManifest.xml index 392941e19..22ef3aa96 100644 --- a/samples/sandbox/src/main/AndroidManifest.xml +++ b/samples/sandbox/src/main/AndroidManifest.xml @@ -9,6 +9,7 @@ android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" + android:name=".sandbox.SandboxApplication" android:supportsRtl="true" android:theme="@style/Theme.Appyx">