Create a Booking App UIs Using React Native — UIs Completion (Part 4)

Upscalix
7 min readOct 20, 2023

--

Please read part 1, part 2, and part 3 before read this article.

Continuing to the fourth part of the article, in this section, we will proceed with the layout process for the remaining design.

The design we will implement in this article section is as follows.

  1. Layouting Order Form

We will break it down into 3 parts; the header, fields and submit section. Let’s open and edit the file PersonalInformation.js.

1a. Header Order Form

In the header section, there is a back button, page title and a plus (+) button, which will be used to add people.

Here is the code for the header section.

import React from 'react';
import {
SafeAreaView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';

import FontAwesome from 'react-native-vector-icons/FontAwesome';

const PersonalInformation = props => {
return (
<SafeAreaView style={styles.safeArea}>
<View style={styles.headerContainer}>
<View style={styles.headerRightContentContainer}>
<TouchableOpacity
onPress={props.navigation.goBack}
style={styles.backButton}>
<FontAwesome name="chevron-left" size={24} color="white" />
</TouchableOpacity>

<Text style={styles.headerText}>Personal Information</Text>
</View>

<TouchableOpacity>
<FontAwesome />
</TouchableOpacity>
</View>
</SafeAreaView>
);
};

const styles = StyleSheet.create({
safeArea: {
backgroundColor: 'mediumseagreen',
flex: 1,
},
headerContainer: {
alignItems: 'center',
flexDirection: 'row',
padding: 20,
},
headerRightContentContainer: {
flexDirection: 'row',
flex: 1,
},
backButton: {
alignItems: 'center',
justifyContent: 'center',
},
headerText: {
color: 'white',
fontSize: 28,
fontWeight: 'bold',
marginLeft: 20,
},
});

export default PersonalInformation;

1b. Fields Order Form

For the fields section, since it is dynamic and can have more than one data entry, we will use useFieldArray from react-hook-form. Let’s import the module first.

import {useFieldArray, useForm} from 'react-hook-form'; 

Then, declare the form.

const { 
control,
formState: {isValid},
} = useForm();

const {
fields,
append,
remove
} = useFieldArray({ control, name: 'list', });

Let’s add one state for numbering the IDs.

const [idNumbering, setIdNumbering] = useState(0); 

And when the page is initially active, let’s add one empty data entry.

useEffect(() => { 
append({
id: idNumbering,
firstName: '',
lastName: '',
identityType: '',
identityNumber: '',
phoneNumber: '',
});
}, [idNumbering, append]);

For the UI of the fields, we place them outside the header and inside a ScrollView. Then, we iterate through the data fields, and in each iteration, we return a view containing the person’s number, a delete button, input fields for first name, last name, identity type, identity number, and phone number.

<ScrollView
style={styles.scrollView}
contentContainerStyle={styles.scrollViewContentContainer}>
{fields.map((field, index) => {
return (
<View style={styles.formContainer} key={field.id}>
<View style={styles.formHeaderContainer}>
<Text style={styles.formTitle}>Person {index + 1}</Text>

<TouchableOpacity onPress={() => remove(index)}>
<FontAwesome name="trash" color="red" size={24} />
</TouchableOpacity>
</View>

<Input
control={control}
placeholder="First Name"
style={styles.textInput}
name={`list.${index}.firstName`}
required
/>

<Input
control={control}
placeholder="Last Name"
style={styles.textInput}
name={`list.${index}.lastName`}
required
/>

<Input
control={control}
placeholder="Identity Type"
style={styles.textInput}
name={`list.${index}.identityType`}
required
/>

<Input
control={control}
placeholder="Identity Number"
style={styles.textInput}
name={`list.${index}.identityNumber`}
required
/>

<Input
control={control}
placeholder="Phone Number"
style={styles.textInput}
name={`list.${index}.phoneNumber`}
required
/>
</View>
);
})}
</ScrollView>
scrollView: {
backgroundColor: 'white',
flex: 1,
},
scrollViewContentContainer: {
flexGrow: 1,
padding: 20,
},
formHeaderContainer: {
flexDirection: 'row',
},
formTitle: {
flex: 1,
fontSize: 20,
fontWeight: 'bold',
},
formContainer: {
padding: 20,
backgroundColor: 'gainsboro',
borderRadius: 10,
marginBottom: 20,
},
textInput: {
backgroundColor: 'white',
padding: 10,
marginTop: 20,
},

1c. Submit Section Order Form

For this section, it’s straightforward. We display two text elements and one button to navigate the user to the OrderSuccess page. We also set the button to be disabled when the form is not valid.

<View style={styles.submitSectionContainer}>
<View>
<Text style={styles.totalLabel}>Total</Text>

<Text style={styles.priceLabel}>${total}</Text>
</View>

<TouchableOpacity
activeOpacity={0.6}
disabled={!isValid}
style={{
...styles.submitButton,
backgroundColor: isValid ? 'goldenrod' : 'dimgray',
}}
onPress={() => props.navigation.replace('OrderSuccess')}>
<Text style={styles.submitButtonLabel}>Submit</Text>
</TouchableOpacity>
</View>
submitSectionContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
padding: 20,
alignItems: 'center',
},
totalLabel: {
color: 'black',
fontSize: 20,
fontWeight: 'bold',
},
priceLabel: {
color: 'white',
fontSize: 20,
fontWeight: 'bold',
marginTop: 10,
},
submitButton: {
paddingVertical: 10,
paddingHorizontal: 15,
borderRadius: 10,
},
submitButtonLabel: {
fontSize: 20,
color: 'white',
fontWeight: 'bold',
},

The OrderForm page is now complete, and finally, we add the onPress props to the plus button with the following code:

onPress={() => setIdNumbering(idNumbering + 1)} 

This is used to increment idNumbering. When the value of idNumbering changes, it triggers the code to append new data in the useEffect, creating a new field.

2. Layouting Order Success

Open the OrderSuccess.js file. For the OrderSuccess page, we will only display a text message followed by a button that, when pressed, will take the user to the MyOrders page.

import React from 'react';
import {
SafeAreaView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';

const OrderSuccess = props => {
return (
<SafeAreaView style={styles.safeArea}>
<View style={styles.container}>
<View style={styles.icon} />

<Text style={styles.text}>Order has been successfully placed!</Text>
</View>

<TouchableOpacity
activeOpacity={0.6}
onPress={() => props.navigation.navigate('MyOrders')}
style={styles.button}>
<Text style={styles.buttonLabel}>Go To My Orders</Text>
</TouchableOpacity>
</SafeAreaView>
);
};

const styles = StyleSheet.create({
safeArea: {
flex: 1,
},
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
icon: {
height: 80,
width: 80,
backgroundColor: 'mediumseagreen',
borderRadius: 10,
},
text: {
fontSize: 20,
margin: 20,
},
button: {
alignSelf: 'center',
backgroundColor: 'goldenrod',
paddingHorizontal: 20,
paddingVertical: 15,
borderRadius: 10,
margin: 20,
},
buttonLabel: {
fontWeight: 'bold',
color: 'white',
fontSize: 20,
},
});

export default OrderSuccess;

3. Layouting My Orders

Open the MyOrders.js file. Here, we will split it into two stages; the header and the list.

3a. My Orders Header

Here is the display code for the header of the MyOrders page.

import React from 'react';
import {
SafeAreaView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';

import FontAwesome from 'react-native-vector-icons/FontAwesome';

const MyOrders = props => {
return (
<SafeAreaView style={styles.safeArea}>
<View style={styles.container}>
<View style={styles.headerContainer}>
<Text style={styles.headerTitle}>My Orders</Text>

<View style={styles.headerNameContainer}>
<Text style={styles.headerName}>John Doe</Text>

<TouchableOpacity
activeOpacity={0.6}
onPress={() => props.navigation.replace('Login')}>
<FontAwesome name="sign-out" size={24} color="red" />
</TouchableOpacity>
</View>
</View>
</View>
</SafeAreaView>
);
};

const styles = StyleSheet.create({
safeArea: {
backgroundColor: 'mediumseagreen',
flex: 1,
},
container: {
backgroundColor: 'white',
flex: 1,
},
headerContainer: {
backgroundColor: 'mediumseagreen',
padding: 20,
},
headerTitle: {
fontSize: 20,
color: 'white',
fontWeight: 'bold',
},
headerNameContainer: {
flexDirection: 'row',
marginTop: 10,
},
headerName: {
flex: 1,
color: 'black',
fontSize: 20,
fontWeight: 'bold',
},
});

export default MyOrders;

3b. List My Orders

Let’s first prepare some dummy data to display the list of My Orders.

const orders = [
{
name: 'Padar Island',
numberOfPeople: 3,
priceTotal: 165,
},
{
name: 'Mount Bromo',
numberOfPeople: 2,
priceTotal: 80,
},
];

The code for the list view, which uses a ScrollView, is placed outside the header.

<ScrollView
style={styles.scrollView}
contentContainerStyle={styles.scrollViewContentContainer}>
{orders.map(order => {
return (
<TouchableOpacity
disabled
activeOpacity={0.6}
style={styles.orderItemButton}>
<Text style={styles.orderItemTitle}>{order}</Text>

<Text style={styles.orderItemSubtitle}>3 Persons</Text>

<Text style={styles.orderItemPrice}>$165</Text>
</TouchableOpacity>
);
})}
</ScrollView>
scrollView: {
flex: 1,
},
scrollViewContentContainer: {
paddingHorizontal: 20,
paddingTop: 20,
},
orderItemButton: {
backgroundColor: 'gainsboro',
borderRadius: 10,
marginBottom: 20,
padding: 20,
},
orderItemTitle: {
fontSize: 20,
color: 'black',
fontWeight: 'bold',
},
orderItemSubtitle: {
fontSize: 20,
color: 'black',
marginTop: 10,
},
orderItemPrice: {
alignSelf: 'flex-end',
fontSize: 20,
color: 'green',
marginTop: 50,
fontWeight: 'bold',
},

4. Bottom Tabs Icons

The final touch, because the Bottom Tabs still display default icons, let’s set the icons to the ones you want to use in the BottomTabs.js file.

First, import the FontAwesome module:

import FontAwesome from 'react-native-vector-icons/FontAwesome'; 

Then, add the title and tabBarIcon keys to the options props in each screen like this:

<BottomTabsComponent.Screen 
name="Home"
options={{
title: 'Home',
tabBarIcon: ({color}) => (
<FontAwesome name="home" size={24} color={color} />
),
}}
component={Home}
/>

<BottomTabsComponent.Screen
name="MyOrders"
options={{
title: 'My Orders',
tabBarIcon: ({color}) => (
<FontAwesome name="list" size={24} color={color} />
),
}}
component={MyOrders}
/>

So, when saved, the Bottom Tabs will change to look like this.

And that is how we create booking app UIs using React Native. To summarise, there are four steps that you can do:

  1. Project initiation
  2. Stack navigator & bottom tab navigator
  3. Layouting UIs
  4. UIs completion

What do you think about these articles series? Leave a comment below!

Interested in realising your dream app to help your business and your target market? Contact Upscalix now.

This article is written by Upscalix Senior Frontend and Mobile Developer, Nova.

--

--