fbpx

React Native Plant App UI #6 : Login Screen

This tutorial is the sixth part of our React Native Plant App tutorial series. In the previous part, we successfully implemented the Terms of services Modal view and completed the overall UI sections of the Welcome screen. This tutorial is the continuation of the same tutorial from where we left off in the last part. So, it is recommended to go through the previous part in order to get insight and knowledge of the overall project.

In case of wanting to learn from the beginning, all the previous parts for this tutorial series are available below:

As mentioned in the previous parts, the motivation to work on this tutorial series came from React Native App Templates that provide a wide variety of mobile application templates written in React Native and powered by universal features and design. These app templates allow us to implement our own apps and even start our own startups. And, this sixth part is also the continuation of coding implementations and designs from the Youtube video tutorial by React UI Kit for the Plant App. The video tutorial delivers the coding implementation of the overall app very thoroughly. However, there is no verbal guidance for coding and implementation. Hence, this tutorial series is the implementation of the same coding style and designs in the form of the article. Thus, the learners can go through each step and take their time understanding the implementations.

Overview

In this sixth part of this tutorial series, we are going to implement the Login Screen with different UI sections. The Login screen is going to contain the text fields for email and password along with a button to login as well as the forgot password button. But first, we are going to configure the default Header for all the screens in our navigation file. Then, we will start implementing the different UI sections of the Login Screen.

So, let us begin!!  

 

Configuring default header in Navigation

Here, we are going to configure the default header in the index.js file of ‘./navigation/’ folder. We might have remembered that we have defined all the header style configs in the defaultNavigationOptions of our navigator. Now, we need to configure those header style options as shown in the code snippet below:

defaultNavigationOptions: {
    headerStyle: {},
    headerBackImage: <Image source={require('../assets/icons/back.png')} />,
    headerBackTitle: null,
    headerLeftContainerStyle: {},
    headerRightContainerStyle: {},
  }

Here, we have used the Image component in the headerBackImage option which enables us to set the back button image for each screen. Now in the Login.js file, we  need to remove the header null configuration in the navigationOptions as shown in the code snippet below:

static navigationOptions = {
 }

Hence, we will get the following result in our emulator screen: As we can see, we have got the back button in the header of the Login Screen.

Note that this back button will appear in all the screens present in our stack navigator unless custom header is implemented in the Screen file itself.

Adding styles

Now, we are going to configure some styles which are provided in the code snippet below:

defaultNavigationOptions: {
    headerStyle: {
      height: theme.sizes.base * 4,
      backgroundColor: theme.colors.white, // or 'white
      borderBottomColor: "transparent",
      elevation: 0, // for android only
    },
    headerBackImage: <Image source={require('../assets/icons/back.png')} />,
    headerBackTitle: null,
    headerLeftContainerStyle: {
      alignItems: 'center',
      marginLeft: theme.sizes.base,    //for iOS multiply the value by 2
      paddingRight: theme.sizes.base,
    },
    headerRightContainerStyle: {
      alignItems: 'center',
      paddingRight: theme.sizes.base,
    },
  }

Hence, we will get the following result in our emulator screen: As we can see, we have custom the custom-styled header with the back button.  

 

Implementing Login Screen

In this step, we are going to implement the different UI sections of the Login screen. The Login screen will contain input fields and buttons. The inputs will be of email and password. So, we need to define state variables for inputs as shown in the code snippet below:

state = {
    email: ‘’,
    password: ‘‘,
  }

Here, we have defined email and password state variables.  

Adding Email and Password Text Inputs

Here, we are going to add email and password text inputs. For that, we need to make the following imports to our Login.js screen:

import { theme } from '../constants';
import { Button, Block, Text, Input } from '../components';

Here, we have imported the theme from ‘./constants.’ folder and Input component from our predefined custom components. Now, we are going to make use of the Input component in the render() function of the Login.js file in order to add text inputs to the screen. For that, we need to make use of the code from the following code snippet:

render(){
    return (
      <Block padding={[0, theme.sizes.base * 2]}>
        <Text h1 bold>Login</Text>
        <Block middle>
          <Input 
            label="Email"
            style = {styles.input}
            defaultValue={this.state.email}
            onChangeText={text => this.setState({email : text})}
          />
          <Input 
  secure
            label="Password"
            style = {styles.input}
            defaultValue={this.state.password}
            onChangeText={text => this.setState({password : text})}
          />
        </Block>
      </Block>
      
    );
  }

Here, we have a Block component wrapping Text component and Input components. The Input components are configured with label, style and defaultValue props. Then, we are taking the input value and changing our state variables by using the onChangeText event. There are some styles configured to Input components as well which is provided in the code snippet below:

input: {
    borderRadius: 0,
    borderWidth: 0,
    borderBottomColor: theme.colors.gray2,
    borderBottomWidth: StyleSheet.hairlineWidth,
  },

Now, we are going to add valid email and password values. For that, we need to define two constants as shown in the code snippet below:

const VALID_EMAIL = "kriss @kriss.com";
const VALID_PASSWORD = "12345";

Now, we need to set these valid email and password constants to the email and password states as shown  in the code snippet below:

state = {
    email: VALID_EMAIL,
    password: VALID_PASSWORD,
  }

Hence, we will get the following result in our emulator screen:   As we can see, we have got the inputs as well as valid input values in the Login Screen. But we can see that the keyboard blocks the inputs when we trigger the keyboard to appear on the screen. So, we need to avoid the keyboard from blocking the inputs.  

Avoiding Keyboard

In order to avoid the keyboard from blocking the inputs or any other view elements, we need to make use of the KeyboardAvoidingView component from the react-native package. But first, we need to import this component into our Login.js screen:

import { StyleSheet, KeyboardAvoidingView } from 'react-native';

Now, we need to wrap the overall screen template in the render() function of Login screen with KeyboardAvoidingView component as shown in the code snippet below:

return (
      <KeyboardAvoidingView style={styles.login} behavior="padding">
        <Block padding={[0, theme.sizes.base * 2]}>
          <Text h1 bold>Login</Text>
          <Block middle>
            <Input 
              label="Email"
              style = {styles.input}
              defaultValue={this.state.email}
              onChangeText={text => this.setState({email : text})}
            />
            <Input 
              secure
              label="Password"
              style = {styles.input}
              defaultValue={this.state.password}
              onChangeText={text => this.setState({password : text})}
            />
          </Block>
        </Block>
      </KeyboardAvoidingView>
    );

Here, we have also added some style configurations and behavior props to the KeyboardAvoidingView component. the behavior prop is set to padding in order to show padding when the keyboard appears. The required style is provided in the code snippet below:

login: {
    flex: 1,
    justifyContent: 'center',
  },

Hence, we will get the following result in our emulator screen: As we can see, our input fields are shifted upwards when the keyboard appears to avoid its blocking.  

Adding Buttons

In this step, we are going to add buttons to the Login screen. We are going to add two buttons. One is the Login button and another is Forgot password button. This button will be placed below the input fields. In order to add buttons, we need to use the code from the following code snippet:

<Block middle>
    <Input 
      label="Email"
      style = {styles.input}
      defaultValue={this.state.email}
      onChangeText={text => this.setState({email : text})}
    />
    <Input 
      secure
      label="Password"
      style = {styles.input}
      defaultValue={this.state.password}
      onChangeText={text => this.setState({password : text})}
    />
    <Button gradient onPress={() => this.handleLogin()}>
        <Text bold white center>Login</Text>
    </Button>

    <Button onPress={() => {}}>
      <Text gray caption center style={{ textDecorationLine: 'underline' }}>
        Forgot your password?
      </Text>
    </Button>
  </Block>

Here, we have used the Button component. The Button component with some style props wraps the Text component in order to display text inside the button. Hence, we will get the following result in our emulator screen: As we can see, we have got the Login button with gradient style and Forgot password button in plain underlined text.  

Configuring the Login button

As we might have seen that, we have called the handleLogin() function in the onPress event of the Login button. But, we still have not defined any sort of function named handleLogin(). Here, we are going to defined and configure the handleLogin() function. But first, we need to define some extra states for errors and loading effects:

state = {
    email: VALID_EMAIL,
    password: VALID_PASSWORD,
    errors: [],
    loading: false,
  }

Here, we have defined errors state as array and loading state as false. The errors state will store the errors while logging in. The loading state will trigger some sort of button style to display logging in loader. Now, in the handleLogin() function, we need to configure the valid login and invalid login with errors. For that, we need to use the code from the following code snippet:

handleLogin() {
    const { navigation } = this.props;
    const { email, password } = this.state;
    const errors = [];

    this.setState({ loading: true });

    // check with backend API or with some static data
    if (email !== VALID_EMAIL) {
      errors.push('email');
    }
    if (password !== VALID_PASSWORD) {
      errors.push('password');
    }

    this.setState({ errors, loading: false });

    if (!errors.length) {
      navigation.navigate("Browse");
    }
  }

Here, we have imported the navigation prop as well as the states. When we press the Login button and call the handleLogin() function, the loading state is set to true to display the loading. Then, the email and password states are checked by comparing them to valid values. If the values are wrong then errors are pushed to errors array state. If the email and password are valid then the loading state is set to false and then navigated to Browse screen.

Handling errors

Now, we need to show some sort of message or style change when the wrong email or password is entered. First, we need to import the state variables in the render() function as shown in the code snippet below:

render(){
  const { navigation } = this.props;
  const { loading, errors } = this.state;
  const hasErrors = key => errors.includes(key) ? styles.hasErrors : null;

Here, we have imported the loading and errors states. Then, we have defined a new function called hasErrors() and it returns the value depending on the key value of the errors state array. Now, we are going to change the style of the particular input field in order to show the error. For that, we need to use code from the following code snippet:

return (
      <KeyboardAvoidingView style={styles.login} behavior="padding">
        <Block padding={[0, theme.sizes.base * 2]}>
          <Text h1 bold>Login</Text>
          <Block middle>
            <Input
              label="Email"
              error={hasErrors('email')}
              style={[styles.input, hasErrors('email')]}
              defaultValue={this.state.email}
              onChangeText={text => this.setState({ email: text })}
            />
            <Input
              secure
              label="Password"
              error={hasErrors('password')}
              style={[styles.input, hasErrors('password')]}
              defaultValue={this.state.password}
              onChangeText={text => this.setState({ password: text })}
            />
            <Button gradient onPress={() => this.handleLogin()}>
                <Text bold white center>Login</Text>
            </Button>

            <Button onPress={() => {}}>
              <Text gray caption center style={{ textDecorationLine: 'underline' }}>
                Forgot your password?
              </Text>
            </Button>
          </Block>
        </Block>
      </KeyboardAvoidingView>
    );

Here, we have added the error prop to both the Input components which call the hasError() function with specific parameters. Then, we have also added the error style which is provided1 in the code snippet below:

hasErrors: {
    borderBottomColor: theme.colors.accent,
  }

Hence, we will get the following result in the emulator screen: As we can see, the color of the input fields turns red when we enter the wrong credentials. Note that, the screen does not navigate to Browse Screen because we have commented it out in the index.js file of the navigation folder.  

Loading state configuration

Here, we are going to show the loader in the Login button when we click on the Login button. For that, we need to import the ActivityIndicator as shown in the code snippet below:

import { ActivityIndicator, Keyboard, KeyboardAvoidingView, StyleSheet } from 'react-native'

Now, we need to close the keyboard whenever we press the login button. For that, we need to use the dismiss() function of the Keyboard component as shown in the code snippet below:

handleLogin() {
    const { navigation } = this.props;
    const { email, password } = this.state;
    const errors = [];

    Keyboard.dismiss();

Now, we need to display the ActivityIndicator or Text component on the basis of the loading state in the Login button. For that, we need to use the following code inside the Button component for login button:

<Button gradient onPress={() => this.handleLogin()}>
   {loading ?
     <ActivityIndicator size="small" color="white" /> : 
     <Text bold white center>Login</Text>
   }
 </Button>

Now, in order to render the loader, we need to add delay to handleLogin() function. For that, we are going to use setTimeout function in our handleLogin() function as shown in the code snippet below:

handleLogin() {
    const { navigation } = this.props;
    const { email, password } = this.state;
    const errors = [];
    Keyboard.dismiss();
    this.setState({ loading: true });
    // check with backend API or with some static data
    setTimeout(() => {
      if (email !== VALID_EMAIL) {
        errors.push('email');
      }
      if (password !== VALID_PASSWORD) {
        errors.push('password');
      }
  
      this.setState({ errors, loading: false });
  
      if (!errors.length) {
        navigation.navigate("Browse");
      }
    }, 2000);
    
  }

Here, we have used the setTimeout function and delayed the outcome of pressing the Login button by 2 seconds. Hence, we will get the following result in our emulator screen: As we can see, a loader appears whenever we press the Login button.

However, we can remove the setTimeout function as it is used only to display the loading state. In the real scenario, when we are requesting some data from the server, it will automatically show up. With this, we have come to the end of this part of the tutorial.

Finally, we have successfully completed the implementation of the Login Screen in our React Native Plant UI App.  

Conclusion

This tutorial is the sixth part of the React Native Plant App UI tutorial series. In this part, we continued from where we left off in the fifth part of this tutorial series. In this part of the tutorial, we learned how to config a navigation file to set the default header with a custom back button. Then, we got stepwise guidance on how to use different custom components to implement the overall UI of the Login screen. Lastly, we also learned how to show the loader as well as handle the errors.

In the next part of this tutorial series, we are going to implement the overall UI of the Forgot Password screen in the Forgot.js file.

 

If you like this article post, please like it and share it. And, if you have any problem or issue, please comment below. Thanks!!

 

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Shares