Build this simple video player with react native and expo to tell a multi-part story -Part 4

Kalen Hammann
6 min readFeb 15, 2018

--

Adding a forward and a back button to step through a whole sequence of videos

We realized that simply having a list of videos with a button next to each would break up the flow of the story we wanted to tell, so wedecided to use the buttons in another way: When the viewer reached the end of one video, she would be able to simply click a single button to go right on to the next “chapter.” If she wanted to revisit the previous video, she could click another button to go back.

How could we most easily accomplish this? Well, part of the strength of React and React Native is how they keep information about what is to be rendered in a single “state” object. The app can then quickly and easily re-render elements on the page whenever state changes.

Using “State” to tell the app which video to play

We had already used state to tell the app whether the video should play or be paused and whether it the sound should be on or muted.

So we added “currentVideo” to state and used it to refer to whichever video we wanted to play. An easy way to do that was to put all our video links in an array

const VIDEOS = [
'https://s3.amazonaws.com/bostondelhi/onboarding_screen.mp4',
'https://s3.amazonaws.com/bostondelhi/V2_edited.mp4',
'https://s3.amazonaws.com/bostondelhi/V3_edited.mp4',
'https://s3.amazonaws.com/bostondelhi/V4_edited.mp4'
]

and set currentVideo initially equal to VIDEOS[0]. Then if we set the video source in the videoplayer to depend on state

source= {{uri: VIDEOS[this.state.currentVideo]}}

the video at the first index would play first, and which video played would change as the value of currentVideo changed.

Changing State to Step through the series of videos

How could we make our forward and back buttons step us through the videos one at a time? What if we just reset currentVideo to currentVideo+1 every time the forward button was clicked, and set it to currentVideo-1 every time the back button was clicked?

This immediately raised the issue of what to do if we were at the beginning or end of the array. Where would the buttons take us then?

We decided to make the sequence of videos a loop: from the last video we would proceed directly back to the first, and from the first video we would go “back” to the last video in the array. In code, that looked like this:

forwardButton = () => {
if (this.state.currentVideo != VIDEOS.length-1) {
this.setState({currentVideo: this.state.currentVideo + 1});
} else {
this.setState({currentVideo: 0});
}
}
backButton = () => {
if (this.state.currentVideo != 0) {
this.setState({currentVideo: this.state.currentVideo - 1});
} else {
this.setState({currentVideo: VIDEOS.length-1});
}
}

Now we just needed to add the buttons. We chose MaterialIcons that seemed appropriate (“navigate-forward” and “navigate-back”) and used flex to put them on the same line as we had done with the volume and play/pause controls. We separated them with text saying “next video” and had something we thought looked pretty good. In code:

<View style={{flex: .25, flexDirection: 'row', 
alignItems: 'center'}}>
<MaterialIcons
name={"navigate-before"}
size={45}
color="black"
onPress={this.backButton}
/>
<Text>Next Video</Text>
<MaterialIcons
name={"navigate-next"}
size={45}
color="black"
onPress={this.forwardButton}
/>
</View>

And here again is how it looked in real life. Our Simple Story-Telling Video Player was done!

Even Simpler Summary

Leaving out all the fascinating story bits, here’s what to do to use React Native and Expo to build a Simple Story-Telling Video Player for yourself:

A. Get create-react-native-app working with expo on your phone:

  1. Get Node and NPM or Yarn installed and running on your machine. (Google it!).
  2. Get the expo app installed on your phone (https://expo.io/).
  3. In a terminal, navigate to the directory in which you want to create your app and run
$ npm install -g create-react-native-app
$ create-react-native-app my-video. &nbsp; //or whatever you name
your app
$ cd my-video
$ npm start
# For Mac users, you can run this instead, and the simulator will open.
$ npm run ios
# For genymotion users (Linux/Windows), you can run this:
$ npm run android

(MAKE SURE YOUR PHONE AND YOUR COMPUTER
ARE ON THE SAME WI-FI NETWORK
or this next step won’t work!):

5. To run the app on your phone, open the expo app and snap a picture of the QR code that just appeared in your terminal.

#If no QR code appearedI. You may need to install watchman   (https://facebook.github.io/watchman/docs/install.html)    On a Mac with homebrew:
$ brew update
$ brew install watchman
II. (Less likely) you may need to change versions of npm
$ npm install npm@4.6.1

B. Get a video player with basic mute and play controls installed in your app:

  1. Install a package of icons you’ll need:
 $ npm install — save @expo/vector-icons

2. Replace the code in App.js with this (thanks to Eyal Cohen: https://medium.com/front-end-hacking/how-to-play-video-with-react-native-and-expo-30523bfcb311):

import React from 'react';
import { StyleSheet, View, Dimensions } from 'react-native';
import { Video } from 'expo';
import { MaterialIcons, Octicons } from '@expo/vector-icons';
export default class App extends React.Component {
render() {
const { width } = Dimensions.get('window');
return (
<View style={styles.container}>
<View>
<Text style={{ textAlign: 'center' }}>
React Native Video</Text>
<Video
source={{ uri:
'http://d23dyxeqlo5psv.cloudfront.net/big_buck_bunny.mp4' }}
shouldPlay={this.state.shouldPlay}
resizeMode="cover"
style={{ width, height: 300 }}
isMuted={this.state.mute}
/>
<View style={styles.controlBar}>
<MaterialIcons
name={this.state.mute ? "volume-mute" :
"volume-up"}
size={45}
color="white"
onPress={this.handleVolume}
/>
<MaterialIcons
name={this.state.shouldPlay ? "pause" :
"play-arrow"}
size={45}
color="white"
onPress={this.handlePlayAndPause}
/>
</View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
controlBar: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: 45,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: "rgba(0, 0, 0, 0.5)",
}
});

Once that’s working,

C. Set your player up to play a series of videos in sequence:

  1. Add an array of links to video files you’ll want to play,
  2. Call this.state.currentVideo as your video source, and
  3. Add forward and back buttons to call functions that will change the value of currentVideo in state:
import React from 'react';
import { Button, StyleSheet, Text, View, Dimensions } from 'react-
native';
import { Video } from 'expo';
import { MaterialIcons, Octicons } from '@expo/vector-icons';
export default class App extends React.Component {
render() {
const { width } = Dimensions.get('window');
const VIDEOS = [ // replace these links with links to your videos
'https://s3.amazonaws.com/bostondelhi/onboarding_screen.mp4',
'https://s3.amazonaws.com/bostondelhi/V2_edited.mp4',
'https://s3.amazonaws.com/bostondelhi/V3_edited.mp4',
'https://s3.amazonaws.com/bostondelhi/V4_edited.mp4'
]
export default class App extends React.Component {
state = {
currentVideo: 0,
mute: false,
shouldPlay: true,
}
handlePlayAndPause = () => {
this.setState((prevState) => ({
shouldPlay: !prevState.shouldPlay
}));
}
handleVolume = () => {
this.setState((prevState) => ({
mute: !prevState.mute
}));
}
forwardButton = () => {
if (this.state.currentVideo != VIDEOS.length-1) {
this.setState({currentVideo: this.state.currentVideo + 1});
} else {
this.setState({currentVideo: 0});
}
}
backButton = () => {
if (this.state.currentVideo != 0) {
this.setState({currentVideo: this.state.currentVideo - 1});
} else {
this.setState({currentVideo: VIDEOS.length-1});
}
}
render() {
const { width } = Dimensions.get('window');

return (
<View style={styles.container}>
<View >
<Text style={{ textAlign: 'center', fontSize: 18,
fontWeight: 'bold' }}>Daily Life in Delhi</Text
<Video
source= {{uri: VIDEOS[this.state.currentVideo]}}
shouldPlay={this.state.shouldPlay}
resizeMode="cover"
style={{ width, height: 300 }}
isMuted={this.state.mute}
/>
<View style={styles.controlBar}>
<MaterialIcons
name={this.state.mute ? "volume-mute" :
"volume-up"}
size={45}
color="white"
onPress={this.handleVolume}
/>
<MaterialIcons
name={this.state.shouldPlay ? "pause" :
"play-arrow"}
size={45}
color="white"
onPress={this.handlePlayAndPause}
/>
</View>
</View>
<View style={{flex: .25, flexDirection: 'row',
alignItems: 'center'}}>
<MaterialIcons
name={"navigate-before"}
size={45}
color="black"
onPress={this.backButton}
/>
<Text>Next Video</Text>
<MaterialIcons
name={"navigate-next"}
size={45}
color="black"
onPress={this.forwardButton}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
controlBar: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: 45,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: "rgba(0, 0, 0, 0.5)",
}
});

Voila! You have a simple video player that will step forward or back through your array, video by video, and when it reaches the end of the array, will begin again at the other end to create a loop of videos.

If you like, you can check out the tutorial at https://facebook.github.io/react-native/docs/tutorial.html for ways to take your app to the next level.

If you have questions or comments, I’d love to hear them. I’m on LinkedIn at https://www.linkedin.com/in/kalenhammann/ ; my other apps are at http://github.com/drkalen and http://kalenhammann.com.

Enjoy telling your stories on your phone!

--

--