Skip to content

Commit

Permalink
Disposing solution from #427 to fix animation not ended dispose bug
Browse files Browse the repository at this point in the history
  • Loading branch information
DevSrSouza committed Jun 4, 2024
1 parent 1a6b6ce commit 9510523
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import cafe.adriel.voyager.core.model.ScreenModel
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.core.screen.ScreenKey
import cafe.adriel.voyager.transitions.ScreenTransition
Expand All @@ -27,6 +30,18 @@ private val colors = listOf(
Color.Black
)

class BaseSampleScreenModel(
val index: Int
) : ScreenModel {

init {
println("Init BaseSampleScreenModel $index")
}
override fun onDispose() {
println("Disposing BaseSampleScreenModel $index")
}
}

abstract class BaseSampleScreen(
private val transitionType: String
) : Screen {
Expand All @@ -37,10 +52,16 @@ abstract class BaseSampleScreen(

@Composable
override fun Content() {
val model = rememberScreenModel {
BaseSampleScreenModel(index)
}
val color = remember {
colors.getOrNull(index % colors.size) ?: colors.random()
}
Column(
modifier = Modifier
.fillMaxSize()
.background(colors[index % colors.size].copy(alpha = 0.3f))
.background(color.copy(alpha = 0.3f))
.padding(40.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
Expand All @@ -16,7 +17,9 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.NavigatorDisposeBehavior
import cafe.adriel.voyager.transitions.ScreenTransition
import kotlin.random.Random

class ScreenTransitionActivity : ComponentActivity() {

Expand All @@ -31,40 +34,67 @@ class ScreenTransitionActivity : ComponentActivity() {
@Composable
fun Content() {
Navigator(
screen = NoCustomAnimationSampleScreen(0)
screen = NoCustomAnimationSampleScreen(0),
disposeBehavior = NavigatorDisposeBehavior(disposeSteps = false)
) { navigator ->
Box(modifier = Modifier.fillMaxSize()) {
ScreenTransition(
navigator = navigator,
defaultTransition = SlideTransition()
defaultTransition = SlideTransition(),
disposeScreenAfterTransitionEnd = true,
)

Row(
Column(
modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth()
.padding(40.dp),
horizontalArrangement = Arrangement.spacedBy(20.dp)
) {
Button(
onClick = { navigator.push(FadeAnimationSampleScreen(navigator.items.size)) },
modifier = Modifier.weight(1f)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
horizontalArrangement = Arrangement.spacedBy(20.dp)
) {
Text(text = "Fade")
}
Button(
onClick = { navigator.push(FadeAnimationSampleScreen(navigator.items.size)) },
modifier = Modifier.weight(1f)
) {
Text(text = "Fade")
}

Button(
onClick = { navigator.push(NoCustomAnimationSampleScreen(navigator.items.size)) },
modifier = Modifier.weight(1f)
) {
Text(text = "Default")
}
Button(
onClick = { navigator.push(NoCustomAnimationSampleScreen(navigator.items.size)) },
modifier = Modifier.weight(1f)
) {
Text(text = "Default")
}

Button(
onClick = { navigator.pop() },
modifier = Modifier.weight(1f)
Button(
onClick = { navigator.pop() },
modifier = Modifier.weight(1f)
) {
Text(text = "Pop")
}
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
horizontalArrangement = Arrangement.spacedBy(20.dp)
) {
Text(text = "Pop")
Button(
onClick = { navigator.replace(NoCustomAnimationSampleScreen(Random.nextInt())) },
modifier = Modifier.weight(1f)
) {
Text(text = "Replace")
}

Button(
onClick = { navigator.replaceAll(NoCustomAnimationSampleScreen(Random.nextInt())) },
modifier = Modifier.weight(1f)
) {
Text(text = "ReplaceAll")
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@ import androidx.compose.animation.ExitTransition
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.togetherWith
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Modifier
import cafe.adriel.voyager.core.annotation.ExperimentalVoyagerApi
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.core.screen.ScreenKey
import cafe.adriel.voyager.core.stack.StackEvent
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.internal.disposableEvents
import cafe.adriel.voyager.transitions.internal.rememberPrevious

@ExperimentalVoyagerApi
public interface ScreenTransition {
Expand Down Expand Up @@ -129,9 +133,20 @@ public fun ScreenTransition(
disposeScreenAfterTransitionEnd: Boolean = false,
content: ScreenTransitionContent = { it.Content() }
) {
// This can be costly because is checking every single item in the list
// we should re evaluate how validation works, maybe validating screen keys or ===
val previousItems = rememberPrevious(navigator.items)
val screenCandidatesToDispose = rememberSaveable(saver = screenCandidatesToDisposeSaver()) {
mutableStateOf(emptySet())
}

val currentScreens = navigator.items

DisposableEffect(currentScreens) {
onDispose {
val newScreenKeys = navigator.items.map { it.key }
screenCandidatesToDispose.value += currentScreens.filter { it.key !in newScreenKeys }
.map { ScreenData(it.key, it) }
}
}

AnimatedContent(
targetState = navigator.lastItem,
transitionSpec = {
Expand All @@ -155,15 +170,13 @@ public fun ScreenTransition(
if (this.transition.targetState == this.transition.currentState) {
LaunchedEffect(Unit) {
if (disposeScreenAfterTransitionEnd) {
// if disposeSteps = true, lastEvent will be always idle
// else it will keep the event and we can dispose our self.
if (navigator.lastEvent in disposableEvents) {
val newScreenKeys = navigator.items.map { it.key }
previousItems?.filter { it.key !in newScreenKeys }?.forEach {
navigator.dispose(it)
}
val newScreens = navigator.items.map { it.key }
val screensToDispose = screenCandidatesToDispose.value.filterNot { it.key in newScreens }
if (screensToDispose.isNotEmpty()) {
screensToDispose.forEach { navigator.dispose(it.screen) }
navigator.clearEvent()
}
screenCandidatesToDispose.value = emptySet()
}
}
}
Expand All @@ -173,3 +186,15 @@ public fun ScreenTransition(
}
}
}

private data class ScreenData(
val key: ScreenKey,
val screen: Screen
)

private fun screenCandidatesToDisposeSaver(): Saver<MutableState<Set<ScreenData>>, List<ScreenData>> {
return Saver(
save = { it.value.toList() },
restore = { mutableStateOf(it.toSet()) }
)
}

This file was deleted.

0 comments on commit 9510523

Please sign in to comment.