Chuck Stein
08/19/2021, 5:19 PMremember
but instead of recalculating if dependencies change, it recalculates if a given predicate is true?
My use case is I have some UI content within an AnimatedVisibility
block, where visible = uiState is InitialUiState
. (My UI state is a sealed class with subtypes we can call InitialUiState
and SecondaryUiState
.) Some of the UI content I'm animating depends on a property contained within InitialUiState
, but not SecondaryUiState
. So when it's animating out of visibility, my uiState
no longer contains the data needed in order to render the UI. I can wrap the content in if (uiState is InitialUiState)
, but then the content just disappears instead of animating out. My thought is I need to memoize the most recent InitialUiState
value, using something like val initialUiState = rememberUnless(uiState is InitialUiState) { uiState as? InitialUiState }
, so that we can access the data contained within the last InitialUiState
, even when the current value of uiState
is a SecondaryUiState
. In this example it would recalculate on every composition where uiState is InitialUiState
, but "remember" and return the previous calculation otherwise.Colton Idle
08/19/2021, 5:44 PMChuck Stein
08/19/2021, 6:17 PMderivedStateOf
before, but after reading the docs I'm not sure how it would apply to my situation. It seems like it will always use the most recent value of the state it depends on, so when the uiState
is a SecondaryUiState
, how would I access the most recent InitialUiState
?Chuck Stein
08/19/2021, 8:59 PM@Composable
inline fun <T> rememberAndRecalculateIf(predicate: Boolean, calculation: @DisallowComposableCalls () -> T): T {
var recalculationKey by remember { mutableStateOf(false) }
if (predicate) {
// changing the key means we should recalculate, and we only change it if the predicate is true
recalculationKey = !recalculationKey
}
return remember(recalculationKey, calculation)
}
Chuck Stein
08/19/2021, 8:59 PMAnimatedVisibility
, to not capture and make available within its scope the last Snapshot state before visible
became false...Colton Idle
08/19/2021, 9:22 PMDoris Liu
08/19/2021, 11:08 PMAnimatedContent
would work better for your use case, where you specify what the UI looks like for initialUIState
and {}
(i.e. empty) for SecondaryUiState
. Then when the targetState changes from initialUIState to SecondaryUiState, AnimatedContent
will keep a reference to the previous state until the previous content has been animated out.Chuck Stein
08/20/2021, 9:19 PMAnimatedContent
! Following the flowchart in the documentation led me to AnimatedVisibility
, because I wouldn't say the answer to "Animating appearance/disappearance" is "No" for my use case 😅
So AnimatedContent
works except for one major issue -- since my targetState
is the current uiState
(as it contains the data I need to keep a reference to while animating out), it is now running the transition each time there are any changes to the data contained within InitialUiState
! Ideally I want to only run the transition each time uiState is InitialUiState
changes, but if I set that to my targetState
then I only have access to that boolean within the content
block, whereas what I really need is a reference to the previous InitialUiState
, so that I can animate that data out once the actual uiState
is SecondaryUiState
. So, is there any way to run the transition every time a particular state changes, but then each time that state changes, also hold onto additional state to make available within the content
block? If not, I don't see any way to achieve my (pretty normal) use case with the current animation APIs.Doris Liu
08/20/2021, 9:40 PMAnimatedContent
perceive all InitialUiState
instances as the same state. One thing you could try is overriding equals
in InitialUiState
. Seems like AnimatedContent should have customizable equality support.Doris Liu
08/20/2021, 9:42 PMChuck Stein
08/20/2021, 10:58 PMequals
in InitialUiState
to return true if the other value is also an InitialUiState
, then whenever I modify a property of InitialUiState
, the UI does not update to reflect the change, I assume because the Compose runtime thinks the State
has not changed since it sees that it is "equal" to the previous State
.Doris Liu
08/20/2021, 11:24 PMequals
:
val targetState = key(state is InitialUiState) {
rememberUpdatedState(state)
}
AnimatedContent(targetState) { target ->
if (target.value is InitialUiState) {
// Content for initial ui state
} else {
// Content for secondary ui state
}
}
Chuck Stein
08/20/2021, 11:24 PMAnimatedContent
seems like it would get the job done. Although I wonder if this would be flexible enough for all use cases -- I could see the content
block potentially needing to reference multiple state objects from when the old content started animating out. Almost seems like when the targetState
changes, instead of calling the content
block with the previous targetState
to animate the outgoing content, we should enter into a Snapshot
taken just before targetState
changed, and then call the content
block inside of that Snapshot
to animate the outgoing content. Wouldn't that automatically take care of making all state objects from the old content still available while it's animating out?Chuck Stein
08/20/2021, 11:25 PMDoris Liu
08/20/2021, 11:29 PMAlmost seems like when theWe are working on such a concept in the low level of Compose. It would be very useful as a default behavior for removal animation, once it's done.changes, instead of calling thetargetState
block with the previouscontent
to animate the outgoing content, we should enter into atargetState
taken just beforeSnapshot
changed, and then call thetargetState
block inside of thatcontent
to animate the outgoing content.Snapshot
Chuck Stein
08/20/2021, 11:33 PMChuck Stein
08/21/2021, 1:15 AMkey
and rememberUpdatedState
seems to work, but I can't figure out why. What exactly is key
doing here? I can't tell from the docs, and the examples I've seen all have to do with identifying composables within a for loop. At first I assumed it would only run the block to update the state if the value of state is InitialUiState
changed, but I tested and this doesn't seem to be the case. So if the targetState
is changing even when it's changing from one InitialUiState
to another InitialUiState
, why does no transition occur between the two?Doris Liu
08/21/2021, 1:25 AMkey
specifies the identity of containing composable. In that sense, it's the same as specifying different keys for different content in a loop. What it does here is to create a new rememberUpdatedState
composable as the identity changes. Therefore you get a new State<UiState>
instance when changing from InitialUiState to SecondaryUiState.
When changing from one InitialUIState to another, the key stays the same (i.e. true), only the State<UiState>#value
gets updated.Chuck Stein
08/21/2021, 1:29 AM