Lukasz Kalnik
02/16/2022, 4:56 PMLukasz Kalnik
02/16/2022, 4:56 PM@Composable
fun EnterNameDialog(
uiState: EnterNameDialogState
) {
val currentText = remember { mutableStateOf(uiState.name) }
AlertDialog(
// ....
text = {
TextField(
value = currentText.value
onValueChange = { currentText.value = it }
// ...
}
data class EnterNameDialogState(
val show: Boolean,
val name: String,
)
Lukasz Kalnik
02/16/2022, 5:00 PM"test"
inside of uiState.name
, when setting a breakpoint it gets immediately set to empty string in currentText.value
. It is also empty in the UI obviously.Lukasz Kalnik
02/16/2022, 5:01 PM""
when the user dismisses/confirms the dialog. Is it possible that the remember
remembers this old state from previous composition?Adam Powell
02/16/2022, 5:06 PMcurrentText.value
is set to ""
?Adam Powell
02/16/2022, 5:07 PMuiState.name
directly and report new values up either via callback or method on uiState
so that they can be applied to uiState
Lukasz Kalnik
02/16/2022, 5:13 PMonValueChange
was directly calling up to ViewModel callback which was only in charge of the name
etc.).
However we have changed that after adding a trailing icon to clear the text.
It was convenient to keep the editing of the text internally via remember
and only send the text up to ViewModel when the user confirmed the dialog.Lukasz Kalnik
02/16/2022, 5:14 PMAdam Powell
02/16/2022, 5:17 PMLukasz Kalnik
02/16/2022, 5:17 PMcurrentText = remember // ...
?Adam Powell
02/16/2022, 5:17 PMEnterNameDialog
is calledLukasz Kalnik
02/16/2022, 5:49 PMval currentText = remember { }
line and added the callback to viewModel for onValueChange
and everything works perfectly.Lukasz Kalnik
02/16/2022, 5:49 PMremember
.Lukasz Kalnik
02/17/2022, 10:11 AMcurrentText.value
was always reset to ""
.remember { mutableStateOf(value) }
works. I thought you can kind of reinitialize it from outside, by passing a new value
.remember
makes the computation inside lazy, and only calls it the first time, initializing it with value
.value
as ""
indeed at the first call site of EnterNameDialog
, when user entered the screen. So we couldn't change it later because of remember
.Lukasz Kalnik
02/17/2022, 4:39 PMremember
.
You just need to use remember
with an additional parameter as key. This way the remembered value will get recalculated every time the key changes:
val currentText = remember(uiState.name) { mutableStateOf(uiState.name) }
That updates currentText
every time uiState.name
changes.Lukasz Kalnik
02/18/2022, 10:53 AMremember
to uiState.name
, it will only recalculate its value if it receives a new uiState.name
but with a different value than the previous uiState.name
it was initialized with.
So the approach from the message above doesn't work in the following scenario:
1. App shows the dialog, initializing it with an empty string as uiState.name
2. User types something like "test"
3. User clicks the "OK" button
4. Dialog is dismissed
5. User opens the dialog again
6. App code again initializes the dialog with an empty string as uiState.name
7. remember
looks at uiState.name
and compares it with its previous value
8. Both values are empty strings, so remember
determines the key didn't change
9. Because of that, remember
decides there's no need to reinitialize currentText
so it provides the cached value
10. But the cached currentText
value was last updated by the user typing "test"
the previous time the dialog was opened
11. So dialog now opens with the old "test"
text instead of the empty string from uiState.name
Lukasz Kalnik
02/18/2022, 10:59 AMkey
for remember
reinitialization something that is guaranteed to change every time you want to reinitialize currentText
.
In my case EnterNameDialogState
contains a show
variable, which defines if the dialog is shown or hidden.
So if I want to reinitialize the remembered currentText
every time the dialog is shown or hidden, I just couple the remember
invocation to uiState.show
(instead of uiState.name
as before). Note that inside of mutableStateOf()
we still have uiState.name
, because that's what we want to reinitialize currentText
with:
val currentText = remember(uiState.show) { mutableStateOf(uiState.name) }
What it does is "whenever uiState.show
is changed, reinitialize currentText
with a MutableState
of uiState.name
, otherwise provide cached value".