Cloning Tinder Using React Native Elements and Expo — SitePoint

October 01, 2019 0 Comments

Cloning Tinder Using React Native Elements and Expo — SitePoint

 

 

Making pixel-perfect layouts on mobile is hard. Even though React Native makes it easier than its native counterparts, it still requires a lot of work to get a mobile app to perfection.

In this tutorial, we’ll be cloning the most famous dating app, Tinder. We’ll then learn about a UI framework called React Native Elements, which makes styling React Native apps easy.

Since this is just going to be a layout tutorial, we’ll be using Expo, as it makes setting things up much easier than plain old react-native-cli. We’ll also be making use of a lot of dummy data to make our app.

We’ll be making a total of four screens—Home, Top Picks, Profile, and Messages.

Want to learn React Native from the ground up? This article is an extract from our Premium library. Get an entire collection of React Native books covering fundamentals, projects, tips and tools & more with SitePoint Premium. Join now for just $9/month.

Prerequisites

For this tutorial, you need a basic knowledge of React Native and some familiarity with Expo. You’ll also need the Expo client installed on your mobile device or a compatible simulator installed on your computer. Instructions on how to do this can be found here.

You also need to have a basic knowledge of styles in React Native. Styles in React Native are basically an abstraction similar to that of CSS, with just a few differences. You can get a list of all the properties in the styling cheatsheet.

Throughout the course of this tutorial we’ll be using yarn. If you don’t have yarn already installed, install it from here.

Also make sure you’ve already installed expo-cli on your computer.

If it’s not installed already, then go ahead and install it:

$ yarn global add expo-cli 

To make sure we’re on the same page, these are the versions used in this tutorial:

  • Node 11.14.0

  • npm 6.4.1

  • yarn 1.15.2

  • expo 2.16.1

Make sure to update expo-cli if you haven’t updated in a while, since expo releases are quickly out of date.

We’re going to build something that looks like this:

Tinder Demo in Expo

If you just want to clone the repo, the whole code can be found on GitHub.

Getting Started

Let’s set up a new Expo project using expo-cli:

$ expo init expo-tinder 

It will then ask you to choose a template. You should choose tabs and hit Enter.

Expo Init - Choose A Template

Then it will ask you to name the project. Type expo-tinder and hit Enter again.

Expo Init - Name the Project

Lastly, it will ask you to press y to install dependencies with yarn or n to install dependencies with npm. Press y.

Expo Init - Install the dependencies

This bootstraps a brand new React Native app using expo-cli.

React Native Elements

React Native Elements is a cross-platform UI Toolkit for React Native with consistent design across Android, iOS and Web.

It’s easy to use and completely built with JavaScript. It’s also the first UI kit ever made for React Native.

It allows us to fully customize styles of any of our components the way we want so every app has its own unique look and feel.

It’s also open source and backed by a community of awesome developers.

You can build beautiful applications easily.

React Native Elements Demo

Cloning Tinder UI

We’ve already created a project named expo-tinder.

To run the project, type this:

$ yarn start 

Press i to run the iOS Simulator. This will automatically run the iOS Simulator even if it’s not opened.

Press a to run the Android Emulator. Note that the emulator must be installed and started already before typing a. Otherwise it will throw an error in the terminal.

It should look like this:

Expo Tabs App

The initial setup has already installed react-navigation for us. The bottom tab navigation also works by default because we chose tabs in the second step of expo init. You can check it by tapping on Links and Settings.

The screens/ folder is responsible for the content displayed when the tabs are changed.

Now, completely remove the contents of HomeScreen.js and replace them with the following:

import React from 'react' import { Text, View } from 'react-native' class HomeScreen extends React.Component { render() { return ( <View> <Text>Home Screen</Text> </View> ) } } export default HomeScreen 

You should see the updated UI now:

The empty Home screen

Now we’ll adapt the tabs according to the application we’re going to build. For our Tinder clone, we’re going to have four screens: Home, Top Picks, Profile, and Messages.

We can completely delete LinksScreen.js and SettingsScreen.js from the screens/ folder. Notice our app breaks, with a red screen full of errors.

This is because we’ve linked to it in the navigation/ folder. Open MainTabNavigator.js in the navigation/ folder. It currently looks like this:

import React from 'react'; import { Platform } from 'react-native'; import { createStackNavigator, createBottomTabNavigator } from 'react-navigation'; import TabBarIcon from '../components/TabBarIcon'; import HomeScreen from '../screens/HomeScreen'; import LinksScreen from '../screens/LinksScreen'; import SettingsScreen from '../screens/SettingsScreen'; const HomeStack = createStackNavigator({ Home: HomeScreen, }); HomeStack.navigationOptions = { tabBarLabel: 'Home', tabBarIcon: ({ focused }) => ( <TabBarIcon focused={focused} name={ Platform.OS === 'ios' ? `ios-information-circle${focused ? '' : '-outline'}` : 'md-information-circle' } /> ), }; const LinksStack = createStackNavigator({ Links: LinksScreen, }); LinksStack.navigationOptions = { tabBarLabel: 'Links', tabBarIcon: ({ focused }) => ( <TabBarIcon focused={focused} name={Platform.OS === 'ios' ? 'ios-link' : 'md-link'} /> ), }; const SettingsStack = createStackNavigator({ Settings: SettingsScreen, }); SettingsStack.navigationOptions = { tabBarLabel: 'Settings', tabBarIcon: ({ focused }) => ( <TabBarIcon focused={focused} name={Platform.OS === 'ios' ? 'ios-options' : 'md-options'} /> ), }; export default createBottomTabNavigator({ HomeStack, LinksStack, SettingsStack, }); 

Remove references to LinksStack and SettingsStack completely, because we don’t need these screens in our app. It should look like this:

import React from 'react' import { Platform } from 'react-native' import { createBottomTabNavigator, createStackNavigator, } from 'react-navigation' import TabBarIcon from '../components/TabBarIcon' import HomeScreen from '../screens/HomeScreen' const HomeStack = createStackNavigator({ Home: HomeScreen, }) HomeStack.navigationOptions = { tabBarLabel: 'Home', tabBarIcon: ({ focused }) => ( <TabBarIcon focused={focused} name={ Platform.OS === 'ios' ? `ios-information-circle${focused ? '' : '-outline'}` : 'md-information-circle' } /> ), } export default createBottomTabNavigator({ HomeStack, }) 

Go ahead and create TopPicksScreen.js, ProfileScreen.js and MessagesScreen.js inside the screens/ folder.

Add the following inside TopPicksScreen.js:

import React from 'react' import { Text, View } from 'react-native' class TopPicksScreen extends React.Component { render() { return ( <View> <Text>Top Picks Screen</Text> </View> ) } } export default TopPicksScreen 

Add the following inside ProfileScreen.js:

import React from 'react' import { Text, View } from 'react-native' class ProfileScreen extends React.Component { render() { return ( <View> <Text>Profile Screen</Text> </View> ) } } export default ProfileScreen 

Add the following inside MessagesScreen.js:

import React from 'react' import { Text, View } from 'react-native' class MessagesScreen extends React.Component { render() { return ( <View> <Text>Messages Screen</Text> </View> ) } } export default MessagesScreen 

Let’s go ahead and change components/TabBarIcon.js, since we’ll be needing custom icons on our bottom tab navigation. It currently looks like this:

import React from 'react'; import { Icon } from 'expo'; import Colors from '../constants/Colors'; export default class TabBarIcon extends React.Component { render() { return ( <Icon.Ionicons name={this.props.name} size={26} style={{ marginBottom: -3 }} color={this.props.focused ? Colors.tabIconSelected : Colors.tabIconDefault} /> ); } } 

The only thing we’re doing here is adding an Icon prop so we can have different types of Icon instead of just Ionicons. Currently, the different supported types are AntDesign, Entypo, EvilIcons, Feather, FontAwesome, FontAwesome5, FontAwesome5Brands, Foundation, Ionicons, MaterialCommunityIcons, MaterialIcons, SimpleLineIcons, Octicons and Zocial.

You can choose a variety of different icons from the @expo/vector-icons directory. It adds a compatibility layer around @oblador/react-native-vector-icons to work with the Expo asset system.

TabBarIcon.js should now look like this:

import React from 'react' import Colors from '../constants/Colors' export default class TabBarIcon extends React.Component { render() { const { Icon, name, focused } = this.props return ( <Icon name={name} size={26} style={{ marginBottom: -3 }} color={focused ? Colors.tabIconSelected : Colors.tabIconDefault} /> ) } } 

Now we can pass the Icon prop to the above TabBarIcon component to load different icons.

We need to change the implementation of HomeStack in the MainTabNavigator.js folder to incorporate with the new TabBarIcon component’s Icon prop.

Change the HomeStack variable implementation to this:

import { Icon } from 'expo' const HomeStack = createStackNavigator({ Home: HomeScreen, }) HomeStack.navigationOptions = { tabBarLabel: 'Home', tabBarIcon: ({ focused }) => ( <TabBarIcon Icon={Icon.MaterialCommunityIcons} focused={focused} name="fire" /> ), } 

The only change here is the addition of Icon={Icon.MaterialCommunityIcons}, since we changed the implementation of TabBarIcon to accept the icon source so we can use different types of icons from different providers.

Now these icons need to be loaded first. Otherwise, we’ll see a flash of empty screen before the icons show up. For that, we need to change App.js by adding the following:

Font.loadAsync({ // This is the font that we're using for our tab bar ...Icon.MaterialIcons.font, ...Icon.MaterialCommunityIcons.font, ...Icon.FontAwesome.font, ...Icon.Feather.font, }), 

These font types are used at some points in our application. That’s why we’ve included only four fonts. For example, MaterialCommunityIcons is used in the HomeStack variable in the MainTabNavigator.js file, as shown above.

We’ll also be hiding our StatusBar in App.js with this:

<StatusBar hidden /> 

We’ll also replace the assets used in App.js:

Asset.loadAsync([ require('./assets/images/splash.png'), require('./assets/images/icon.png'), ]), 

The App.js file should now look like this:

import { AppLoading, Asset, Font, Icon } from 'expo' import React from 'react' import { StatusBar, StyleSheet, View } from 'react-native' import AppNavigator from './navigation/AppNavigator' export default class App extends React.Component { state = { isLoadingComplete: false, } render() { if (!this.state.isLoadingComplete && !this.props.skipLoadingScreen) { return ( <AppLoading startAsync={this._loadResourcesAsync} onError={this._handleLoadingError} onFinish={this._handleFinishLoading} /> ) } else { return ( <View style={styles.container}> <StatusBar hidden /> <AppNavigator /> </View> ) } } _loadResourcesAsync = async () => { return Promise.all([ Asset.loadAsync([ require('./assets/images/splash.png'), require('./assets/images/icon.png'), ]), Font.loadAsync({ // This is the font we're using for our tab bar ...Icon.MaterialIcons.font, ...Icon.MaterialCommunityIcons.font, ...Icon.FontAwesome.font, ...Icon.Feather.font, }), ]) } _handleLoadingError = error => { // In this case, you might want to report the error to your error // reporting service, such as Sentry console.warn(error) } _handleFinishLoading = () => { this.setState({ isLoadingComplete: true }) } } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', }, }) 

We also need to link all of the above screens—TopPicksScreen.js, ProfileScreen.js and MessagesScreen.js—inside screens/ in MainTabNavigator.js inside the navigation/ folder, as shown in the following flowchart:

Flowchart Demo

Also add the following in MainTabNavigator.js:

import MessagesScreen from '../screens/MessagesScreen' import ProfileScreen from '../screens/ProfileScreen' import TopPicksScreen from '../screens/TopPicksScreen' const TopPicksStack = createStackNavigator({ TopPicks: TopPicksScreen, }) TopPicksStack.navigationOptions = { tabBarLabel: 'TopPicks', tabBarIcon: ({ focused }) => ( <TabBarIcon Icon={Icon.FontAwesome} focused={focused} name="diamond" /> ), } const MessagesStack = createStackNavigator({ Messages: MessagesScreen, }) MessagesStack.navigationOptions = { tabBarLabel: 'Messages', tabBarIcon: ({ focused }) => ( <TabBarIcon Icon={Icon.FontAwesome} focused={focused} name="commenting-o" /> ), } const ProfileStack = createStackNavigator({ Profile: ProfileScreen, }) ProfileStack.navigationOptions = { tabBarLabel: 'Profile', tabBarIcon: ({ focused }) => ( <TabBarIcon Icon={Icon.Feather} focused={focused} name="user" /> ), } 

The above code creates three stack navigators—TopPicksStack, MessagesStack and ProfileStack. The static property navigationOptions lets us add our own label and icon to the bottom tab.

Also, change createBottomTabNavigator to make sure TopPicksStack, MessagesStack and ProfileStack show up in the bottom tab navigation:

export default createBottomTabNavigator({ HomeStack, TopPicksStack, MessagesStack, ProfileStack, }) 

Now you should be able to see different icons in the bottom tab navigation with different screens as follows:

Different icons in the bottom tab navigation

We now need to get rid of the header that’s showing on each screen, taking up some top space. To get rid of it, we need to add headerMode: 'none' in the createStackNavigator config.

We need to add it on HomeStack, TopPicksStack, MessagesStack and ProfileStack.

HomeStack should look like this:

const HomeStack = createStackNavigator( { Home: HomeScreen, }, { headerMode: 'none', }, ) 

Do the same for the rest of them. Now if you check, the text goes up to the top left, right above the clock.

Different icons in the bottom tab navigation

There’s an easy fix for this. We need to use SafeAreaView. SafeAreaView renders content within the safe area boundaries of a device. Let’s go into the screens/ directory and change HomeScreen.js to use SafeAreaView, so that it looks like this:

import React from 'react' import { SafeAreaView, Text } from 'react-native' class HomeScreen extends React.Component { render() { return ( <SafeAreaView> <Text>Home Screen</Text> </SafeAreaView> ) } } export default HomeScreen 

It now renders the content inside the boundaries of the device.

SafeAreaView added to the app

Go ahead and change the rest of them to do the same.

It’s repetitive to wrap SafeAreaView inside every component instead of setting it up on a root component like App.js. But be aware that this won’t work if you try doing it on App.js.

Remember, SafeAreaView should always be set up on screen components or any content in them, and not wrap entire navigators. You can read more about it on this blog post.

Screens

Now that our navigation is taken care of, we can start working on the layout.

We’re going to be using a UI toolkit called React Native Elements, so go ahead and install it:

$ yarn add react-native-elements 

Before starting anything, make sure to copy the assets/ directory from the GitHub repo entirely for dummy images.

Now we’ll start working on the Home screen.

Home Screen

Before starting to work on HomeScreen.js, let’s delete unnecessary files. Go to the components/ folder and delete StyledText.js and the __tests__ folder.

Now let’s start working on our Home screen.

Firstly, create Card.js in the components/ folder. We’re going to display a profile card with the person’s name, their age and how far away they live.

We’re going to use a Tile component from react-native-elements to display our User Card.

The Tile component from react-native-elements looks like this:

Tile component from react-native-elements

import React from 'react' import { Platform, StyleSheet } from 'react-native' import { Tile } from 'react-native-elements' import Layout from '../constants/Layout' const BOTTOM_BAR_HEIGHT = !Platform.isPad ? 29 : 49 // found from https://stackoverflow.com/a/50318831/6141587 export const Card = ({ pic, title, caption }) => ( <Tile imageSrc={pic} imageContainerStyle={styles.imageContainer} activeOpacity={0.9} title={title} titleStyle={styles.title} caption={caption} captionStyle={styles.caption} containerStyle={styles.container} featured /> ) const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', }, imageContainer: { width: Layout.window.width - 30, height: Layout.window.height - BOTTOM_BAR_HEIGHT * 6, borderRadius: 20, overflow: 'hidden', // this does magic }, title: { position: 'absolute', left: 10, bottom: 30, }, caption: { position: 'absolute', left: 10, bottom: 10, }, }) 

The Card component takes pic, title and caption, which in turn are passed on to the Tile component.

The Tile component has some additional properties. activeOpacity is a number passed to control opacity on pressing the Tile, which is optional, but the default value is 0.2, which makes it look transparent on press, so we pass a value close to 1 to keep it opaque. The featured prop changes the look of Tile. It keeps the text in title and a caption prop on the image rather than below when featured is not specified or is set to false.

The rest are styles applied to get the user card right. The container style centers the user card. imageContainer has a width and a height. The width is set to the total width of the device—30dp (device pixels)—and the height is set to the total height of the device—BOTTOM_BAR_HEIGHT * 6.

We get the BOTTOM_BAR_HEIGHT from stackoverflow.com.

We get the device width from the constants/Layout.js file, which basically contains the following:

import { Dimensions } from 'react-native' const width = Dimensions.get('window').width const height = Dimensions.get('window').height export default { window: { width, height, }, isSmallDevice: width < 375, } 

Then we add a border radius to the image. But the border radius won’t be applied. We also need overflow: hidden to make it work.

Then we position our title and caption to use absolute positioning and make them appear on the bottom-left corner, just above the image.

Next, create a utils/shuffleArray.js file and paste the following into it:

// found at https://stackoverflow.com/a/46545530/6141587 const shuffleArray = array => array .map(a => ({ sort: Math.random(), value: a })) .sort((a, b) => a.sort - b.sort) .map(a => a.value) export default shuffleArray 

This makes sure our array is randomized every time.

Now create a constants/Pics.js file and paste in the following:

import shuffleArray from '../utils/shuffleArray' export const HomeScreenPics = shuffleArray([ { pic: require('../assets/images/women/women1.jpg'), title: 'Amelia, 27', caption: '16 miles away', }, { pic: require('../assets/images/women/women2.jpg'), title: 'Joanna, 19', caption: '2 miles away', }, { pic: require('../assets/images/women/women3.jpg'), title: 'Charlie, 32', caption: '24 miles away', }, { pic: require('../assets/images/women/women4.jpg'), title: 'Mary, 23', caption: '45 miles away', }, { pic: require('../assets/images/women/women5.jpg'), title: 'Lucy, 27', caption: '32 miles away', }, { pic: require('../assets/images/women/women6.jpg'), title: 'Rachel, 29', caption: '30 miles away', }, { pic: require('../assets/images/women/women7.jpg'), title: 'Ava, 31', caption: '14 miles away', }, { pic: require('../assets/images/women/women8.jpg'), title: 'Monica, 35', caption: '19 miles away', }, { pic: require('../assets/images/women/women9.jpg'), title: 'Lisa, 25', caption: '7 miles away', }, { pic: require('../assets/images/women/women10.jpg'), title: 'Julia, 22', caption: '9 miles away', }, { pic: require('../assets/images/men/men1.jpg'), title: 'Aaron, 24', caption: '3 miles away', }, { pic: require('../assets/images/men/men2.jpg'), title: 'Novak, 27', caption: '12 miles away', }, { pic: require('../assets/images/men/men3.jpg'), title: 'Justin, 32', caption: '20 miles away', }, { pic: require('../assets/images/men/men4.jpg'), title: 'Tony, 21', caption: '4 miles away', }, { pic: require('../assets/images/men/men5.jpg'), title: 'Leo, 30', caption: '22 miles away', }, { pic: require('../assets/images/men/men6.jpg'), title: 'Ronald, 39', caption: '35 miles away', }, { pic: require('../assets/images/men/men7.jpg'), title: 'Johnny, 41', caption: '44 miles away', }, { pic: require('../assets/images/men/men8.jpg'), title: 'Chandler, 35', caption: '29 miles away', }, { pic: require('../assets/images/men/men9.jpg'), title: 'Joey, 29', caption: '17 miles away', }, { pic: require('../assets/images/men/men10.jpg'), title: 'Alfie, 37', caption: '27 miles away', }, ]) 

This contains all the images required for our app. Notice every time we call shuffleArray to randomize our array.

Let’s install react-native-deck-swiper to make sure our cards get swiped like Tinder. The latest version (v1.6.7 at the time of writing) uses react-native-view-overflow, which doesn’t support Expo. Hence, we’re going to install v1.5.25:

$ yarn add react-native-deck-swiper@1.5.25 

Now go into the HomeScreen.js file and paste the following:

import React from 'react' import { SafeAreaView, StyleSheet } from 'react-native' import Swiper from 'react-native-deck-swiper' import { Card } from '../components/Card' import { HomeScreenPics } from '../constants/Pics' class HomeScreen extends React.Component { render() { return ( <SafeAreaView style={styles.container}> <Swiper cards={HomeScreenPics} renderCard={Card} infinite // keep looping cards infinitely backgroundColor="white" cardHorizontalMargin={0} stackSize={2} // number of cards shown in background /> </SafeAreaView> ) } } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: 'transparent', }, }) export default HomeScreen 

Now our cards are swipable, and our home screen looks like this:

Home Screen Demo

Try swiping now, and it should work as follows:

If you want to learn how to make these kinds of Tinder Swipe animations, you should check out Varun Nath’s Tinder Swipe Series on YouTube.

Now that our Home screen is finished, let’s build the Top Picks screen.

Top Picks Screen

Now let’s design the Top Picks screen.

Firstly, go into constants/Pics.js and add the following bit at the end:

export const TopPicksScreenPics = shuffleArray([ { pic: require('../assets/images/women/women11.jpg'), title: 'Annie, 40', caption: '26h left', }, { pic: require('../assets/images/women/women12.jpg'), title: 'Lena, 31', caption: '20h left', }, { pic: require('../assets/images/women/women13.jpg'), title: 'Kendra, 19', caption: '15h left', }, { pic: require('../assets/images/women/women14.jpg'), title: 'Mia, 23', caption: '45h left', }, { pic: require('../assets/images/women/women15.jpg'), title: 'Jenny, 27', caption: '12h left', }, { pic: require('../assets/images/men/men11.jpg'), title: 'Dwayne, 34', caption: '13h left', }, { pic: require('../assets/images/men/men12.jpg'), title: 'Novak, 27', caption: '22h left', }, { pic: require('../assets/images/men/men13.jpg'), title: 'Zikomo, 32', caption: '20h left', }, { pic: require('../assets/images/men/men14.jpg'), title: 'Sam, 19', caption: '42h left', }, { pic: require('../assets/images/men/men15.jpg'), title: 'Richard, 31', caption: '21h left', }, ]) 

These are the images we’ll need in the Top Picks screen.

Now add the following code in TopPicksScreen.js:

import React from 'react' import { ScrollView, StyleSheet, View } from 'react-native' import { Text, Tile } from 'react-native-elements' import { SafeAreaView } from 'react-navigation' import { TopPicksScreenPics } from '../constants/Pics' class TopPicksScreen extends React.Component { render() { return ( <SafeAreaView> <ScrollView> <Text h2 h2Style={styles.h2Style}> Top Picks </Text> <Text h4 h4Style={styles.h4Style}> Featured profiles of the day, picked just for you </Text> <View style={styles.grid}> {TopPicksScreenPics.map(({ pic, title, caption }, i) => ( <Tile imageSrc={pic} activeOpacity={0.9} title={title} titleStyle={styles.title} caption={caption} captionStyle={styles.caption} featured key={title} /> ))} </View> </ScrollView> </SafeAreaView> ) } } const styles = StyleSheet.create({ h2Style: { fontWeight: 'bold', textAlign: 'center', color: '#000000', }, h4Style: { textAlign: 'center', color: '#757575', }, grid: { marginTop: 20, marginBottom: 20, }, title: { position: 'absolute', left: 10, bottom: 50, backgroundColor: 'black', marginBottom: -2, padding: 10, }, caption: { position: 'absolute', left: 10, bottom: 0, backgroundColor: 'black', marginTop: 10, padding: 10, }, }) export default TopPicksScreen 

Firstly, we use the basic Text component found in react-native-elements with a heading and a subheading.

Then we loop through all the images we just added in constants/Pics.js and display them using the Tile component.

The title and caption are placed in center by default, but we’ve moved them to the bottom left with position:'absolute'.

That concludes our Top Picks screen, which was very simple.

It looks like this:

Top Picks screen demo

Messages Screen

Now let’s get started with the Messages screen. Firstly, we need some dummy data to display on the listicles.

Create Messages.js in the constants/ folder and paste in the following:

import shuffleArray from '../utils/shuffleArray' export const Messages = shuffleArray([ { pic: require('../assets/images/women/women1.jpg'), title: 'Amelia, 27', message: "Let's get to your favorite restaurant.", }, { pic: require('../assets/images/women/women2.jpg'), title: 'Joanna, 19', message: "What's the best way to win you over?", }, { pic: require('../assets/images/men/men2.jpg'), title: 'Novak, 27', message: 'Will catch up with you later.', }, { pic: require('../assets/images/men/men3.jpg'), title: 'Justin, 32', message: 'Probably not going to work out :(', }, { pic: require('../assets/images/women/women3.jpg'), title: 'Charlie, 32', message: 'How about we go for a coffee on Sunday?', }, { pic: require('../assets/images/women/women5.jpg'), title: 'Lucy, 27', message: 'Sleeping for now.', }, { pic: require('../assets/images/men/men1.jpg'), title: 'Aaron, 24', message: 'See you soon.', }, { pic: require('../assets/images/men/men4.jpg'), title: 'Tony, 21', message: 'Seriously, come on time.', }, { pic: require('../assets/images/men/men5.jpg'), title: 'Leo, 30', message: "What'd you like?", }, { pic: require('../assets/images/women/women4.jpg'), title: 'Mary, 23', message: "Hey, what's up?", }, { pic: require('../assets/images/women/women14.jpg'), title: 'Michelle, 45', message: 'Howdy!!!', }, { pic: require('../assets/images/women/women12.jpg'), title: 'Arya, 18', message: 'Not today!', }, ]) 

Next, create MessagesScreen.js in the components/ folder and paste in the following:

import React from 'react' import { SafeAreaView, ScrollView, StyleSheet } from 'react-native' import { ListItem } from 'react-native-elements' import { Messages } from '../constants/Messages' class MessagesScreen extends React.Component { render() { return ( <SafeAreaView> <ScrollView> {Messages.map((user, i) => ( <ListItem key={i} leftAvatar={{ source: user.pic, size: 'large' }} title={user.title} titleStyle={styles.title} subtitle={user.message} subtitleStyle={styles.subtitle} chevron /> ))} </ScrollView> </SafeAreaView> ) } } const styles = StyleSheet.create({ title: { fontSize: 24, color: '#3F3F3F', }, subtitle: { color: '#A5A5A5', }, }) export default MessagesScreen 

We take the dummy data Messages and map over it and put it in a ListItem exported from react-native-elements. The ListItem component displays a list of items one after the other, just like we see on any messages app—with a large avatar, the name of the user, and the message. react-native-elements takes away all the hassle of writing our own listicle for messages so that we can just use five lines of code to make a nice list.

It currently looks like this:

Messages screen demo

Profile Screen

Let’s make the final Profile screen.

First, create a utils/randomNo.js file and paste in the following:

export const randomNo = (min, max) => Math.floor(Math.random() * (max - min) + min) 

The function randomNo returns a random number between min and max.

Now open up components/ProfileScreen.js and paste in the following:

import React from 'react' import { Image, SafeAreaView, StyleSheet, View } from 'react-native' import { Divider, Icon, Text } from 'react-native-elements' import Layout from '../constants/Layout' import { HomeScreenPics } from '../constants/Pics' import { randomNo } from '../utils/randomNo' const { pic, title } = HomeScreenPics[randomNo(1, HomeScreenPics.length)] const Social = ({ name }) => ( <Icon name={name} type="font-awesome" containerStyle={styles.iconContainer} size={32} /> ) class ProfileScreen extends React.Component { render() { return ( <SafeAreaView style={styles.container}> <View style={styles.imageContainer}> <Image source={pic} style={styles.image} /> </View> <Text h4 style={styles.name}> {title} </Text> <Text style={styles.desc}>Fashion Designer at Amelia & Co.</Text> <Divider style={styles.divider} /> <Text style={styles.desc}> I love to travel. I have a cat named pickles. If he likes you, I probably will too. </Text> <Divider style={styles.divider} /> <Text style={styles.desc}>Find me on Social here</Text> <View style={styles.socialLinks}> <Social name="snapchat" /> <Social name="instagram" /> <Social name="facebook-square" /> </View> </SafeAreaView> ) } } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', }, imageContainer: { margin: 20, }, image: { width: Layout.window.width - 60, // device width - some margin height: Layout.window.height / 2 - 60, // device height / 2 - some margin borderRadius: 20, }, name: { color: '#5E5E5E', alignSelf: 'flex-start', marginLeft: 30, }, desc: { color: '#5E5E5E', alignSelf: 'flex-start', marginTop: 5, marginHorizontal: 30, fontSize: 14, }, divider: { backgroundColor: '#C0C0C0', width: Layout.window.width - 60, margin: 20, }, socialLinks: { flex: 1, alignItems: 'flex-start', flexDirection: 'row', width: Layout.window.width, marginLeft: 40, }, iconContainer: { paddingHorizontal: 8, paddingVertical: 15, }, }) export default ProfileScreen 

Let’s decipher the code a bit.

Firstly, we get a random pic and title from the HomeScreenPics array, which is not the first image but can be any of the rest of the images from that array.

Then we’ve created a Social component, looking like this:

const Social = ({ name }) => ( <Icon name={name} type="font-awesome" containerStyle={styles.iconContainer} size={32} /> ) 

This takes in a name as a prop. We use this in our render method. The render method contains the usual SafeAreaView, Text, View, and our custom Social component—with a little bit of styling which we’ve already covered above.

The only unique component here is a Divider component. Dividers are visual separators of content. We use them to make a distinction between different sections of content.

Lastly, we add some styling. That’s it.

It currently looks like this:

Profile screen demo

I’ve also made a repo, in case you want to clone it. You can find it here on GitHub.

Conclusion

We’ve successfully cloned a Tinder UI with a little bit of custom styling and with a lot of help from React Native Elements.

React Native Elements takes all the hassle away while building a beautiful UI by using its pre-made component library.

We could also create everything completely from scratch without using any UI library, but it would require us to write a lot of code—mostly styling. By using a UI library, we can write less code and deliver our application faster.

You can now mimic any UI by taking the smallest part of the UI and building it. Use UI frameworks to write less code and ship faster.


Tag cloud