In the previous part of this React-Native series, we talked about what’s React-Native and Expo, what are some key similarities and differences between React and React-Native, and we also initialized our project.

In this episode, we’ll build the home screen of our React-Native application.

For a quick reminder, here’s the sketch of it:

React Native Mobile App HomeScreen

Table of contents:


Splitting the Home screen

First things first, let’s split up the Home screen into two files: an index.js and a styles.js, just so that the main logic and the stylesheets are well-separated.

# the screens’ directory structure as of now
screens
├── Home
│   ├── index.js
│   └── styles.js
└── Routes.js

Let’s initialize the styles.js with a basic container style:

import { StyleSheet } from "react-native";

export default StyleSheet.create({
 container: {
   flex: 1,
   backgroundColor: "#0a0a0a",
   justifyContent: "center",
   alignItems: "center"
 }
});

Next, import the styles in the index.js, delete the StyleSheet and modify the JSX a bit, so that the index.js looks like this:

// basic imports ...
import styles from './styles';

export default class Home extends Component {
 render() {
   return (
     <View style={styles.container}>
	{/* // this View is empty on purpose! */}
     </View>
   );
 }
}

// notice the deleted styles - they are imported from the styles.js!

Creating the Header

Let’s create the header! We want it to be visible on the Home and the Game screen too, so we will build a component named Header that will display the rainbow text, and re-use it on both screens.

React Native Mobile App Name

Simply create a Header.js file in the components directory and copy-paste the code below.

import React from "react";
import { Text, View, StyleSheet } from "react-native";

const Header = () => (
 <View style={{ flexDirection: "row" }}>
   <Text style={[styles.header, { color: "#E64C3C" }]}>c</Text>
   <Text style={[styles.header, { color: "#E57E31" }]}>o</Text>
   <Text style={[styles.header, { color: "#F1C431" }]}>l</Text>
   <Text style={[styles.header, { color: "#68CC73" }]}>o</Text>
   <Text style={[styles.header, { color: "#3998DB" }]}>r</Text>
   <Text style={styles.header}>blinder</Text>
 </View>
);

const styles = StyleSheet.create({
 header: {
   fontSize: 50,
   color: "#ecf0f1",
   fontFamily: "dogbyte"
 }
});

export { Header };

Because the Header does not need an internal state, we can define it as a stateless functional component. If you aren’t familiar with the idea of using SFCs yet, Hackernoon has a really great summary of what they are and how they work.

Next off, let’s initialize Components/index.js:

export * from './Header'

And import the Header component in the Home/index.js:

import { Header } from '../../components'
// …
<View style={styles.container}>
       <Header />
     	</View>

If you check your Expo Client at this point, your app will look like this:

react native mobile app homescreen only with text

This looks cool, but there’s a small error that we should fix before moving on to our next component: the iOS status bar blends into the background. We can fix this in a few lines of code on the root level, at the App.js,

with the StatusBar component.


Fixing the Status Bar

First, import the StatusBar from react-native and the Fragment from react (if you don’t know about React.Fragment yet, be sure to check the documentation, but in a nutshell, you can use Fragments when you don’t want to add another div to the DOM, but you need to return two or more components from somewhere for React).

import React, { Component, Fragment } from 'react';
import { StatusBar } from 'react-native';

Then add the StatusBar component to our app:

else {
   return (
      <Fragment>
        <StatusBar barStyle="light-content" />
        <Routes />
      </Fragment>
    )
}

It’s just a few lines of code that’s definitely worth adding - the app will now look like this:

react native mobile app homescreen with status bar

That’s one small step for the code, one giant leap for the overall UX.


Adding Interactive Elements to our React-Native App

The logo looks pretty cool, but maybe it’s time to make some elements that the user can interact with - so let’s start with the big Play button that will dominate the screen:

react native mobile app play button

Since we aren’t going to make a traditional button (it will contain an image, a text, and won’t have its own background or border), we won’t use a <Button> - instead, we’ll use a <TouchableOpacity>. It’s a react-native component that gives any component the ability to be tappable and respond to the interactions by dimming the opacity of it. You can read more about it in the React-Native docs.

Let’s import Text, Image, and TouchableOpacity along with View:

import { View, Text, Image, TouchableOpacity } from "react-native";

Create a callback named onPlayPress. Until we figure out what to do with it, a placeholder console.log() will be fine:

onPlayPress = () => {
  console.log("onPlayPress event handler");
};

And finally, the button itself:

<TouchableOpacity onPress={this.onPlayPress} style={{ flexDirection: 'row', alignItems: 'center' }}>
  <Image
    source={require("../../assets/icons/play_arrow.png")}
    style={styles.playIcon}
  />
  <Text style={styles.play}>PLAY!</Text>
</TouchableOpacity>

Notice how you can import images with the require() function. We will use it along this course because the documentation features it, however, there are better solutions out there, so be sure to check them out if you are aiming for production.

If you run the app, you’ll have to realize that we already passed the styles, but they are not defined yet, so let’s go to styles.js and create them:

play: {
   fontSize: 45,
   fontFamily: "dogbyte",
   color: "#ecf0f1",
   marginTop: 5
 },
 playIcon: {
   height: 60,
   width: 60,
   marginRight: 15
 }

There’s nothing special there that would need any explanation, so let’s move on. The app should look like this at this point:

react native mobile app logo and start

Let’s continue with the Hi-score component:

we can worry about the layout later.


Building the High Score Panel


react native mobile app highscore

The high score panel has a similar layout to the Play button, but it’s not tappable, and it’s slightly smaller too - thus we’ll need another bunch of styles for the new, smaller sizes:

hiscore: {
  fontSize: 28.5,
  fontFamily: "dogbyte",
  color: "#ecf0f1",
  marginTop: 5
},
trophyIcon: {
  height: 45,
  width: 45,
  marginRight: 12.5
}

And include the new <Image /> and <Text> in a <View>:

<View style={{ flexDirection: 'row', alignItems: 'center' }}>
  <Image
    source={require("../../assets/icons/trophy.png")}
    style={styles.trophyIcon}
  />
  <Text style={styles.hiscore}>Hi-score: 0</Text>
</View>

We will make this section show the real high score later in the 5th post of this series in the “Persisting data -

storing the high scores” section.


DIY: Build the Leaderboard Button!

This button will look and behave very similar to the Play button:

react native mobile app highscore leaderboard

At this point, try to pause reading and copy-pasting things from the article, and try to build this on your own! Practice makes perfect. If you are stuck with it, try to look back at the Play button and the Hi-score panel’s source for some inspiration, or if you really don’t feel like it,

check the finished code in the GitHub repo.


Let’s add a copyright banner & speaker icon!

In the sketch, there were a few smaller things on the bottom of this screen: a copyright banner, and a speaker icon for muting the game.

react native mobile app copyrights

The container is just an absolute positioned View, nor the text style isn’t too exciting:

bottomContainer: {
   position: "absolute",
   left: 15,
   right: 15,
   bottom: 12.5 // the 2.5px bottom margin from the text is subtracted from the 15px spacing
 },
 copyrightText: {
   fontSize: 16,
   fontFamily: "dogbyte",
   marginBottom: 2.5
 }

And the JSX isn’t a big hack either:

<View style={styles.bottomContainer}>
  <Text style={[styles.copyrightText, { color: "#E64C3C" }]}>
    Music: Komiku
  </Text>
  <Text style={[styles.copyrightText, { color: "#F1C431" }]}>
    SFX: SubspaceAudio
  </Text>
  <Text style={[styles.copyrightText, { color: "#3998DB" }]}>
    Development: RisingStack
  </Text>
</View>

You may notice that toggling the speaker icon only updates an internal state as of now. We will add music and SFX later, in the 5th post of this series. Let’s define our initial state:

state = {
  isSoundOn: true
};

Toggling the sound will also change the icon, and as it has two possible states (music muted and enabled), we’ll have two corresponding icons:

react native mobile app music toggle

In the render function, we want to dynamically import the icon based on the state:

 render() {
   const imageSource = this.state.isSoundOn
     ? require("../../assets/icons/speaker-on.png")
     : require("../../assets/icons/speaker-off.png");
   // ...

We need to add a TouchableOpacity with an image in it. It will display the speaker icon, but to push it to the right side of the screen, you can either play with adding margins, or adding a <View style={{ flex: 1 }} /> before the button.

The empty view will fill up all the empty space on the screen because of its flex: 1 property. It may seem a bit odd for first, but it’s a commonly used practice in React-Native development to use this when doing MVPs, but in production, you should probably stick with using margins or any other solution that’s idiomatic to your use case.

<View style={{ flex: 1 }} />
<TouchableOpacity onPress={this.onToggleSound}>
    <Image source={imageSource} style={styles.soundIcon} />
</TouchableOpacity>

Currently, our app looks like this:

react native mobile app styling

You may have already noticed that the spacing is messed up, so let’s fix that by adding some spacing with margin:

  • For the TouchableOpacity wrapping the play button, add marginTop: 80 to the style property
  • For the View wrapping the Hi-score, add marginTop: 20
  • For the TouchableOpacity wrapping the Leaderboard button, add marginTop: 80

What to expect in Part 3 of this React-Native Tutorial Series

Now that the UI elements can breathe and our home screen looks nice, we can move on the screen that the players are going to spend most of their time on - the game screen.

You can access the code that’s written at this point here.