Building Forms with Jetpack Compose

Introduction

Forms are an essential part of most mobile applications, allowing users to input data efficiently. With Jetpack Compose, building forms is intuitive, declarative, and highly customizable. Jetpack Compose provides powerful UI components such as TextField, Checkbox, RadioButton, and Switch to create interactive forms seamlessly.

In this guide, we’ll explore:

  • How to create forms in Jetpack Compose.
  • Handling user input with state management.
  • Form validation techniques.
  • Submitting form data.

Creating a Simple Form in Jetpack Compose

Let’s start by building a basic form with TextField and a submit button.

@Composable
fun SimpleForm() {
    var name by remember { mutableStateOf("") }
    var email by remember { mutableStateOf("") }
    
    Column(modifier = Modifier.padding(16.dp)) {
        TextField(
            value = name,
            onValueChange = { name = it },
            label = { Text("Name") },
            modifier = Modifier.fillMaxWidth()
        )
        
        Spacer(modifier = Modifier.height(8.dp))
        
        TextField(
            value = email,
            onValueChange = { email = it },
            label = { Text("Email") },
            modifier = Modifier.fillMaxWidth()
        )
        
        Spacer(modifier = Modifier.height(16.dp))
        
        Button(
            onClick = { /* Handle submission */ },
            modifier = Modifier.align(Alignment.End)
        ) {
            Text("Submit")
        }
    }
}

Handling Form State with rememberSaveable

To persist form state across configuration changes (e.g., screen rotation), use rememberSaveable:

@Composable
fun PersistentForm() {
    var name by rememberSaveable { mutableStateOf("") }
    var email by rememberSaveable { mutableStateOf("") }
    
    Column(modifier = Modifier.padding(16.dp)) {
        TextField(value = name, onValueChange = { name = it }, label = { Text("Name") })
        Spacer(modifier = Modifier.height(8.dp))
        TextField(value = email, onValueChange = { email = it }, label = { Text("Email") })
    }
}

Adding Checkboxes, RadioButtons, and Switches

Checkbox Example

@Composable
fun CheckboxExample() {
    var isChecked by remember { mutableStateOf(false) }
    Row(verticalAlignment = Alignment.CenterVertically) {
        Checkbox(checked = isChecked, onCheckedChange = { isChecked = it })
        Text("Accept Terms & Conditions")
    }
}

RadioButton Example

@Composable
fun GenderSelection() {
    val genders = listOf("Male", "Female", "Other")
    var selectedGender by remember { mutableStateOf(genders[0]) }
    
    Column {
        genders.forEach { gender ->
            Row(verticalAlignment = Alignment.CenterVertically) {
                RadioButton(
                    selected = (gender == selectedGender),
                    onClick = { selectedGender = gender }
                )
                Text(gender)
            }
        }
    }
}

Switch Example

@Composable
fun NotificationToggle() {
    var isEnabled by remember { mutableStateOf(false) }
    Row(verticalAlignment = Alignment.CenterVertically) {
        Switch(checked = isEnabled, onCheckedChange = { isEnabled = it })
        Text("Enable Notifications")
    }
}

Form Validation in Jetpack Compose

Adding form validation helps ensure users provide correct inputs before submission.

@Composable
fun ValidatedForm() {
    var name by remember { mutableStateOf("") }
    var email by remember { mutableStateOf("") }
    var errorMessage by remember { mutableStateOf("") }
    
    Column(modifier = Modifier.padding(16.dp)) {
        TextField(
            value = name,
            onValueChange = { name = it },
            label = { Text("Name") },
            isError = name.isEmpty()
        )
        if (name.isEmpty()) {
            Text("Name cannot be empty", color = Color.Red)
        }
        
        Spacer(modifier = Modifier.height(8.dp))
        
        TextField(
            value = email,
            onValueChange = { email = it },
            label = { Text("Email") },
            isError = !email.contains("@")
        )
        if (!email.contains("@")) {
            Text("Enter a valid email", color = Color.Red)
        }
        
        Spacer(modifier = Modifier.height(16.dp))
        
        Button(
            onClick = {
                if (name.isNotEmpty() && email.contains("@")) {
                    errorMessage = "Form submitted successfully!"
                } else {
                    errorMessage = "Please fix errors before submitting."
                }
            },
            enabled = name.isNotEmpty() && email.contains("@")
        ) {
            Text("Submit")
        }
        
        Spacer(modifier = Modifier.height(8.dp))
        Text(errorMessage, color = if (errorMessage.contains("successfully")) Color.Green else Color.Red)
    }
}

Conclusion

Jetpack Compose makes building forms intuitive, flexible, and state-driven. Using rememberSaveable ensures data persistence, while validation techniques improve user input accuracy.

Key Takeaways:

  • Use LazyColumn for forms with multiple fields to optimize performance.
  • Persist form state using rememberSaveable.
  • Ensure form validation before submission.

Call to Action

Start building dynamic forms with Jetpack Compose today! Subscribe to our blog for more Jetpack Compose tutorials and best practices.