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.
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 Kitfor 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.