React Native Travel Article App UI Clone #3 : Destination Info Section
This tutorial is the third part of our React Native Travel Article App UI clone series. In the previous part, we successfully implemented a portion of the Destination Section with the scrolling/swiping transition. This tutorial is the continuation of where we left off in the last part. So, it is recommended to go through the previous parts in order to get the knowledge and insight into the overall project.
In case of wanting to learn from the beginning, all the parts for this tutorial series are available below:
As mentioned in the previous part, this tutorial series was inspired by the React Native Universal Listings App Template that consists of a wide variety of use cases, powered by universal features and design which enables us to implement our own apps to list anything that belongs in categories and can be added on a map. And, this third part is also the continuation of coding implementations and designs from the Youtube video tutorial by React UI Kit for the Travel Article App clone. The video tutorial delivers the overall coding using fast coding which can be difficult to grasp for any developer especially the beginners. This article tutorial provides the stepwise implementation which will be easier to understand and implement.
Overview
In this third part of this tutorial series, we are going to implement the remaining portion of the Destinations section which will include the Destination Info card along with active delimiter dots. The idea is to start by implementing the Destination Info Section. Then, we will position the Info Section properly. Then finally, we will add the delimiter dots at the bottom of the Destinations section. So, let us begin!!
Implementing Destination Info Card
Here, we are going to implement the add the Destination Info Card to the Destinations section in the renderDestination()
function. The Destinations Info card will only contain the Destination title and its description. For that, we need to use the code from the following code snippet:
<ImageBackground
style={[styles.flex, styles.destination]}
imageStyle = {{borderRadius : 12}}
source= {{uri : item.preview}}
>
<View style={[styles.row, {justifyContent: 'space-between'}]}>
<View style={{flex : 0}}>
<Image source={{uri: item.user.avatar}} style={styles.avatar}/>
</View>
<View style={[styles.column, {flex : 2, paddingHorizontal : 18}]}>
<Text style={{color : 'white', fontWeight : 'bold'}}>{item.user.name}</Text>
<Text style={{color : 'white'}}>{item.location}</Text>
</View>
<View style={{flex : 0, justifyContent : 'center', alignItems : 'flex-end'}}>
<Text style={styles.rating}>{item.rating}</Text>
</View>
</View>
<View style={[styles.column, styles.destinationInfo]}>
<Text style={{ fontSize: 24, fontWeight: '500', paddingBottom: 8, }}>
{item.title}
</Text>
<View style={[ styles.row, { justifyContent: 'space-between', alignItems: 'flex-end', }]}>
<Text>
{item.description.split('').slice(0, 50)}...
</Text>
</View>
</View>
</ImageBackground>
Here, we have added the second View
component inside the ImageBackground
component. This View
component wraps a Text
component to display the destination title and another View
component for the destination description. Here, we have clipped the description to 50 characters only in order to accommodate it into the Destination Info card. There are some styles bound to the added components as well which is provided in the code snippet below:
destination : {
width : width - (36 * 2),
marginHorizontal : 36,
paddingHorizontal : 36,
paddingVertical : 24,
borderRadius : 12,
backgroundColor : 'pink',
position : 'relative'
},
destinationInfo : {
position : 'absolute',
borderRadius : 12,
paddingHorizontal : 36,
paddingVertical : 24,
backgroundColor : 'white',
},
Hence, we will get the following result in our emulator screen: As we can see, we have got the Destination Info card over our background image in the Destinations section. Now we, need to position it properly in order to make it look appealing.
Configuring Info Card to a Proper position
Here, we are going to position the Destination Info card in an ideal position in the Destinations section. However, this Destination Info Card section will not be in the same position as in the actual app for this tutorial series. This is due to the difference in styling properties between the Android and iOS platforms. Since the implementation of this tutorial series is in the Android emulator, the position of the Destination Info Card will be different from that in the actual app. However, we will include the code for the iOS platform so that the Destination Info card will appear in the proper position as in the actual app in the iOS emulator.
We will learn more about it as we move forward with this part of the tutorial. Now, we are going to add some style properties as well as shadow styling to our Destination Info card in our Destination section. For that, we need to use the code from the following code snippet:
renderDestination(item){
return(
<ImageBackground
style={[styles.flex, styles.destination, styles.shadow]}
imageStyle = {{borderRadius : 12}}
source= {{uri : item.preview}}
>
<View style={[styles.row, {justifyContent: 'space-between'}]}>
<View style={{flex : 0}}>
<Image source={{uri: item.user.avatar}} style={styles.avatar}/>
</View>
<View style={[styles.column, {flex : 2, paddingHorizontal : 18}]}>
<Text style={{color : 'white', fontWeight : 'bold'}}>{item.user.name}</Text>
<Text style={{color : 'white'}}>{item.location}</Text>
</View>
<View style={{flex : 0, justifyContent : 'center', alignItems : 'flex-end'}}>
<Text style={styles.rating}>{item.rating}</Text>
</View>
</View>
<View style={[styles.column, styles.destinationInfo, styles.shadow]}>
<Text style={{ fontSize: 24, fontWeight: '500', paddingBottom: 8, }}>
{item.title}
</Text>
<View style={[ styles.row, { justifyContent: 'space-between', alignItems: 'flex-end', }]}>
<Text>
{item.description.split('').slice(0, 50)}...
</Text>
</View>
</View>
</ImageBackground>
)
}
The required styles, as well as some changes to predefined styles, are provided in the code snippet below:
destination : {
width : width - (36 * 2),
marginHorizontal : 36,
paddingHorizontal : 36,
paddingVertical : 24,
borderRadius : 12,
overflow : 'visible'
},
destinationInfo : {
position : 'absolute',
borderRadius : 12,
paddingHorizontal : 36,
paddingVertical : 16,
bottom : 5, //In iOS platform, bottom : -36
right : 36,
left : 36,
backgroundColor : 'white',
},
shadow : {
shadowColor: 'black',
shadowOffset: {
width: 0,
height: 6,
},
shadowOpacity: 0.05,
shadowRadius: 10,
elevation: 5,
}
As we can see, there some extra information in one of the style properties specifically for the iOS platform.
Changes for iOS platform
Now, we need to make some style configuration in the FlatList
component of the renderDestinations()
function. Note that this style configuration will only work on the iOS platform. Therefore, there will be no changes in the Android platform with the style configuration that will be done here. First, let us see the style configuration in the code snippet below:
<FlatList
horizontal
pagingEnabled
scrollEnabled
showsHorizontalScrollIndicator = {false}
scrollEventThrottle = {16}
snapToAlignment = "center"
// style={{ overflow : 'visible' }} //In IOS platform
data = {destinations}
keyExtractor = {(item, index)=> `${item.id}`}
renderItem = {({item}) => this.renderDestination(item)}
/>
Important Fact:
As we can see, we have included the style prop with overflow
style property which is set to ‘visible’. Moreover, this prop is actually commented out. This is because this overflow
visible style property does not seem to work in the Android platform. But rest assured that this property will work on the iOS platform.
Based on some research work for this issue, there seems to be a problem with overflow
visible property in the Android platform. By adding this property, the actual Destination Info card should have appeared as hanging over and from the bottom of the background image in the Destinations section. But since this property does not work in the Android platform, we will have to place the Destination Info card above the image background but not hanging. Hence, we will get the following result in our Android emulator screen:
As we can see, the Destination Info card appears over the background image in our Destinations section. In the actual app, this Info card should be hanging from the bottom of the background image. However, if we decide to build this app in the iOS platform and apply the above commented out style properties then, this Info card’s position will appear like that in the actual app.
We can achieve this in the Android platform using a third-party package like react-native-view-overflow. But this package does not have support for the expo client. If we build this using the React Native CLI then this package might work. So, for now, we going to settle for having the Destination Info card over the background image as shown in the Android emulator screenshot above.
Adding Delimiter for each Destination
Here, we are going to add the delimiter dots at the bottom of the destination cards. Since there are four destination cards, we are going to add four delimiter dots. For that, we need to use create a new function called renderDots().
And, its implementation is provided in the following code snippet:
renderDots(){
return(
<View
style={
[
styles.flex,
styles.row,
{justifyContent: 'center',alignItems:'center', marginTop : Platform.OS === 'ios' ? 36 * 2 : 48}]}>
{destinations.map((item, index) => {
return (
<View
key={`step-${item.id}`}
style={[styles.dots, item.id === 1 ? styles.activeDot : null ]}
/>
)
})}
</View>
)
}
This renderDots()
function returns the template for the delimiter dots. Here, we have added a parent View
component that wraps the iteration of View
component for each item in the destinations
data array. We have used the map()
array function to iterate through each item in the destinations
array data. This allows us to define the number of dots as per the number of items
in the destinations
array data. The map()
function returns the View
component to render delimiter dots for each item. Here, we have included some styles to render the dots which is provided in the code snippet below:
dots: {
width: 10,
height: 10,
borderWidth: 2.5,
borderRadius: 5,
marginHorizontal: 6,
backgroundColor: '#DCE0E9',
borderColor: 'transparent',
},
activeDot: {
width: 12.5,
height: 12.5,
borderRadius: 6.25,
borderColor: '#007BFA',
}
Configuration in the Destinations section
Now, we need to call this renderDots()
function in the renderDestinations()
function as shown in the code snippet below:
<View style={[ styles.flex, styles.column, styles.destinations]}>
<FlatList
horizontal
pagingEnabled
scrollEnabled
showsHorizontalScrollIndicator = {false}
scrollEventThrottle = {16}
snapToAlignment = "center"
// style={{ overflow : 'visible' }} //In IOS platform
data = {destinations}
keyExtractor = {(item, index)=> `${item.id}`}
renderItem = {({item}) => this.renderDestination(item)}
/>
{this.renderDots()}
</View>
Then, we also need to make some style changes in order to position the delimiter dots properly. The required style changes are provided in the code snippet below:
destinations : {
flex: 2,
justifyContent: 'space-between',
paddingBottom: 30,
},
destination : {
width : width - (36 * 2),
height : width * 0.66,
marginHorizontal : 36,
paddingHorizontal : 36,
paddingVertical : 20,
borderRadius : 12,
},
destinationInfo : {
position : 'absolute',
borderRadius : 12,
paddingHorizontal : 36,
paddingVertical : 16,
bottom : 20, //In iOS platform, bottom : -36
left : 36,
right : 36,
backgroundColor : 'white',
},
Next, we need to add the ScrollView
component to the render()
function of our List.js screen as well. This will make the whole screen scrollable vertically.
render(){
return (
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={{ paddingBottom: 36}}
>
{this.renderDestinations()}
{this.renderRecommended()}
</ScrollView>
);
}
Here, we have added the showsVerticalScrollIndicator
prop which is set as false
to hide the vertical scroll bar. Hence, we will get the following result in our Android emulator screen:
As we can see, we have got the delimiter dots at the bottom of the Destinations section. We will make it animate in accordance with the scrolling of the Destinations section in the upcoming tutorials. For now, we will end this part of the tutorial here.
Hence, we have successfully implemented the Destination Info card section as well as the delimiter dots in the List screen of our React Native Travel Article app UI clone.
Conclusion
This tutorial is the third part of the React Native Travel Article App UI clone tutorial series. In this part, we continued from where we left off in the second part of this tutorial series. In this part of the tutorial, we learned how to implement the Destination Info card section and position it over the background image. We also got detail information on the difference caused by overflow style property in the case of the Android and iOS platforms. Then finally, we learned how to implement the delimiter dots at the bottom of the Destinations section.
In the next part of this tutorial series, we are going to implement the Recommendation section on our List screen.
So, Stay Tuned folks!!!
If you like this article post, please like it and share it. And, if you have any problem or issue, please comment below. Thanks!!
Great app