DEV Community

Cover image for React Native Travel Article App UI Clone #3 : Info Section
kris
kris

Posted on • Updated on • Originally published at kriss.io

React Native Travel Article App UI Clone #3 : 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 first
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!!!

Affiliate Disclosure

Keep in mind that we may receive commissions when you click our links and make purchases. However, this does not impact our reviews and comparisons. We try our best to keep things fair and balanced, in order to help you make the best choice for you.

Top comments (0)