How can I validate user input in two fields password
and confirmPassword
in jetpack compose. I tried a var matchError
but it doesn't show error when the passwords don't match. How can I fix this? Also, how can i reference the password field from ConfirmPasswordTextFieldComponent
function? Validation occurs when the user types in the confirmPassword
fields.
@Composable
fun ConfirmPasswordTextFieldComponent(
text: String,
labelValue: String,
confirmText:String, painterResource: Painter,
onTextChanged: (String) -> Unit,
hasError: Boolean = false,
) {
val localFocusManager = LocalFocusManager.current
val password = remember {
mutableStateOf("")
}
val passwordVisible = remember {
mutableStateOf(false)
}
val matchError = remember { mutableStateOf(false) }
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.clip(componentShapes.small),
label = { Text(text = labelValue) },
colors = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = Primary,
focusedLabelColor = Primary,
cursorColor = Primary,
backgroundColor = BgColor
),
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Password,
imeAction = ImeAction.Done
),
singleLine = true,
keyboardActions = KeyboardActions {
localFocusManager.clearFocus()
},
maxLines = 1,
value = password.value,
onValueChange = {
password.value = it
onTextChanged(it)
},
leadingIcon = {
Icon(painter = painterResource, contentDescription = "")
},
trailingIcon = {
val iconImage = if (passwordVisible.value) {
Icons.Filled.Visibility
} else {
Icons.Filled.VisibilityOff
}
val description = if (passwordVisible.value) {
stringResource(id = R.string.hide_password)
} else {
stringResource(id = R.string.show_password)
}
IconButton(onClick = { passwordVisible.value = !passwordVisible.value }) {
Icon(imageVector = iconImage, contentDescription = description)
}
},
isError = hasError || matchError.value,
visualTransformation = if (passwordVisible.value) VisualTransformation.None else PasswordVisualTransformation(),
)
Spacer(modifier = Modifier.height(8.dp))
if (confirmText != text) {
Text(
text = stringResource(id = R.string.error_password_no_match),
color = colorResource(id = R.color.colorSoftRed300),
fontSize = 10.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.semantics { contentDescription = "ConfirmPasswordMessage" },
)
matchError.value = true
} else {
matchError.value = false
}
}
PasswordTextFieldComponent:
@Composable
fun PasswordTextFieldComponent(
labelValue: String, painterResource: Painter,
onTextSelected: (String) -> Unit,
errorStatus: Boolean = false
) {
val localFocusManager = LocalFocusManager.current
val password = remember {
mutableStateOf("")
}
val passwordVisible = remember {
mutableStateOf(false)
}
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.clip(componentShapes.small),
label = { Text(text = labelValue) },
colors = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = Primary,
focusedLabelColor = Primary,
cursorColor = Primary,
backgroundColor = BgColor
),
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Password,
imeAction = ImeAction.Done
),
singleLine = true,
keyboardActions = KeyboardActions {
localFocusManager.clearFocus()
},
maxLines = 1,
value = password.value,
onValueChange = {
password.value = it
onTextSelected(it)
},
leadingIcon = {
Icon(painter = painterResource, contentDescription = "")
},
trailingIcon = {
val iconImage = if (passwordVisible.value) {
Icons.Filled.Visibility
} else {
Icons.Filled.VisibilityOff
}
val description = if (passwordVisible.value) {
stringResource(id = R.string.hide_password)
} else {
stringResource(id = R.string.show_password)
}
IconButton(onClick = { passwordVisible.value = !passwordVisible.value }) {
Icon(imageVector = iconImage, contentDescription = description)
}
},
visualTransformation = if (passwordVisible.value) VisualTransformation.None else PasswordVisualTransformation(),
isError = !errorStatus
)
}
A simplified example for validation.
We can use derivedState
for this use case.
@Composable
fun PasswordValidationSample() {
var password by remember {
mutableStateOf("")
}
var confirmPassword by remember {
mutableStateOf("")
}
val isNotMatching = remember {
derivedStateOf {
password != confirmPassword
}
}
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top,
) {
OutlinedTextField(value = password, onValueChange = { password = it })
OutlinedTextField(value = confirmPassword, onValueChange = { confirmPassword = it })
if (isNotMatching.value) {
Text(text = "Passwords not matching")
}
}
}