This post is focused on how I created a form validation for user registration.
I will just be posting the Composable I created because this could just be implemented anywhere. There is no outside connection to it yet except for a couple of booleans to handle submission and failure on submission.
The user input is validated in the TextFields onValueChanged method. If there are any issues with the user input, the TextField flags an error to display a Text composable with the error message under TextField the user is typing in.
Code
build.gradle – add extended material icons to use Visibility icons
implementation(libs.androidx.material.icons.extended)
libs.versions.toml – for icons stated above
[versions]
....
materialIconsExtended = "1.7.6"
....
[libraries]
....
androidx-material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "materialIconsExtended" }
....
RegisterUI – main composable, I created a seperate file for it to keep things organized.
@Composable
fun RegisterUI(
userRegisterFail: Boolean,
isRegisterProcessing: Boolean,
) {
var usernameValidator by remember { mutableStateOf(true) }
var emailValidator by remember { mutableStateOf(true) }
var passwordValidator by remember { mutableStateOf(true) }
var passwordVisible by remember { mutableStateOf(false) }
var username by remember { mutableStateOf("Winston") }
var email by remember { mutableStateOf("winston@clowncollege.com") }
var password by remember { mutableStateOf("Password01") }
ElevatedCard(
modifier = Modifier
.fillMaxWidth()
.padding(25.dp),
colors = CardDefaults.elevatedCardColors(MaterialTheme.colorScheme.primaryContainer)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp), horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.padding(10.dp))
Text(text = "Register", style = MaterialTheme.typography.titleMedium)
Spacer(modifier = Modifier.padding(10.dp))
TextField(value = username, onValueChange = {
username = it
if (username.isEmpty()) {
usernameValidator = false
} else {
usernameValidator = true
}
}, placeholder = { Text(text = "Choose your user name") })
if (!usernameValidator) {
Text(
text = "Invalid Username",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.error
)
}
Spacer(modifier = Modifier.padding(10.dp))
TextField(value = email, onValueChange = {
email = it
if (email.isEmpty() || !android.util.Patterns.EMAIL_ADDRESS.matcher(email)
.matches()
) {
emailValidator = false
} else {
emailValidator = true
}
}, placeholder = { Text(text = "Enter Your Email") })
if (!emailValidator) {
Text(
text = "Invalid Email",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.error
)
}
Spacer(modifier = Modifier.padding(10.dp))
TextField(value = password, onValueChange = {
password = it
if (password.isEmpty() || password.length < 8 || !password.contains("[0-9]".toRegex()) || !password.contains("[A-Z]".toRegex()) || password.contains("[{('\"~\\[|\\]^)}]".toRegex())) {
passwordValidator = false
} else {
passwordValidator = true
}
}, placeholder = { Text(text = "Enter Your Password") },
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
trailingIcon = {
IconButton(onClick = { passwordVisible = !passwordVisible }) {
Icon(
imageVector = if (passwordVisible) Icons.Filled.Visibility else Icons.Filled.VisibilityOff,
contentDescription = if (passwordVisible) "Hide Password" else "Show Password"
)
}
}
)
if (!passwordValidator) {
Text(
text = "Invalid Password" +
"\n-Needs to be at least 8 characters long" +
"\n-At least 1 number" +
"\n-At least 1 capital letter" +
"\n-Must Not contain one of the following " +
"special characters:" +
"\n { ( ' \" ~ [ | ] ^ ) }",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.error
)
}
Spacer(modifier = Modifier.padding(10.dp))
//check if form is processing info, change from button to loading circle
if (!isRegisterProcessing) {
var check = listOf(usernameValidator, emailValidator, passwordValidator)
Button(
enabled = if (check.any { !it }) false else true,
onClick = {
//submit form
}) {
Text(text = "Register")
}
} else {
CircularProgressIndicator()
}
if (userRegisterFail) {
Spacer(modifier = Modifier.padding(10.dp))
Text(
text = "Registration Error",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.error
)
}
}
}
}
Hope this helps.