December 15, 2021 4:37 am

valokafor

In this post, I will use Android’s Jetpack Compose to create the UI of a sign-in and sign-up screen. For the dwindling number of Android developers who have not explored Jetpack Compose, it is Android’s modern toolkit for building native UI according to Google. Jetpack Compose pathway provides a structured introduction to Jetpack Compose and I encourage you to check it out if you are brand new to Compose as this post is ideal for people who have at least read the Compose basics tutorial.

Here is the Login screen UI we will create. In the next post, I will implement the actual login with Firebase Authentication.

With the traditional Android View approach, what you see in this screen can be created with:

  • An ImageView to add the logo
  • Two Textview for the app name and tagline
  • Two EditText boxes for the email and password
  • Another clickable text view for the forgot password text
  • A sign in button
  • And more TextViews for the privacy policy disclaimers.

Here is a sample implementation of the screen with an XML layout. Now, let’s create the same screen with JetPack Compose.

Step 1 – Create New Project

Create a new Android Studio project by selecting the Empty Compose Activity template naming it Pronto Login or whatever you like. Alternatively, you can add a new Activity to an existing project. Either way, you should start with a blank activity that looks like the one in the next code snippet.

Step 2 – Add SignInScreen

Add a Kotlin file named SignInScreen.kt and this is where we will add the composable function to display the login screen. In this File, add a composable method called SignIn(). Also, add a Preview method so you can view your progress as you build out the screen. Call this method from your MainActivity removing the Hello World call. Your Main Activity and the new SignInScreen should look like the following code snippet at the moment.


class MainActivity : ComponentActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContent {
           ProntoLoginTheme {
               // A surface container using the 'background' color from the theme
               Surface(color = MaterialTheme.colors.background) {
                   SignIn()
               }
           }
       }
   }
}

@Composable
fun SignIn() {

}

@Preview
@Composable
fun PreviewSignInScreen() {
   SignIn()
}

Step 3 – Remember User Input

A well-written composable function should be a pass-through entity. Meaning, it takes whatever you gave it and prints it to the screen. It should have no logic or the capability to manipulate its inputs. With the Android View system, the EditText both retains the user input, re-displays it to the user, and exposes a onTextChanged() callback for you to handle the user input. Jetpack Compose equivalent of EditText is TextField, and it provides a onValueChange() callback that passes back the user input for you to handle as you see fit.

If you want TextField to redisplay the user input as they type, you have to pass back the user input back to it and it will recompose itself to show it. This concept is called Recomposition. Add the following two lines of code to the SignIn method.

@Composable
fun SignIn() {
   var email by remember { mutableStateOf(TextFieldValue("")) }
   var password by remember { mutableStateOf(TextFieldValue("")) }
}

Step 4 – Add Scaffold

A Scaffold implements the basic material design visual layout structure. It can be likened to Bootstrap template for Android UI, it has placeholders known as slots for AppBar (Top and Botton), NavigationDrawer, and FloatingActionButton (FAB). Scaffold ensures that these components will be positioned and work together correctly. 

The design for this Login screen does not have an App Bar, FAB, etc so we will not utilize those slots in our Scaffold. Instead, the only content for our Scaffold will be a Box, and this Box in turn will only have one Column. The box could be likened to a Framelayout in a View and a Column column could be likened to a LinearLayout.

Scaffold(
   content = {
       Box {
           Column()
       }
   }
)

Step 5 – Add Modifiers to Column

Update the Column to add the following Modifiers, which will ensure that the layout takes up all the space, is scrollable, and is centered horizontally with 16 dp padding.

@Composable
fun SignIn() {
   var email by remember { mutableStateOf(TextFieldValue("")) }
   var password by remember { mutableStateOf(TextFieldValue("")) }

   Scaffold(
       content = {
           Box {
               Column(
                   modifier = Modifier
                       .fillMaxSize()
                       .verticalScroll(rememberScrollState())
                       .padding(all = 16.dp),
                   horizontalAlignment = Alignment.CenterHorizontally
               )
           }
       }
   )
}

Step 6 – Add Logo

From now on, we will just be describing or rather composing what we want to be displayed on the UI, and first in the list is the Logo. Download any free sample logo from the internet and place it in the drawable folder. Update the screen as shown below to display the logo. You should be able to build and see the logo display in the preview.

Step 7 – Update Color And Theme

The default Android Studio template comes with the Purple color theme, you can leave it as it is for this demo, however, if you want your finished UI to look like the demo app, then you should update your Color to match mine like so.

val Red200 = Color(0xFFEF9A9A)
val Red500 = Color(0xFFF44336)
val Red700 = Color(0xFFD32F2F)
val Blue200 = Color(0xFF90CAF9)

And then update the Dark and Light color to use your updated color like so.

private val DarkColorPalette = darkColors(
    primary = Red200,
    primaryVariant = Red700,
    secondary = Blue200
)

private val LightColorPalette = lightColors(
    primary = Red500,
    primaryVariant = Red700,
    secondary = Blue200
)

Step 8 – Add App Name and Tagline.

Text is Jetpack Compose equivalent of TextView in View and you can use Spacer to put horizontal or vertical spacing between two components. Add the next two lines below your logo image component. Add the string resource tag_line and ensure you can preview as shown below.

Text(
   text = stringResource(id = app_name),
   style = MaterialTheme.typography.h5,
   fontWeight = FontWeight.Bold,
   textAlign = TextAlign.Center,
   modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))

Text(
   text = stringResource(id = R.string.tag_line),
   style = MaterialTheme.typography.subtitle1,
   color = MaterialTheme.colors.onSurface,
   fontStyle = FontStyle.Italic
)

Step 9 – Add Email & Password Textfield

We will use OutlinedTextField to add the email and password field. Outlined text fields have less visual emphasis than filled text fields. The code snippet below shows how we can add an OutlinedTextview to our screen. Notice the use of value and onValueChange(). Here we are passing the email variables we added earlier. This was the value of the email is being updated and displayed at the same time.

OutlinedTextField(
                        value = email,
                        shape = RoundedCornerShape(16.dp),
                        colors = TextFieldDefaults.outlinedTextFieldColors(
                            focusedBorderColor = MaterialTheme.colors.primary,
                            unfocusedBorderColor = Color.Gray.copy(0.1f)
                        ),
                        textStyle = MaterialTheme.typography.body1,
                        modifier = Modifier.fillMaxWidth(),
                        label = {
                            Text(
                                text = stringResource(id = R.string.hint_enter_email),
                                style = MaterialTheme.typography.body1
                            )
                        },
                        placeholder = {
                            Text(
                                text = stringResource(id = R.string.hint_email),
                                style = MaterialTheme.typography.body1
                            )
                        },
                        onValueChange = {
                            email = it
                        }
                    )
 Spacer(Modifier.height(8.dp))

Add a similar block for the password OutlinedTextField like below, notice the visualTransformation property.

OutlinedTextField(
   value = password,
   shape = RoundedCornerShape(16.dp),
   colors = TextFieldDefaults.outlinedTextFieldColors(
       focusedBorderColor = MaterialTheme.colors.primary,
       unfocusedBorderColor = Color.Gray.copy(0.1f)
   ),
   visualTransformation = PasswordVisualTransformation(),
   leadingIcon = {
       Card(
           elevation = 1.dp,
           shape = RoundedCornerShape(8.dp),
           modifier = Modifier
               .width(50.dp)
               .height(50.dp)
               .padding(
                   bottom = 8.dp,
                   top = 8.dp,
                   start = 12.dp,
                   end = 8.dp
               ),
           content = {
               Icon(
                   imageVector = Icons.Filled.Lock,
                   contentDescription = null,
                   tint = MaterialTheme.colors.primary,
                   modifier = Modifier
                       .background(Color.Gray.copy(0.1f))
                       .size(18.dp)
                       .padding(8.dp)
               )
           }
       )
   },
   textStyle = MaterialTheme.typography.body1,
   modifier = Modifier
       .fillMaxWidth(),
   keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text),
   label = {
       Text(
           text = stringResource(id = R.string.hint_current_password),
           style = MaterialTheme.typography.body1
       )
   },
   placeholder = {
       Text(
           text = stringResource(id = R.string.hint_current_password),
           style = MaterialTheme.typography.body1
       )
   },
   onValueChange = {
       password = it
   }
)

Step 10 – Add Leading Icon

You can optionally add leading icons to the email and password TextFields as shown in the design. Leading icon is a property of OutlinedTextField and you can add one like this. Notice the use of a Card to elevate the Icon.

leadingIcon = {
   Card(
       elevation = 1.dp,
       shape = RoundedCornerShape(8.dp),
       modifier = Modifier
           .width(50.dp)
           .height(50.dp)
           .padding(
               bottom = 8.dp,
               top = 8.dp,
               start = 12.dp,
               end = 8.dp
           ),
       content = {
           Icon(
               imageVector = Icons.Filled.Email,
               contentDescription = null,
               tint = MaterialTheme.colors.primary,
               modifier = Modifier
                   .background(Color.Gray.copy(0.1f))
                   .size(18.dp)
                   .padding(8.dp)

           )
       }
   )
},

Step 10 – Add Forgot Password

Forgot password is just a Text that is aligned to the end.

Spacer(Modifier.height(16.dp))
Text(
   stringResource(id = R.string.forget_password),
   modifier = Modifier
       .fillMaxWidth(),
   style = MaterialTheme.typography.caption,
   textAlign = TextAlign.End
)

Step 11: Add Sign In Button

Compose also have a composable named button which is equivalent to Button in View. It exposes an onClick() callback that does what you would expect to do. It has properties that enable you to style the button without the need for drawable. Here is the button for this login screen.

Button(
   onClick = {},
   shape = RoundedCornerShape(24.dp),
   elevation = ButtonDefaults.elevation(),
   colors = ButtonDefaults.buttonColors(backgroundColor = MaterialTheme.colors.primary),
   enabled = true,
   modifier = Modifier
       .fillMaxWidth()
       .height(80.dp)
       .padding(
           start = 32.dp,
           end = 32.dp,
           top = 16.dp,
           bottom = 16.dp
       )
) {
   Text(
       modifier = Modifier.padding(
           start = 16.dp,
           top = 4.dp,
           bottom = 4.dp,
           end = 16.dp
       ),
       text = stringResource(id = R.string.sign_in),
       style = TextStyle(color = Color.White, fontSize = 16.sp)
   )
}

If you are following along, then at this point your preview screen should look similar to the next screenshot.

What follows after these are a few Text composable aligned vertically and horizontally to complete the login screen. You can see these in the complete source code for this post and once you complete the Sign In screen, you just need to duplicate it to create the Signup screen, you just need to add an additional Textfield.

Download the Source Code

About the Author

I am a Software Engineer & Entrepreneur. I create remarkable apps for my portfolio, employer and clients.