'How to use a condition in a MutableState in Jetpack Compose
I'm trying to change my loginEnabled value to true or false based off two textfield's being not empty, however my current implementation always returns false, can someone see what mistake im making? My email & password state's are updated using the onValueChange function inside two textfield's
@Composable
fun LoginScreen() {
var email by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
var passwordVisibility by remember { mutableStateOf(false) }
var loginEnabled by remember { mutableStateOf(
email.isNotEmpty() && password.isNotEmpty()
)}
TextField(
value = email,
onValueChange = { email = it },
label = { Text("Email") }
)
TextField(
value = password,
onValueChange = { password= it },
label = { Text("Password") }
)
Solution 1:[1]
@Composable
fun LoginScreen() {
var email by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
var passwordVisibility by remember { mutableStateOf(false) }
var loginEnabled = email.isNotEmpty() && password.isNotEmpty()
TextField(
value = email,
onValueChange = { email = it },
label = { Text("Email") }
)
TextField(
value = password,
onValueChange = { password= it },
label = { Text("Password") }
)
Your code did not work as expected because you were using a remember
block, which was preventing re-calculation of the loginEnabled
variable. Whenever a value on which it depends changed, a recomposition would be triggered, but remember
would return the previously stored value and prevent it from updating correctly. All that was required was to remove the remember
from the initialization bit, and the code worked.
As pointed out by @TylerGanon in the comment below, when a single variable depends on multiple other variables, where every variable (including the dependent one) are supposed to be state-holders, i.e., they are of type MutableState<T>
, it is better to use derivedStateOf {...}
to get values for the dependent variable. The usage is as
@Composable fun CountDisplay(count: State<Int>) {
Text("Count: ${count.value}")
}
@Composable fun Example() {
var a by remember { mutableStateOf(0) }
var b by remember { mutableStateOf(0) }
val sum = remember { derivedStateOf { a + b } }
// Changing either a or b will cause CountDisplay to recompose but not trigger Example
// to recompose.
CountDisplay(sum)
}
So it is a bit different than mutableStateOf()
since when a state-holder that it depends on, changes -- it will not cause a recomposition of the entire composable, but only a re-calculation. For example, above, when a
or b
changed, the Example
Composable did NOT recompose, because the only places a
and b
were being read in the Composable
was the calculation
block of the derivedStateOf
call. Hence, only a re-calculation took place and updated the value of the dependent variable, i.e., sum
. Now, since sum
is again not directly used in Example
, it will still not recompose, but it is being passed as a parameter to CountDisplay
, making it the only Composable
to recompose in this entire process. So, changing the value of a
, and b
did not trigger any recompositions on Example
, but triggered one on CountDisplay
.
Are we clear?
Solution 2:[2]
Have a look at the doc for remember(calculation: @DisallowComposableCalls () -> T): T
:
Remember the value produced by [calculation]. [calculation] will only be evaluated during the composition.Recomposition will always return the value produced by composition
You have to use:
var loginEnabled by remember(email, password) { mutableStateOf(
email.isNotEmpty() && password.isNotEmpty()
)}
Remember the value returned by calculation if key1 and key2 are equal to the previous composition, otherwise produce and remember a new value by calling calculation.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | jns |