In this Jetpack compose tutorial we will learn how to create user registration and login form in Android application using Jetpack Compose. How to navigation from one screen to other screen by compose Navigation component
Let's get started
Step 1: Create android application in android studio
Step 2: Follow step for setup Jetpack Compose with Android Studio
Step 3: Add LoginScreen composable function
Our login screen contains Email and password fields and submit button
@Composable fun LoginScreen() { val context = LocalContext.current val email = remember { mutableStateOf(TextFieldValue()) } val emailErrorState = remember { mutableStateOf(false) } val passwordErrorState = remember { mutableStateOf(false) } val password = remember { mutableStateOf(TextFieldValue()) } Column( modifier = Modifier .fillMaxSize() .padding(16.dp), verticalArrangement = Arrangement.Center, ) { Text(text = buildAnnotatedString { withStyle(style = SpanStyle(color = Color.Red)) { append("S") } withStyle(style = SpanStyle(color = Color.Black)) { append("ign") } withStyle(style = SpanStyle(color = Color.Red)) { append(" I") } withStyle(style = SpanStyle(color = Color.Black)) { append("n") } }, fontSize = 30.sp) Spacer(Modifier.size(16.dp)) OutlinedTextField( value = email.value, onValueChange = { if (emailErrorState.value) { emailErrorState.value = false } email.value = it }, isError = emailErrorState.value, modifier = Modifier.fillMaxWidth(), label = { Text(text = "Enter Email*") }, ) if (emailErrorState.value) { Text(text = "Required", color = Color.Red) } Spacer(Modifier.size(16.dp)) val passwordVisibility = remember { mutableStateOf(true) } OutlinedTextField( value = password.value, onValueChange = { if (passwordErrorState.value) { passwordErrorState.value = false } password.value = it }, isError = passwordErrorState.value, modifier = Modifier.fillMaxWidth(), label = { Text(text = "Enter Password*") }, trailingIcon = { IconButton(onClick = { passwordVisibility.value = !passwordVisibility.value }) { Icon( imageVector = if (passwordVisibility.value) Icons.Default.VisibilityOff else Icons.Default.Visibility, contentDescription = "visibility", tint = Color.Red ) } }, visualTransformation = if (passwordVisibility.value) PasswordVisualTransformation() else VisualTransformation.None ) if (passwordErrorState.value) { Text(text = "Required", color = Color.Red) } Spacer(Modifier.size(16.dp)) Button( onClick = { when { email.value.text.isEmpty() -> { emailErrorState.value = true } password.value.text.isEmpty() -> { passwordErrorState.value = true } else -> { passwordErrorState.value = false emailErrorState.value = false Toast.makeText( context, "Logged in successfully", Toast.LENGTH_SHORT ).show() } } }, content = { Text(text = "Login", color = Color.White) }, modifier = Modifier.fillMaxWidth(), colors = ButtonDefaults.buttonColors(backgroundColor = Color.Red) ) Spacer(Modifier.size(16.dp)) Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { TextButton(onClick = { }) { Text(text = "Register ?", color = Color.Red) } } } }
|
Add Registration Screen composable function
Registration page contains Name, Email, Mobile number, country code, password, confirm password and submit button
@Composable fun RegistrationScreen() { val context = LocalContext.current val name = remember { mutableStateOf(TextFieldValue()) } val email = remember { mutableStateOf(TextFieldValue()) } val countryCode = remember { mutableStateOf(TextFieldValue()) } val mobileNo = remember { mutableStateOf(TextFieldValue()) } val password = remember { mutableStateOf(TextFieldValue()) } val confirmPassword = remember { mutableStateOf(TextFieldValue()) } val nameErrorState = remember { mutableStateOf(false) } val emailErrorState = remember { mutableStateOf(false) } val passwordErrorState = remember { mutableStateOf(false) } val confirmPasswordErrorState = remember { mutableStateOf(false) } val scrollState = rememberScrollState() Column( modifier = Modifier .fillMaxSize() .padding(16.dp) .verticalScroll(scrollState), verticalArrangement = Arrangement.Center, ) { Text(text = buildAnnotatedString { withStyle(style = SpanStyle(color = Color.Red)) { append("R") } withStyle(style = SpanStyle(color = Color.Black)) { append("egistration") } }, fontSize = 30.sp) Spacer(Modifier.size(16.dp)) OutlinedTextField( value = name.value, onValueChange = { if (nameErrorState.value) { nameErrorState.value = false } name.value = it }, modifier = Modifier.fillMaxWidth(), isError = nameErrorState.value, label = { Text(text = "Name*") }, ) if (nameErrorState.value) { Text(text = "Required", color = Color.Red) } Spacer(Modifier.size(16.dp)) OutlinedTextField( value = email.value, onValueChange = { if (emailErrorState.value) { emailErrorState.value = false } email.value = it }, modifier = Modifier.fillMaxWidth(), isError = emailErrorState.value, label = { Text(text = "Email*") }, ) if (emailErrorState.value) { Text(text = "Required", color = Color.Red) } Spacer(modifier = Modifier.size(16.dp)) Row() { OutlinedTextField( value = countryCode.value, onValueChange = { countryCode.value = it }, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, autoCorrect = false ), modifier = Modifier.fillMaxWidth(0.3f), label = { Text(text = "Code") }, ) Spacer(modifier = Modifier.size(16.dp)) OutlinedTextField( value = mobileNo.value, onValueChange = { mobileNo.value = it }, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Phone, autoCorrect = false ), label = { Text(text = "Mobile No") }, ) } Spacer(Modifier.size(16.dp)) val passwordVisibility = remember { mutableStateOf(true) } OutlinedTextField( value = password.value, onValueChange = { if (passwordErrorState.value) { passwordErrorState.value = false } password.value = it }, modifier = Modifier.fillMaxWidth(), label = { Text(text = "Password*") }, isError = passwordErrorState.value, trailingIcon = { IconButton(onClick = { passwordVisibility.value = !passwordVisibility.value }) { Icon( imageVector = if (passwordVisibility.value) Icons.Default.VisibilityOff else Icons.Default.Visibility, contentDescription = "visibility", tint = Color.Red ) } }, visualTransformation = if (passwordVisibility.value) PasswordVisualTransformation() else VisualTransformation.None ) if (passwordErrorState.value) { Text(text = "Required", color = Color.Red) } Spacer(Modifier.size(16.dp)) val cPasswordVisibility = remember { mutableStateOf(true) } OutlinedTextField( value = confirmPassword.value, onValueChange = { if (confirmPasswordErrorState.value) { confirmPasswordErrorState.value = false } confirmPassword.value = it }, modifier = Modifier.fillMaxWidth(), isError = confirmPasswordErrorState.value, label = { Text(text = "Confirm Password*") }, trailingIcon = { IconButton(onClick = { cPasswordVisibility.value = !cPasswordVisibility.value }) { Icon( imageVector = if (cPasswordVisibility.value) Icons.Default.VisibilityOff else Icons.Default.Visibility, contentDescription = "visibility", tint = Color.Red ) } }, visualTransformation = if (cPasswordVisibility.value) PasswordVisualTransformation() else VisualTransformation.None ) if (confirmPasswordErrorState.value) { val msg = if (confirmPassword.value.text.isEmpty()) { "Required" } else if (confirmPassword.value.text != password.value.text) { "Password not matching" } else { "" } Text(text = msg, color = Color.Red) } Spacer(Modifier.size(16.dp)) Button( onClick = { when { name.value.text.isEmpty() -> { nameErrorState.value = true } email.value.text.isEmpty() -> { emailErrorState.value = true } password.value.text.isEmpty() -> { passwordErrorState.value = true } confirmPassword.value.text.isEmpty() -> { confirmPasswordErrorState.value = true } confirmPassword.value.text != password.value.text -> { confirmPasswordErrorState.value = true } else -> { Toast.makeText( context, "Registered successfully", Toast.LENGTH_SHORT ).show() navController.navigate("login_screen") { popUpTo(navController.graph.startDestinationId) launchSingleTop = true } } } }, content = { Text(text = "Register", color = Color.White) }, modifier = Modifier.fillMaxWidth(), colors = ButtonDefaults.buttonColors(backgroundColor = Color.Red) ) Spacer(Modifier.size(16.dp)) Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { TextButton(onClick = { navController.navigate("login_screen") { popUpTo(navController.graph.startDestinationId) launchSingleTop = true } }) { Text(text = "Login", color = Color.Red) } } } }
|
Now we are ready with Login and registration pages. How we could navigate from one screen to other screen with Compose library.
There we have one component called Navigation component which will be used to navigation from one screen to other screen.
To work with navigation component we need to add below dependencies
implementation "androidx.navigation:navigation-compose:2.4.0-alpha06" |
The NavController of the Navigation component is statefull and it will keeps track of the back stack of screens in our app and the state of each screen.
We can create a NavController by using the rememberNavController() method in with composable
From official website
Each NavController is associated with a single NavHost composable. The NavHost links the NavController with a navigation graph that specifies the composable destinations that we should able to navigate between. When we navigate between composables, the content of the NavHost is automatically recomposed. Each composable destination in our navigation graph is associated with a route
Add below code in MainActivity.kt
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) setContent { MaterialTheme { // A surface container using the 'background' color from the theme Surface(color = MaterialTheme.colors.background) { LoginAndRegistration() } } } } } @Composable fun LoginAndRegistration(){ val navController = rememberNavController() NavHost(navController = navController, startDestination = "login_screen", builder = { composable("login_screen", content = { LoginScreen(navController = navController) }) composable("register_screen", content = { RegisterScreen(navController = navController) }) }) } |
Now we need to pass this navigation controller to our login and registration composable
@Composable fun LoginScreen(navController: NavController) |
@Composable fun RegistrationScreen(navController: NavController) |
Add onClick functionality for Register button in Login Screen
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { TextButton(onClick = { navController.navigate("register_screen") { popUpTo(navController.graph.startDestinationId) launchSingleTop = true } }) { Text(text = "Register ?", color = Color.Red) } } |
Add onClick functionality for Login button in Registration Screen
Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center ) { TextButton(onClick = { navController.navigate("login_screen") { popUpTo(navController.graph.startDestinationId) launchSingleTop = true } }) { Text(text = "Login", color = Color.Red) } } |
Complete code
package com.example.jetpack import android.os.Bundle import android.view.WindowManager import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Visibility import androidx.compose.material.icons.filled.VisibilityOff import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import com.example.jetpack.ui.theme.JetPackTheme import com.example.jetpack.widget.CheckBoxDemo class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) setContent { MaterialTheme { // A surface container using the 'background' color from the theme Surface(color = MaterialTheme.colors.background) { LoginAndRegistration() } } } } } @Composable fun LoginAndRegistration() { val navController = rememberNavController() NavHost(navController = navController, startDestination = "login_screen", builder = { composable("login_screen", content = { LoginScreen(navController = navController) }) composable( "register_screen", content = { RegistrationScreen(navController = navController) }) }) } @Composable fun LoginScreen(navController: NavController) { val context = LocalContext.current val email = remember { mutableStateOf(TextFieldValue()) } val emailErrorState = remember { mutableStateOf(false) } val passwordErrorState = remember { mutableStateOf(false) } val password = remember { mutableStateOf(TextFieldValue()) } Column( modifier = Modifier .fillMaxSize() .padding(16.dp), verticalArrangement = Arrangement.Center, ) { Text(text = buildAnnotatedString { withStyle(style = SpanStyle(color = Color.Red)) { append("S") } withStyle(style = SpanStyle(color = Color.Black)) { append("ign") } withStyle(style = SpanStyle(color = Color.Red)) { append(" I") } withStyle(style = SpanStyle(color = Color.Black)) { append("n") } }, fontSize = 30.sp) Spacer(Modifier.size(16.dp)) OutlinedTextField( value = email.value, onValueChange = { if (emailErrorState.value) { emailErrorState.value = false } email.value = it }, isError = emailErrorState.value, modifier = Modifier.fillMaxWidth(), label = { Text(text = "Enter Email*") }, ) if (emailErrorState.value) { Text(text = "Required", color = Color.Red) } Spacer(Modifier.size(16.dp)) val passwordVisibility = remember { mutableStateOf(true) } OutlinedTextField( value = password.value, onValueChange = { if (passwordErrorState.value) { passwordErrorState.value = false } password.value = it }, isError = passwordErrorState.value, modifier = Modifier.fillMaxWidth(), label = { Text(text = "Enter Password*") }, trailingIcon = { IconButton(onClick = { passwordVisibility.value = !passwordVisibility.value }) { Icon( imageVector = if (passwordVisibility.value) Icons.Default.VisibilityOff else Icons.Default.Visibility, contentDescription = "visibility", tint = Color.Red ) } }, visualTransformation = if (passwordVisibility.value) PasswordVisualTransformation() else VisualTransformation.None ) if (passwordErrorState.value) { Text(text = "Required", color = Color.Red) } Spacer(Modifier.size(16.dp)) Button( onClick = { when { email.value.text.isEmpty() -> { emailErrorState.value = true } password.value.text.isEmpty() -> { passwordErrorState.value = true } else -> { passwordErrorState.value = false emailErrorState.value = false Toast.makeText( context, "Logged in successfully", Toast.LENGTH_SHORT ).show() } } }, content = { Text(text = "Login", color = Color.White) }, modifier = Modifier.fillMaxWidth(), colors = ButtonDefaults.buttonColors(backgroundColor = Color.Red) ) Spacer(Modifier.size(16.dp)) Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { TextButton(onClick = { navController.navigate("register_screen") { popUpTo(navController.graph.startDestinationId) launchSingleTop = true } }) { Text(text = "Register ?", color = Color.Red) } } } } @Composable fun RegistrationScreen(navController: NavController) { val context = LocalContext.current val name = remember { mutableStateOf(TextFieldValue()) } val email = remember { mutableStateOf(TextFieldValue()) } val countryCode = remember { mutableStateOf(TextFieldValue()) } val mobileNo = remember { mutableStateOf(TextFieldValue()) } val password = remember { mutableStateOf(TextFieldValue()) } val confirmPassword = remember { mutableStateOf(TextFieldValue()) } val nameErrorState = remember { mutableStateOf(false) } val emailErrorState = remember { mutableStateOf(false) } val passwordErrorState = remember { mutableStateOf(false) } val confirmPasswordErrorState = remember { mutableStateOf(false) } val scrollState = rememberScrollState() Column( modifier = Modifier .fillMaxSize() .padding(16.dp) .verticalScroll(scrollState), verticalArrangement = Arrangement.Center, ) { Text(text = buildAnnotatedString { withStyle(style = SpanStyle(color = Color.Red)) { append("R") } withStyle(style = SpanStyle(color = Color.Black)) { append("egistration") } }, fontSize = 30.sp) Spacer(Modifier.size(16.dp)) OutlinedTextField( value = name.value, onValueChange = { if (nameErrorState.value) { nameErrorState.value = false } name.value = it }, modifier = Modifier.fillMaxWidth(), isError = nameErrorState.value, label = { Text(text = "Name*") }, ) if (nameErrorState.value) { Text(text = "Required", color = Color.Red) } Spacer(Modifier.size(16.dp)) OutlinedTextField( value = email.value, onValueChange = { if (emailErrorState.value) { emailErrorState.value = false } email.value = it }, modifier = Modifier.fillMaxWidth(), isError = emailErrorState.value, label = { Text(text = "Email*") }, ) if (emailErrorState.value) { Text(text = "Required", color = Color.Red) } Spacer(modifier = Modifier.size(16.dp)) Row() { OutlinedTextField( value = countryCode.value, onValueChange = { countryCode.value = it }, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, autoCorrect = false ), modifier = Modifier.fillMaxWidth(0.3f), label = { Text(text = "Code") }, ) Spacer(modifier = Modifier.size(16.dp)) OutlinedTextField( value = mobileNo.value, onValueChange = { mobileNo.value = it }, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Phone, autoCorrect = false ), label = { Text(text = "Mobile No") }, ) } Spacer(Modifier.size(16.dp)) val passwordVisibility = remember { mutableStateOf(true) } OutlinedTextField( value = password.value, onValueChange = { if (passwordErrorState.value) { passwordErrorState.value = false } password.value = it }, modifier = Modifier.fillMaxWidth(), label = { Text(text = "Password*") }, isError = passwordErrorState.value, trailingIcon = { IconButton(onClick = { passwordVisibility.value = !passwordVisibility.value }) { Icon( imageVector = if (passwordVisibility.value) Icons.Default.VisibilityOff else Icons.Default.Visibility, contentDescription = "visibility", tint = Color.Red ) } }, visualTransformation = if (passwordVisibility.value) PasswordVisualTransformation() else VisualTransformation.None ) if (passwordErrorState.value) { Text(text = "Required", color = Color.Red) } Spacer(Modifier.size(16.dp)) val cPasswordVisibility = remember { mutableStateOf(true) } OutlinedTextField( value = confirmPassword.value, onValueChange = { if (confirmPasswordErrorState.value) { confirmPasswordErrorState.value = false } confirmPassword.value = it }, modifier = Modifier.fillMaxWidth(), isError = confirmPasswordErrorState.value, label = { Text(text = "Confirm Password*") }, trailingIcon = { IconButton(onClick = { cPasswordVisibility.value = !cPasswordVisibility.value }) { Icon( imageVector = if (cPasswordVisibility.value) Icons.Default.VisibilityOff else Icons.Default.Visibility, contentDescription = "visibility", tint = Color.Red ) } }, visualTransformation = if (cPasswordVisibility.value) PasswordVisualTransformation() else VisualTransformation.None ) if (confirmPasswordErrorState.value) { val msg = if (confirmPassword.value.text.isEmpty()) { "Required" } else if (confirmPassword.value.text != password.value.text) { "Password not matching" } else { "" } Text(text = msg, color = Color.Red) } Spacer(Modifier.size(16.dp)) Button( onClick = { when { name.value.text.isEmpty() -> { nameErrorState.value = true } email.value.text.isEmpty() -> { emailErrorState.value = true } password.value.text.isEmpty() -> { passwordErrorState.value = true } confirmPassword.value.text.isEmpty() -> { confirmPasswordErrorState.value = true } confirmPassword.value.text != password.value.text -> { confirmPasswordErrorState.value = true } else -> { Toast.makeText( context, "Registered successfully", Toast.LENGTH_SHORT ).show() navController.navigate("login_screen") { popUpTo(navController.graph.startDestinationId) launchSingleTop = true } } } }, content = { Text(text = "Register", color = Color.White) }, modifier = Modifier.fillMaxWidth(), colors = ButtonDefaults.buttonColors(backgroundColor = Color.Red) ) Spacer(Modifier.size(16.dp)) Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { TextButton(onClick = { navController.navigate("login_screen") { popUpTo(navController.graph.startDestinationId) launchSingleTop = true } }) { Text(text = "Login", color = Color.Red) } } } } |
Now let's run application
Conclusion: In this jetpack compose tutorial we covered how to create Login and registration pages with compose and Navigation between screens by compose Navigation component.
Tags: Compose Login & Registration Page, Compose Navigation controller, How to navigate screens with compose, Compose Navigation controller
Article Contributed By :
|
|
|
|
7308 Views |