Status Update
Comments
an...@google.com <an...@google.com> #2
ub...@gmail.com <ub...@gmail.com> #3
Please revisit severity & priority of this issue. This is a significant code correctness bug.
an...@google.com <an...@google.com> #4
ub...@gmail.com <ub...@gmail.com> #5
Thank you for the helpful elaboration. I will need to test out how I can correctly leverage key(input)
in my case. My current input
is an enum, not sure how that will pan out for the compose hash key with key(input)
, before and after process death; alternatively I guess I could generate a custom save key, which perhaps would be safer to protect from changes in Compose internals.
I do not agree on this being a use case so uncommon that it's ok to keep this an undocumented limitation; I think it is likely to cause hidden, timing-based bugs in production.
For example, I can show a button depending on data model state. User may click button to show dialog. Compose state of showing dialog is saved for configuration change. When data model state changes, dialog may no longer be valid, and dialog must close. Pretty simple code, but seems broken due to this issue; when data model changes during configuration change (or right before/after process death), the dialog will not close automatically:
var showDialog: Boolean by rememberSaveable(modelState) { mutableStateOf(false) }
jo...@google.com <jo...@google.com> #6
Another use case for this is ModalBottomSheetState (yay). ModalBottomSheetState
's Saver
currentValue
and restores it as initialValue
. initialValue = HalfExpanded && skipHalfExpanded = true
is an illegal argument. If skipHalfExpanded
was set to true before restoration and the currentValue
was HalfExpanded
, the saver will attempt to restore the state with this invalid combination. A custom key helps us but maybe there's a better way.
be...@comdirect.de <be...@comdirect.de> #7
Here is another use case from my side:
A payment flow where you start with an payee input screen (PayeeScreen), followed by a screen to enter the amount to pay (AmountScreen). At the end of the flow, a summary screen (SummaryScreen) is shown. On the summary screen you can tap on the "amount" field to open a new instance of the amount screen. There you can modify the value and press "Continue" confirms the new value while navigating back using the back button dismisses the screen without applying any changes.
Now the following requirements should be realized:
- The amount you enter on AmountScreen is only applied when the user confirms it by pressing next, i.e. you reach the first amount screen, enter 100€ and then go back to the PayeeScreen and then go forward to the AmountScreen again, the amount input field is empty.
- on AmountScreen you don't loose your input on configuration change
- after changing the amount on the second AmountScreen and then navigating back to the first AmountScreen (using system back), the input is set to the changed value
- AmountScreen should be self contained and not know about any outside ViewModels
For this scenario the amount needs to be stored inside the composable orchestrating the flow (i.e. payee, amount and summary screen). This would be really easy, if rememberSaveable would work as expected:
fun AmountScreen(initialValue: String?, onAmountConfirmed: (String) -> Unit) {
val amountInput = rememberSaveable(initialValue) {
mutableStateOf(initialValue)
}
// modify amount input using a text field
TextField(
value = amountInput ?: "",
onValueChange = { amountInput = it },
)
// apply value on "Continue"
Button(onClick = { onAmountConfirmed(amountInputState.value) }) {
Text(text = "Continue")
}
}
an...@google.com <an...@google.com> #8
I think for this specific use case it might work if you just wrap rememberSaveable into extra key(initialValue) { ... }
.
The key composable will change the currentCompositeKeyHash we use inside of rememberSaveable, meaning that when initialValue changes the key changes and the value is not restored.
be...@comdirect.de <be...@comdirect.de> #9
Hi, thank you for the hint. I was able to use key(initialValue) to achieve my goal. For the benefit of others here is my helper function to achieve our specific goal:
@Composable
public fun <T : Any> rememberSaveableWithVolatileInitialValue(
initialValue: T
): MutableState<T> {
return key(initialValue) {
rememberSaveable {
mutableStateOf(initialValue)
}
}
}
an...@google.com <an...@google.com> #10
It seems like:
- There is no good way to fix it
- The mechanism worked like that since 1.0 and there were no real complains about this So we are closing it as obsolete, if you still think there is an important use case we need to fix please file a separate bug
ub...@gmail.com <ub...@gmail.com> #11
I see quite a few real complaints here. I gave an example in
Description