Josh Eldridge
03/23/2024, 4:25 AMrememberSaveable
and how something is considered to leave composition. I'm a bit confused because a rememberSaveable
value sticks around after device rotation, which makes sense. But in the scenario that the rememeberSaveable
was part of an if/else, and "leaves" the composition on rotation, it's value is still preserved after its block is re-introduced. An example below I get into the if
with a button click, add the value a few times, rotate the device (causing me to go back to the else) and click the Go to if
the rememberSaveable
value is still around, even though it left the composition while the composable was in the else
?
Column {
var inIf by remember { mutableStateOf(false) }
if (inIf) {
var rememberTest by rememberSaveable { mutableIntStateOf(1) }
Timber.d(rememberTest.toString())
Button({
rememberTest += 1
}) {
Text("Do it $rememberTest times")
}
Button({
inIf = false
}) {
Text("Go to else")
}
} else {
Button({
inIf = true
}) {
Text("Go to if")
}
}
}
Josh Eldridge
03/23/2024, 4:36 AMrememberSaveable
to be freshly created if I rotated, it was outside the composition after rotation, and I brought it back into the composition through the conditionalAlex Vanyo
03/25/2024, 1:49 AMrememberSaveable
works by storing the state of whatever is in composition when saved instance state is saved. Upon recreation, state starts getting restored back out of saved instance state - but it isn't required that the state be restored immediately in the first composition. The saved instance state will hang around until something pulls it back out.
So in this case, if inIf
is true when rotating, the saved instance state of rememberTest
will be saved. After recreation, where inIf
is false, the saved instance state will remain available but nothing has consumed it yet. When inIf
becomes true
, now the saved instance state will be restored from before the recreation.
The rule is more like "`rememberSaveable` will lose its state when leaving composition unless the cause of leaving composition is due to the saved instance state being saved"Alex Vanyo
03/25/2024, 1:50 AMvar isLoading by remember { mutableStateOf(true) }
LaunchedEffect(Unit) {
delay(1000)
isLoading = true
}
if (isLoading) {
CircularProgressIndicator()
} else {
val myState by rememberSaveable { mutableStateOf(0) }
// ...
}
Josh Eldridge
03/25/2024, 2:14 AMmovableContentOf
to handle layout changes without having two separate composables with their own state. But this is helpful to know still.
Is there a way to know looking inside the rememberSaveable
code to know how it would differentiate leaving composition normally vs from a configuration change aka saved instance state?
When I'm initially looking inside it I'm guessing maybe it maintains the same compositeKey on rotation vs normally leaving the composition it'd generate a new key and thus clear any saved state it was referring to?Alex Vanyo
03/25/2024, 2:24 AMrememberSaveable
internally registers a provider to the LocalSaveableStateRegistry
. When rememberSaveable
leaves composition, it unregisters that provider. So from the perspective of rememberSaveable
, it doesn't care about why it is leaving composition - it just registers and unregisters based on if it is in composition.
The resulting behavior is based on when LocalSaveableStateRegistry
queries all of its providers to gather up saved instance state. When recreation happens, that query will happen before the providers get unregistered. When it is an `if`/`else` swapping whether rememberSaveable
is in composition, there's no query to save the instance state, so it disappears.Josh Eldridge
03/25/2024, 3:26 AMrememberSaveable
and I think I'm understanding better from your explanation. When I dug into it I had the "oh duh" moment because when switching between an if/else normally, the`rememberSaveable` isn't going to save anything off, it's only going to save something off from a config change or basically onSaveInstanceState
right? So there is nothing for the registry to restore in the normal if/else, but in the case of the rotation, that registry still had that value and going back into the if makes this "new" rememberSaveable
consume that restored valueJosh Eldridge
03/25/2024, 3:32 AMrememberSaveable
's in order to put them back into the correct order, and it seems like that key is always the same like you said. I'm guessing this is the important piece to tying them back to the same rememberSaveable calls? It uses the order of the calls to essentially make a queue for each rememberSaveable to call.Alex Vanyo
03/25/2024, 3:33 AMJosh Eldridge
03/25/2024, 3:36 AMAlex Vanyo
03/25/2024, 3:37 AM