Linking WordPress and React Native

Linking WordPress and React Native

You will need a WordPress install to work with. You will also need Node 8.3+ and the React Native CLI installed on your machine.

You will need a WordPress install to work with. You will also need Node 8.3+ and the React Native CLI installed on your machine.

In this tutorial we are going to be building a simple mobile app using React Native called ‘Music News’ which will pull posts from a WordPress website via the WP REST API and display them to the user every time they tap to read more about the news. We’ll start with this and you can dig more and do more by reading and understanding the documentation.

As mobile developers, integrating a data source is a very important aspect of building mobile apps. Having an organized data source ready for integration would make development easy and also reduce the time taken to build and develop mobile apps.

WordPress as a backend can be very effective if you’re building mobile applications that have a lot of consumable content such as a mobile application for a blog, e-commerce website, and so on.

It can provide standard endpoints to return the data you need and also provides standard documentation for all the endpoints you’ll require.

Prerequisites

To follow through with this lesson, a basic understanding of the following are required:

  • WordPress
  • React Native

You also need to have the following installed:

  • WordPress
  • React Native
Getting started

What is WordPress?

WordPress is the easiest and fastest way to build your website. It’s free and open source and it’s based off two very popular technologies, PHP and MySQL.

It is stated that over 29% of websites on the internet are WordPress powered websites.

WordPress is also a content management tool. It allows you to manage content posted on your website easily without the need for you to know anything technical and that’s great because you don’t have to be a programmer to be able to create and manage your website by yourself. There’s a wide variety of websites you can build using WordPress and some of them includes:

  • WordPress
  • React Native

WordPress can be an excellent tool to create your website because:

  • WordPress
  • React Native

What is React Native?

React Native is a mobile JavaScript framework for building real native mobile applications for iOS and Android. It’s based on React made by Facebook’s engineering team which is a JavaScript library for building user interfaces.

Unlike a hybrid application that allows you to write your code in HTML and CSS and then renders it in a native web view container or component, React Native calls Objective-C and Java APIs respectively to render your JSX (a combination of JavaScript and XML-esque markup which is the standard way React applications are written) codes into user interface components on your device.

Some of the known pros of React Native:

  • WordPress
  • React Native

Some of the known cons of React Native:

  • WordPress
  • React Native

What is an API?

API stands for Application Programming Interface. It can be seen as a component that allows for interaction between two programming languages, software libraries, software tools, and so on. It also specifies a set of rules that should be followed in order to interact and communicate with underlying data.

An API could mean something else, depending on the context used and in this context, an API refers to a software component we will interact with to provide us with relevant raw data to be used inside our application.

Configuring WordPress to have an API

To get started, the first step would be to install the WordPress REST API plugin to our already existing WordPress installation. My WordPress site is on my local device so I’ll be installing it there.

  • WordPress
  • React Native

  • WordPress
  • React Native

  • WordPress
  • React Native

  • WordPress
  • React Native

We can now proceed to make more Music News posts on our WordPress normally and then visit the endpoint to see it translated into raw data that can be used in our mobile app.

Setting up our React Native app

To get started on this, you can follow the official docs on how to set up your React Native environment on your computer.

Once that is done, we need to initiate and create our application project.

    $ react-native init MusicNews


Once that is completed, we need to compile and build the application.

    $ react-native run-ios
    # if using android, you can run the command below
    $ react-native run-android


Configuring routing in our app

How our users navigate around our mobile app is important as developers, we should make navigation as easy and smooth as possible. To configure navigation in our Music News app, we’ll use the react-native-router-flux library to manage navigation between the screens of our app where all the music news posts are, to the screen where we’ll read each individual post. React-native-router-flux is an API on top of React Navigation and it allows you to define routes in one central place for easy management and configuration. To get started with react-native-router-flux

    $ npm install react-native-router-flux --save


Now that we have it installed, we go ahead and create our route file and configure all application routing. In the root directory of our Music News app, create a Routes.js file and the contents would look like:

    // Routes.js
    import React, { Component } from 'react';
    import {Platform} from 'react-native';

    // import components from react-native-router-flux
    import {Router, Stack, Scene} from 'react-native-router-flux';

    // import our screens as components 
    import Home from './screens/Home';
    import ViewNews from './screens/ViewNews';

    export default class Routes extends Component<{}> {

        render() {
            return(
                <Router>
                    <Stack key="root" hideNavBar={true}>
                        <Scene key="home" component={Home} title="Home" />
                        <Scene key="viewnews" component={ViewNews} title="View News"/>
                    </Stack>
                </Router>
                )
        }
    }

  • WordPress
  • React Native

Also, edit your App.js file to look like this:

    //App.js
    import React, { Component } from 'react';

    import {
      StyleSheet,
      View,
      StatusBar,
    } from 'react-native';

    import Routes from './Routes';

    export default class App extends Component<{}> {

      render() {
        return (
          <View style={styles.container}>
            <StatusBar
              backgroundColor="#fff"
              barStyle="dark-content"
            />
            <Routes/>
          </View>
        );
      }
    }

    const styles = StyleSheet.create({
      container : {
        flex: 1,
      }
    }); 

Designing screens for our app

React Native offers inbuilt UI components like the FlatListModalSliderText, and so on. For the design of our home page, we will be using the Shoutem UI and react-native-render-html to render all the posts fetched from our WordPress backend.

Now that we’ve successfully configured routing, we need to create the screens as specified in the Routes file. We’ll create a screen folder and create the files as seen below:

    $ npm install --save react-native-render-html
    $ npm install --save @shoutem/ui
    $ react-native link
    $ mkdir screens && cd screens
    $ touch Home.js ViewNews.js


Copy the code below and paste into your /screens/Home.js

    //screens/Home.js
    import React, { Component } from 'react';
    import {
      StyleSheet,
      SafeAreaView,
    } from 'react-native';

    import {
      Subtitle,
      Screen,
      Divider,
      View, 
      Row,
      ListView,
    } from '@shoutem/ui';

    import {Actions} from 'react-native-router-flux';

    export default class Home extends Component<{}> {

      home(){
        Actions.home();
      }

      viewNews(newspost){
        Actions.viewnews({newspost: newspost});
      }

      componentWillMount() {
        this.fetchMusicNews();
      }

      fetchMusicNews = async () => {
        const response = await fetch("http://localhost/wordpress/wp-json/wp/v2/posts/");
        const json = await response.json();
        this.setState({ musicNews: json, isLoadingMusicNews: false });
      };

      constructor(props) {
        super(props);
        this.renderRow = this.renderRow.bind(this);
        this.state = {
          isLoadingMusicNews: true,
          musicNews: [],
        }
      }  

      renderRow(post) {
        const regex = /(<([^>]+)>)/ig;
        let newspost = {
            postId: post.id,
            postDate: post.date,
            postLink: post.guid.rendered,
            postTitle: post.title.rendered,
            postExcerpt: post.excerpt.rendered,
            postContent: post.content.rendered,
            postCategory: post.categories,
        }
        return (
          <Row style={{height: 80}}>
            <View styleName="vertical stretch space-between">
              <Subtitle 
                numberOfLines={2} 
                newspost={newspost} 
                onPress={() => this.viewNews(newspost)}>
                {post.title.rendered.replace(regex, '').toUpperCase()}
              </Subtitle>
            </View>
          </Row>
        );
      }

      render() {
        const regex = "/(<([^>]+)>)/ig"
        const musicNews = this.state.musicNews;
        return (
          <SafeAreaView style={styles.safeArea}>
            <Screen>
              <View>
                  <ListView
                    data={musicNews}
                    renderRow={this.renderRow}
                  />
              </View>
            </Screen>
          </SafeAreaView>
        );
      }
    }

    const styles = StyleSheet.create({
      container: {
        flexDirection: 'column',
        backgroundColor: '#fff'
      },
      safeArea: {
        flex: 1,
        backgroundColor: '#fff'
      },
    });

In the Home.js file, we import all the necessary components to build our interface. We create routing functions so we can navigate from page to page. The fetchMusicNews() is an asynchronous function that allows us to fetch data from our API configured WordPress backend and the componentWillMount() allows us to fetch the async data after the screen is rendered. The fetched data is stored in the musicNews state and will be passed to our component during rendering.

In the renderRow() we define a prop that holds data fetched from our WordPress API and we pass it to the ViewNews page so we don’t have to do a network fetch to get the same data on the next screen. The data fetched is rendered as a list view using the ListView component imported from our UI library and the data is also rendered accordingly.

Our render() function renders the screen and we use the SafeAreaView component to handle the screens of newer device screen like the ones of the iPhone X and higher so the screen doesn’t overlap. Our styles are also defined for different components and the applied to style individual components based on the defined rules.

Copy the code below and paste into your /screens/ViewNews.js

    // screens/ViewNews.js
    import React, { Component } from 'react';
    import {
      StyleSheet,
      SafeAreaView,
      ScrollView,
      Dimensions,
    } from 'react-native';

    import {
      Tile,
      Title,
      Screen,
      Divider,
      View, 
      Overlay
    } from '@shoutem/ui';

    import {Actions} from 'react-native-router-flux';
    import HTML from 'react-native-render-html';

    export default class ViewNews extends Component<{}> {

      home(){
        Actions.reset('home');
        Actions.home();
      }

      constructor(props) {
        super(props);
        this.state = {
          newspost: [],  
        }
      }
      render() {
        const news = this.state.newspost;
        const regex = /[[email protected]#$%^&*<>0-9;]/g;
        console.log("newspost: "+this.props.newspost);
        return (
          <SafeAreaView style={styles.safeArea}>
            <Screen style={{ flex: 1 }}>
              <ScrollView>
              { 
                <View>
                  <Tile>
                      <Overlay styleName="image-overlay">
                        <Title style={{color: '#fff', fontWeight: '800'}} styleName="md-gutter-bottom">{this.props.newspost.postTitle.replace(regex, "").toUpperCase()}</Title>
                        </Overlay>
                    </Tile>
                  <Screen style={{paddingLeft: 15, paddingRight: 15, paddingTop: 15, paddingBottom: 15, width:375}} styleName="paper">
                    <HTML 
                      tagsStyles={{ 
                        body: {fontSize: 20}, 
                        p: {fontSize: 20, fontWeight: "normal"}, 
                        strong: {fontSize: 20,}, 
                        blockquote: {fontSize: 20}, 
                        a: {fontSize: 20, color: "#000"}, 
                        em: {fontSize: 20,}, 
                        img: {height: 250, width: 350}, 
                      }}
                      styleName="paper md-gutter multiline" 
                      html={this.props.newspost.postContent} 
                      imagesMaxWidth={Dimensions.get('window').width} 
                      ignoredStyles={['width', 'height', 'video']}
                      onLinkPress={(evt, href) => this.onLinkPress(href)}
                    />
                    <Divider styleName="line" />
                  </Screen>
                </View>
              }  
              </ScrollView>
            </Screen>

          </SafeAreaView>
        );
      }
    };

    const styles = StyleSheet.create({
      container: {
        flex: 1, // 1:1
        flexDirection: 'column',
        backgroundColor: '#fff'
      },
      safeArea: {
        flex: 1,
        backgroundColor: '#fff'
      },
    });

In the ViewNews.js file, we also import all the necessary components to build our interface. We create routing functions so we can navigate from page to page. The regex variable is a regular expression. We use it to remove some unwanted characters and symbols from our WordPress data.

In the render() function, we go ahead to fetch the data we stored in props from our Home.js and render it using our HTML component. The HTML component is used to render the data because the news item body sent from our WordPress API is sent with an HTML format and we can perform some extra functions like setting image size and dimensions, ignore styles, etc.

Using WordPress data

To build and compile our code:

    $ react-native run-ios
    # If you want to build for android,you can use the command below
    $ react-native run-android


If you are running this on your local host, chances are that you would get a network error. This is because localhost and 127.0.0.1 would refer to the internal React native app. You should replace them with the public IP of your machine or tunnel your localhost via ngrok
If you want to tunnel your WordPress app via ngrok, you can take the following steps:

  • WordPress
  • React Native

After the build is successful, your application should look like

  • WordPress
  • React Native

  • WordPress
  • React Native

Conclusion

We can see how easy it is to use our WordPress data in our mobile application using the WP REST API Plugin. Our data can be integrated with any mobile development framework and web development framework also.

Our Music News app is simple right now, but you can go ahead to add some more styling and explore the Plugin documentation more to be able to build complex and robust applications on top of your WordPress data.

The code base to this tutorial is available in a publicly hosted GitHub repository. Feel free to experiment around with it.

Originally published at https://pusher.com

Learn More

☞ React Native vs Flutter (Which is best for startup ?)

☞ Why and How to use TypeScript in your React App?

☞ The Complete WordPress Website Business Course

☞ WordPress Theme Development with Bootstrap

☞ Wordpress for Beginners - Master Wordpress Quickly

☞ Wordpress Complete Web Design :Latest Wordpress Design Techs

☞ Become a WordPress Developer: Unlocking Power With Code

☞ WordPress for Beginners: Your Guide to WordPress Websites

What is the future of Flutter? Flutter going to kill React Native?

What is the future of Flutter? Flutter going to kill React Native?

What is the future of Flutter? Will Flutter kill React Native? What are the benefits that Flutter provides over other popular frameworks?

An Overview of the two most popular and trending frameworks for Hybrid Application Development. Introduction

Hybrid Application development is ruling the Mobile Application development industry rather than Native Development from a couple of years onwards. Business needs are changing from day to day because of an increase in growth and demand. One of the most popular frameworks that became a friend for most of the mobile application developers is React Native, which was introduced by Facebook in the year 2015. until 2015 there was no perfect framework for hybrid application development even though Phone Gap was available in the market. Phone Gap is based on the C# programming language. Developers didn’t love that framework much because there are certain disadvantages in Phone Gap.

The Reasons for the Increase in Demand for Hybrid Application Development

Developing a Mobile Application is really fundamental for the business, but developing the applications for different mobile platforms is becoming an issue for the business investors because of the following problems

  1. They need to maintain different developing teams for different platforms which might lead to a waste of time and money.
  2. It is difficult to find skilled developers, especially in the Asian countries like India, China, Bangladesh, Russia etc… because Apple has a low market in the Asian countries.
  3. There is a development gap between some of the more popular platforms. Deciding which platform to use or focus on can cause confusion.

The above reasons gave birth to the idea of creating cross-platform mobile applications.

There are lots of solutions available in the market but the most popular and trending one right now is by creating compiled apps that can run on both IOS and Android platforms without creating any issues by providing a good performance to the user. The most powerful competitors in that field at this moment are Flutter and React Native. Let’s discuss the strengths and weaknesses of both Flutter and React Native and perform an objective comparison on React Native and Flutter I’ll give the conclusion weather React Native is going to die or not.

React Native

React Native is a powerful javascript framework developed by Facebook for developing cool amazing applications within a short period of time, which is having a capability to run on both IOS and Android platforms. It is developed based on React, it is a Facebook’s JavaScript library for building user interfaces — but instead of targeting the Web Platform, it targets on mobile platforms.

Flutter

Flutter is a new Hybrid mobile application development framework developed by Google and released in the year 2017. It is built and based on the compiled programming language called Dart, which is agoogle’s general-purpose programming language released in the year 2011. this newly introduced framework will help us to build a variety of critical applications within a short period of time.

One of the advantages of Flutter is that it uses Dart as a programming language, the applications that build with the help of Flutter is able to run on both Android and IOS platform. the applications that are built with the help of Flutter are ridiculously fast in performance.

The Differences Between React Native and Flutter

Till now I’ve completed the introduction of React Native and Flutter and also I explained about why Hybrid Application development is in demand, let’s look at the key differences with React Native and Flutter.

User Interface

React Native

React Native’s UI experience is like using HTML without any CSS framework. While that sounds weird, it actually helps you to build on the native components for both Android and iOS, which provides a better user experience (UX). There is also huge community support, and there are third-party libraries that let you get off the ground in no time.

Flutter

Flutter has built-in UI elements which are probably called as widgets — All the required shapes, graphics, animations that are required for an application will be created with the help of widgets. If you want to customize your UI easily and quickly, you can use Material Design widgets in your Android application development and Cupertino widgets for IOS application development. However, there is limited support to the community for building and maintaining these widgets. As the framework matures, widgets will be the way to quickly iterate and build Flutter apps.

Performance

React Native

Applications that are built with the help of React Native is having an issue with rendering large dataset because React application runs with the help of javascript bridge, we can improve the speed of an application with the use of third-party libraries. However, in the race of a competitive world, React Native can’t able to beat flutter in terms of performance.

Flutter

Applications that are developed with the help of flutter are having the greatest speed over React Native because it is having an advantage of using Dart. Dart is one of the fastest compiled based languages in the world. Flutter applications don’t require any intermediate bridges to run the application like React Native that’s why Flutter is more powerful than React Native.

Development Time

React Native

React native is having several third-party libraries to design a good user interface.it does not require you to write and add separate files for complex UI support, which reduces the development time and improves workflow for the developers. with the help of React Native, developers can develop the cross-platform applications very quickly within a short period of time.

Flutter

The biggest disadvantage of flutter is that we need to add separate code files for Android and IOS platforms when building complex UI elements. To customize the UI elements we require Dart programming language, Developers need to keep a lot of effort for designing the UI for an application. But once the UI is designed with good looking features then automatically it will give better user experience in both platforms rather than Native Applications.

Developers usually say that Applications that are built with the help of Flutter will give more user experience than Native Applications.

Community Support

React Native

React Native’s community support already exceeded Flutter’s — React Native is currently the most popular framework on Stack Overflow, with community support as proof. React Native is completely open source. there are many blogs and forums to help for beginner programmers.

Flutter

Flutter was released in the year 2017. it has been almost 2 years and also there is no much love for the Dart programming language in the StackOverflow community. the community support for the Flutter is quite minimal, but the flutter developers at Google are really good, Flutter documentation is really fantastic and easy to follow for the beginners as well.

Code Structure

React Native

React Native has really done a fantastic job in segregating logic, navigation, and design for an application. This allows you to build out complex mobile applications in an easy manner. this is one of the reasons why developers love towards React Native more than other Frameworks.

Flutter

Flutter doesn’t separate any data, template files, and UI elements. because it uses Dart has a core programming language for building the UI elements even in the complex applications also. So we can simply say that everything is Inline.

Conclusion

React Native currently has steady growth, great community support, amazing code structure, and a really good adoption base for developers. while it comes to Flutter —

It has great potential as part of the future of mobile app development. what I think that

Flutter + Dart + LargeCommunity = Recipe for Success

The only thing that’s stopping most of the developers is that Google’s well-known move of shutting down open source and acquired products.

Hope this article helps you to understand the main difference between Flutter and React Native and the future scope.

How to build a Chat App with React Native

How to build a Chat App with React Native

In this tutorial, we’ll be looking at how to build a demo chat app in React Native

The following features:

  • Public and private chat rooms
  • Roles and permissions
  • Typing indicators
  • Read receipt
  • File uploads
  • Show online and offline users
Prerequisites

Knowledge of **React **and React Native is required to follow this tutorial.

The following package versions are used. If you encounter any issues in compiling the app, try to use the following:

  • Node 11.2
  • Yarn 1.13
  • React Native 0.59

You’ll also need a Chatkit app instance. I assume you already know how to set it up. If not, then check out the official docs.

Lastly, you’ll need an ngrok account for exposing the server to the internet.

Chat App overview

As mentioned earlier, we will be building a chat app. The first screen that the user will see when they open the app is the Login screen. This is where they enter their username so they can log in. We will be creating the users in the **Chatkit **console later:

Once logged in, the app will show the list of rooms they can enter:

Once they’ve selected a room to enter, they’ll be greeted by the Chat app screen:

From the Chat screen, when a user clicks on the avatar of another member of a room, they can see the last message they have seen:

The list of members can be viewed when they click on the Users button in the header. The circle right beside the username indicates their online status:

Aside from those, the app will also have a file attachment and typing indicator feature. Image, audio, and video files can also be previewed.

You can find the code in this GitHub repo.

Setting up roles and permissions

Before we proceed, we first have to set up the roles and permissions for each user in the Chatkit console. We will set up seven users:

Three rooms will also be needed:

  • company
  • web_dev
  • mobile_dev

Everyone will be able to have chat in the company room. While the web and mobile users can only chat within their respective rooms. This can be implemented by means of roles and permissions:

For this app, we only have two roles. These will only be scoped in a specific room:

  • room-admin - check all the permissions.
  • room-member - check all the permissions except room:delete, room:update, room:join, room:leave, room:members:add, room:members:remove.

The only difference between the two is that the room-admin can add or remove users from the room. Though we won’t really be implementing it in the app.

Once you’ve created the roles, the next step is to assign the roles to each of the users. Web Admin and Mobile Admin will have the role of room-admin, while the rest of the users will have the role of room-member. Be sure to apply them to the room where they’re supposed to go:

Don’t forget to assign all of the users as a member of the company room. This time, the room admins will have the same role as the members.

Bootstrapping the app

For us to get into the crucial parts of the app quickly, I prepared a GitHub repo which contains some of the boilerplate code (custom components, navigation setup, styling). Clone it and switch to the starter branch:

    git clone https://github.com/anchetaWern/RNChatkitDemo
    git checkout starter

Whenever I mention “starter branch“, it means that I’m referring to existing code.
Next, install all the dependencies and link the relevant packages:

    yarn
    react-native eject
    react-native link react-native-gesture-handler
    react-native link react-native-permissions
    react-native link react-native-document-picker
    react-native link react-native-fs
    react-native link react-native-config
    react-native link react-native-vector-icons
    react-native link rn-fetch-blob

One of the dependencies (React Native Audio Toolkit) doesn’t play nice with automatic linking. Be sure to check out their docs for information on how to set it up.

Next, we need to update the Android manifest file to allow the app to read from the external storage. This allows us to pick files from the device’s external storage:

    // android/app/src/main/AndroidManifest.xml
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.rnchatkitdemo">
      <uses-permission android:name="android.permission.INTERNET" />
      <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
      ...
    </manifest>

Lastly, update the android/app/build.gradle file and include the gradle file for React Native Config:

    apply from: "../../node_modules/react-native/react.gradle"
    apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle" // add this

Adding the server code

Now we’re ready to start coding. Let’s kick things off with the server. Some of the code for initializing Chatkit and running the server itself has already been added to the starter branch. All we have to do now is install the dependencies, update the .env file, and add the routes that the app will be consuming.

Start by install the dependencies:

    cd server
    yarn

Next, update the .env file with the Chatkit credentials:

    CHATKIT_INSTANCE_LOCATOR_ID="YOUR CHATKIT INSTANCE LOCATOR ID (v1:us1:)"
    CHATKIT_SECRET_KEY="YOUR CHATKIT SECRET KEY"

Now we can proceed with the code. The /users route allows us to fill the users array with users data that will be used when someone logs in. Later on, be sure to access <a href="http://localhost:5000/users" target="_blank">http://localhost:5000/users</a> on your browser before you try logging in a user. This is because the login code uses the data in the users array to get specific user data:

    // server/index.js
    let users = [];
    app.get("/users", async (req, res) => {
      try {
        users = await chatkit.getUsers();
        res.send({ users });
      } catch (get_users_err) {
        console.log("error getting users: ", get_users_err);
      }
    });

Note: We only have the above route to simplify things. You don’t really want to be exposing user data in a production app.
When a user logs in, the app makes a request to the /user route. This will return the user’s info based on their username:

    app.post("/user", async (req, res) => {
      const { username } = req.body;
      try {
        const user = users.find((usr) => usr.name == username);
        res.send({ user });
      } catch (get_user_err) {
        console.log("error getting user: ", get_user_err);
      }
    });

Next, add the route which returns the rooms that the user (the one supplied in the request body) is a member of. This is the reason why we went through the trouble of assigning each of the users to their rooms:

    app.post("/rooms", async (req, res) => {
      const { user_id } = req.body;
      try {
        const rooms = await chatkit.getUserRooms({
          userId: user_id
        });
        res.send({ rooms });
      } catch (get_rooms_err) {
        console.log("error getting rooms: ", get_rooms_err);
      }
    });

The getUserRooms is preferable to the getJoinable rooms method because the rooms we created (except for the company room) are private. This means they won’t show up when you call getJoinableRooms. Another reason is that the room wouldn’t even be returned if the user is already a member of that room.
Lastly, add the route for returning the user permissions. This will be called when a user enters a specific room so that the app knows what the user can or cannot do in that specific room:

    app.post("/user/permissions", async(req, res) => {
      const { room_id, user_id } = req.body;
      try {
        const roles = await chatkit.getUserRoles({ userId: user_id });
        const role = roles.find(role => role.room_id == room_id);
        const permissions = (role) ? role.permissions : [];
        res.send({ permissions });
      } catch (user_permissions_err) {
        console.log("error getting user permissions: ", user_permissions_err);
      }
    });

The response data in the API call above is an array containing the individual permissions. Here’s an example: ['room:join', 'room:leave'].

Group screen

The Group screen is where the user will be redirected to when they have logged in to the app. We won’t go through the login code as it has already been pre-coded. All you need to know is that the Login screen sends a request to the /user route of the server. As you’ve seen earlier, this returns the user data. From this data, the only thing we want to pass to the Group screen is the user_id:

    // src/screens/Group.js
    state = {
      rooms: []
    };

    constructor(props) {
      super(props);
      const { navigation } = this.props;
      this.user_id = navigation.getParam("id");
    }

When the component is mounted, we send a request to the /rooms route. This returns the list of rooms that the user is a member of:

    async componentDidMount() {
      try {
        const response = await axios.post(`${CHAT_SERVER}/rooms`, { user_id: this.user_id });
        const { rooms } = response.data;
        this.setState({
          rooms
        });
      } catch (get_rooms_err) {
        console.log("error getting rooms: ", get_rooms_err);
      }
    }

Next, render the list of rooms:

    render() {
      const { rooms } = this.state;
      return (
        <View style={styles.container}>
          {
            rooms &&
            <FlatList
              keyExtractor={(item) => item.id.toString()}
              data={rooms}
              renderItem={this.renderRoom}
            />
          }
        </View>
      );
    }

Here’s the code for rendering each list item. It has a button which when clicked by the user redirects them to the room they selected:

    renderRoom = ({ item }) => {
      return (
        <View style={styles.list_item}>
          <Text style={styles.list_item_text}>{item.name}</Text>
          <Button title="Enter" color="#0064e1" onPress={() => {
            this.enterChat(item);
          }} />
        </View>
      );
    }

Here’s the code for entering a chat room. This sends a request to the /user/permissions route which then returns the permissions for that specific room. From there, we determine if the user is a room admin if they have the room:members:add permission. This means that they can add new users to the room:

    enterChat = async (room) => {
      try {
        const response = await axios.post(`${CHAT_SERVER}/user/permissions`, { room_id: room.id, user_id: this.user_id });
        const { permissions } = response.data;
        const is_room_admin = (permissions.indexOf('room:members:add') !== -1);

        this.props.navigation.navigate("Chat", {
          user_id: this.user_id,
          room_id: room.id,
          room_name: room.name,
          is_room_admin
        });

      } catch (get_permissions_err) {
        console.log("error getting permissions: ", get_permissions_err);
      }
    };

We won’t really be implementing the adding of users in this tutorial. This is just to show how permissions can be used to limit what the user can do within the app.##

Chat screen

We can now proceed to the main meat of the app. As we have lots of things to implement, I’ve divided it into multiple sections, each discussing a specific feature of the chat app.

Importing the dependencies

First, we import the dependencies:

    // src/screens/Chat.js
    import React, { Component } from "react";
    import { View, Text, ActivityIndicator, FlatList, TouchableOpacity, Alert } from "react-native";
    import { GiftedChat, Send, Message } from "react-native-gifted-chat"; // for the chat UI
    import { ChatManager, TokenProvider } from "@pusher/chatkit-client"; // for implementing chat functionality
    import axios from "axios"; // for making requests to the server
    import Config from "react-native-config"; // for reading .env file
    import Icon from "react-native-vector-icons/FontAwesome"; // for showing icons
    import { DocumentPicker, DocumentPickerUtil } from "react-native-document-picker"; // for picking files
    import * as mime from "react-native-mime-types"; // for determining the mime type of the picked file
    import Modal from "react-native-modal";
    import RNFetchBlob from "rn-fetch-blob"; // for converting the attached file to a blob
    import RNFS from "react-native-fs"; // for getting the base64 string representation of a file

Chatkit expects the file attachments to be a Blob. React Native doesn’t really have support for this format by default. So we use rn-fetch-blob as a polyfill to implement it. Later on, you’ll see this in action in the Attaching files section:

    const Blob = RNFetchBlob.polyfill.Blob;
    const fs = RNFetchBlob.fs;
    window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
    window.Blob = Blob;

Next, we have a few pre-coded components which are used to render either an audio or video player depending on the format of the attached file. Note that these components only support mp3 files for audio, and mp4 files for video:

    import ChatBubble from "../components/ChatBubble";
    import AudioPlayer from "../components/AudioPlayer";
    import VideoPlayer from "../components/VideoPlayer";

You can read my previous tutorial on playing audio and video file attachments in a Chatkit React Native app if you want to learn more about how those components are implemented
Lastly, update the .env file with your Chatkit config.

Initializing the chat code

In the Chat screen’s header, we add a button for opening the modal for viewing all the members of the room (whether they’re offline or online):

    class Chat extends Component {

      static navigationOptions = ({ navigation }) => {
        const { params } = navigation.state;
        return {
          headerTitle: params.room_name,
          headerRight: (
            <View style={styles.header_right}>
              <TouchableOpacity style={styles.header_button_container} onPress={params.showUsersModal}>
                <View style={styles.header_button}>
                  <Text style={styles.header_button_text}>Users</Text>
                </View>
              </TouchableOpacity>
            </View>

          ),
          headerStyle: {
            backgroundColor: "#333"
          },
          headerTitleStyle: {
            color: "#FFF"
          }
        };
      };

      // next: initialize state

    }

Next, initialize the state:

    state = {
      company_users: null,
      room_users: null,
      messages: [],
      is_initialized: false, // if Chatkit is initialized or not
      is_picking_file: false, // to show/hide the loading animation when picking files
      // to show/hide the various modal windows:
      is_video_modal_visible: false,
      is_last_viewed_message_modal_visible: false,
      is_users_modal_visible: false, 
      is_typing: false, // if there's someone in the room who is currently typing
      typing_user: null, // the username of the user who is typing
      show_load_earlier: false, 
      viewed_user: null, // username of user whose read receipt is currently being viewed
      viewed_message: null // the text message being viewed
    };

    // next: add constructor

Next, get the navigation params that were passed from the Group screen earlier:

    constructor(props) {
      super(props);
      const { navigation } = this.props;

      this.user_id = navigation.getParam("user_id");
      this.room_id = navigation.getParam("room_id");
      this.is_room_admin = navigation.getParam("is_room_admin");

      this.modal_types = {
        video: 'is_video_modal_visible',
        last_viewed_message: 'is_last_viewed_message_modal_visible',
        users: 'is_users_modal_visible'
      }
    }

    // next: add componentDidMount

Once the component is mounted, initialize Chatkit:

    async componentDidMount() {
      this.props.navigation.setParams({
        showUsersModal: this.showUsersModal
      });

      try {
        const chatManager = new ChatManager({
          instanceLocator: CHATKIT_INSTANCE_LOCATOR_ID,
          userId: this.user_id,
          tokenProvider: new TokenProvider({ url: CHATKIT_TOKEN_PROVIDER_ENDPOINT })
        });

        let currentUser = await chatManager.connect();
        this.currentUser = currentUser;

        // next: subscribe to room

      } catch (chat_mgr_err) {
        console.log("error with chat manager: ", chat_mgr_err);
      }
    } 

We then subscribe to the room. We only need to subscribe to the hooks for receiving messages, and showing/hiding typing indicators:

    await this.currentUser.subscribeToRoomMultipart({
      roomId: this.room_id,
      hooks: {
        onMessage: this.onReceive,
        onUserStartedTyping: this.startTyping,
        onUserStoppedTyping: this.stopTyping
      }
    });

We don’t need to subscribe to the onPresenceChanged hook because the presence data from this.currentUser.users is live data. This means that when the users either offline or online, their presence state also changes, so the UI is automatically updated.
Lastly, we update the state with the room members data:

    await this.setState({
      is_initialized: true,
      room_users: this.currentUser.users
    });

Chat UI

We can now proceed to the UI. Start by extracting the data that we need from the state:

    render() {
      const {
        is_initialized,
        room_users,
        messages,
        video_uri,
        is_video_modal_visible,
        is_last_viewed_message_modal_visible,
        viewed_user,
        viewed_message,
        is_users_modal_visible,
        show_load_earlier,
        typing_user
      } = this.state;

      // next: render the Chat UI
    }

Next, render the chat UI. All the heavy lifting is already done for us by React Native Gifted Chat, so all we have to do is supply all the props that it needs:

    return (
      <View style={styles.container}>
        {(!is_initialized) && (
          <ActivityIndicator
            size="small"
            color="#0064e1"
            style={styles.loader}
          />
        )}

        {is_initialized && (
          <GiftedChat
            messages={messages}
            onSend={messages => this.onSend(messages)} // function to execute when send button is clicked
            user={{
              _id: this.user_id
            }}
            renderActions={this.renderCustomActions} // for rendering button for attaching files
            renderSend={this.renderSend} // custom send button UI
            renderMessage={this.renderMessage} // custom chat bubble UI
            onInputTextChanged={this.onTyping} // function to execute while the user is typing
            renderFooter={this.renderFooter} // for rendering the typing indicator
            extraData={{ typing_user }} // so that the footer will be re-rendered when the typing user is updated
            onPressAvatar={this.viewLastReadMessage} // function to execute when user avatar is clicked

            loadEarlier={show_load_earlier} // for loading earlier messages
            onLoadEarlier={this.loadEarlierMessages}
          />
        )}

        // next: add modals      

      </View>
    );

Next, render the modal for showing the fullscreen version of the attached video files:

    <Modal isVisible={is_video_modal_visible}>
      <View style={styles.modal}>
        <TouchableOpacity onPress={this.hideModal.bind(this, 'video')}>
          <Icon name={"close"} size={20} color={"#565656"} style={styles.close} />
        </TouchableOpacity>
        <VideoPlayer uri={video_uri} />
      </View>
    </Modal>

Next, render the modal for showing the last message viewed by a specific member of the room:

    {
      viewed_user && viewed_message &&
      <Modal isVisible={is_last_viewed_message_modal_visible}>
        <View style={styles.modal}>
          <View style={styles.modal_header}>
            <Text style={styles.modal_header_text}>Last viewed msg: {viewed_user}</Text>
            <TouchableOpacity onPress={this.hideModal.bind(this, 'last_viewed_message')}>
              <Icon name={"close"} size={20} color={"#565656"} style={styles.close} />
            </TouchableOpacity>
          </View>

          <View style={styles.modal_body}>
            <Text>Message: {viewed_message}</Text>
          </View>
        </View>
      </Modal>
    }

Next, render the modal for showing the list of members of the room:

    {
      room_users &&
      <Modal isVisible={is_users_modal_visible}>
        <View style={styles.modal}>
          <View style={styles.modal_header}>
            <Text style={styles.modal_header_text}>Users</Text>
            <TouchableOpacity onPress={this.hideModal.bind(this, 'users')}>
              <Icon name={"close"} size={20} color={"#565656"} style={styles.close} />
            </TouchableOpacity>
          </View>

          <View style={styles.modal_body}>
            <FlatList
              keyExtractor={item => item.id.toString()}
              data={room_users}
              renderItem={this.renderUser}
            />
          </View>
        </View>
      </Modal>
    }

Here’s the function for rendering each individual user. This will show a green circle next to the user’s name if they are online, and a gray circle if they’re offline:

    renderUser = ({ item }) => {
      const online_status = item.presenceStore[item.id];

      return (
        <View style={styles.list_item_body}>
          <View style={styles.list_item}>
            <View style={[styles.status_indicator, styles[online_status]]}></View>
            <Text style={styles.list_item_text}>{item.name}</Text>
          </View>
        </View>
      );
    }

Here’s the code for showing the users modal:

    showUsersModal = () => {
      this.setState({
        is_users_modal_visible: true
      });
    }

Lastly, here’s the code for hiding a specific modal:

    hideModal = (type) => {
      const modal = this.modal_types[type];
      this.setState({
        [modal]: false
      });
    }

Attaching files

The code for rendering the custom button for attaching files is already in the starter branch, so all we have to do is add the code for actually attaching a file. To implement this, we use the React Native Document Picker package. We call the DocumentPicker.show function to show the default file picker of the device’s operating system. We use the DocumentPickerUtil.allFiles function to specify that all file types can be selected by the user. You can also supply a specific file type if you want. From their source code, you can see that those methods simply return a mime-type wild card. Though we will only have previews for png, jpg, mp3, and mp4 files:

    openFilePicker = async () => {
      await this.setState({
        is_picking_file: true // show the loader instead of the button for picking files to prevent the user from clicking it again
      });

      DocumentPicker.show({
        filetype: [DocumentPickerUtil.allFiles()],
      }, async (err, file) => {
        if (!err) { // if there's no error in picking the file

          // next: convert the file to a blob
        }

        this.setState({
          is_picking_file: false
        });
      });
    }

Next, we determine the mime type of the file and convert it to its base64 string representation using the react-native-fs package. From there, we convert it to a Blob using the rn-fetch-blob package. As you learned earlier, this acts as a polyfill for implementing Blobs in the React Native environment. Lastly, we temporarily store the name, type, and the Blob representation of the file so that we can easily get it later when we send a message:

    try {
      const file_type = mime.contentType(file.fileName);
      const base64 = await RNFS.readFile(file.uri, "base64");

      const file_blob = await Blob.build(base64, { type: `${file_type};BASE64` });

      this.attachment = {
        file_blob: file_blob,
        file_name: file.fileName,
        file_type: file_type
      };

      Alert.alert("Success", "File attached!");

    } catch (attach_err) {
      console.log("error attaching file: ", attach_err);
    }

Sending messages

The code for sending messages is already included in the starter branch. But we need to update it so that it sends multi-part messages (text with attachment). Replace the existing onSend method with the following. The main difference here is that the text message is included in the message_parts array. This allows us to supply different kinds of content to an individual message. In this case, the other type is an attachment. If the user has selected an attachment, we push it into the message_parts array:

    onSend = async ([message]) => {
      let message_parts = [
        { type: "text/plain", content: message.text }
      ];

      if (this.attachment) {
        const { file_blob, file_name, file_type } = this.attachment;
        message_parts.push({
          file: file_blob, // the file Blob
          name: file_name, // the file name
          type: file_type // the file type
        });
      }

      this.setState({
        is_sending: true // show the loading animation for sending a message
      });

      // next: send message
    }

Next, we send the message. The main difference here is that we’re now using the sendMultipartMessage method instead of the sendSimpleMessage method. Another important thing to note is that when a user sends a message, we also assume that they’ve already read the last message they received. So we set a read cursor using the ID of the last message they received:

    try {
      if (this.last_message_id) {
        const set_cursor_response = await this.currentUser.setReadCursor({
          roomId: this.room_id,
          position: this.last_message_id // the ID of the last message they received
        });
      }
      // send the message
      await this.currentUser.sendMultipartMessage({
        roomId: this.room_id,
        parts: message_parts
      });

      this.attachment = null;
      await this.setState({
        is_sending: false // hide the loading animation
      });
    } catch (send_msg_err) {
      console.log("error sending message: ", send_msg_err);
    }

Receiving messages

The code for receiving messages has already been included in the starter branch, but we need to update it so it sets the last_message_id that we used in the code for sending a message earlier:

    onReceive = async (data) => {
      this.last_message_id = data.id; // add this
      // ...
    }

Next, replace the existing getMessage function with the following:

    getMessage = async ({ id, sender, parts, createdAt }) => {

      const text = parts.find(part => part.partType === 'inline').payload.content;
      const attachment = parts.find(part => part.partType === 'attachment');

      const attachment_url = (attachment) ? await attachment.payload.url() : null;
      const attachment_type = (attachment) ? attachment.payload.type : null;

      const msg_data = {
        _id: id,
        text: text,
        createdAt: new Date(createdAt),
        user: {
          _id: sender.id,
          name: sender.name,
          avatar: `https://ui-avatars.com/api/?background=d88413&color=FFF&name=${sender.name}`
        }
      };

      if (attachment) {
        Object.assign(msg_data, { attachment: { url: attachment_url, type: attachment_type } });
      }

      if (attachment && attachment_type.indexOf('video') !== -1) {
        Object.assign(msg_data, { video: attachment_url });
      }

      if (attachment && attachment_type.indexOf('image') !== -1) {
        Object.assign(msg_data, { image: attachment_url });
      }

      return {
        message: msg_data
      };
    }

We’re using the UI Avatars API to automatically generate an image which matches the user’s initials.
In the code above, the most important distinction is the way we’re getting the text. In this case, the message is already considered a multipart message instead of a simple plain-text message. The first part you specify should always be the text. While the parts that follow can be a file attachment or a URL. The type of a specific part can either be inline, url, or attachment. In this case, we’re handling both inline and attachment types. If you specify, text/plain as the type for a message part, it’s automatically considered as inline. And if you specify a file to a message part, it’s automatically considered as an attachment.

To get to the actual text of an inline message part, we simply extract payload.content:

    const text = parts.find(part => part.partType === 'inline').payload.content;

But for an attachment message part, you have to call the payload’s url() method in order to get the direct URL to the file. This is what we use for previewing the files:

    const attachment = parts.find(part => part.partType === 'attachment');
    const attachment_url = (attachment) ? await attachment.payload.url() : null;

Next, add the renderMessage function. We don’t have this on the starter branch because we’re only rendering plain text previously. This time though, we need to cater for audio (mp3), and video (mp4) files as well. We only render a custom chat bubble if it’s an audio file because Gifted Chat already has a feature for rendering previews for image and video files. If it’s a video though, we supply the onLongPress prop to Gifted Chat’s <Message> component. This allows us to listen for the event when a chat bubble is long-pressed. It’s what we use to render the fullscreen video:

    renderMessage = (msg) => {
      const { attachment } = msg.currentMessage;
      const renderBubble = (attachment && attachment.type.indexOf('audio') !== -1) ? this.renderPreview.bind(this, attachment.url) : null;
      const onLongPress = (attachment  && attachment.type.indexOf('video') !== -1) ? this.onLongPressMessageBubble.bind(this, attachment.url) : null;

      const modified_msg = {
        ...msg,
        renderBubble,
        onLongPress,
        videoProps: {
          paused: true // don't autoplay the video because it's just a preview
        }
      }
      return <Message {...modified_msg} />
    }

When a chat bubble that has a custom onLongPress method is long pressed, the app opens the modal for viewing the fullscreen video:

    onLongPressMessageBubble = (link) => {
      this.setState({
        is_video_modal_visible: true,
        video_uri: link // the direct link to the video
      });
    }

Next, here’s the code for rendering a custom chat bubble. This makes use of the <AudioPlayer> component which has already been pre-coded:

    renderPreview = (uri, bubbleProps) => {
      const text_color = (bubbleProps.position == 'right') ? '#FFF' : '#000';
      const modified_bubbleProps = {
        ...bubbleProps
      };
      return (
        <ChatBubble {...modified_bubbleProps}>
          <AudioPlayer url={uri} />
        </ChatBubble>
      );
    }

Typing indicators

To implement the typing indicators, we use Chatkit’s isTypingIn method to set the room where the current user is currently typing in. This will trigger the onUserStartedTyping hook on the other members of the room who are currently online:

    onTyping = async () => {
      try {
        await this.currentUser.isTypingIn({ roomId: this.room_id });
      } catch (typing_err) {
        console.log("error setting is typing: ", typing_err);
      }
    }

When the onUserStartedTyping hook is triggered, the following function is executed. This shows the chat UI’s footer text which displays the typing indicator:

    startTyping = (user) => {
      this.setState({
        is_typing: true,
        typing_user: user.name
      });
    }

If the user stops typing, the onUserStoppedTyping hook is automatically triggered so the following function is executed as well. This hides the chat UI’s footer text:

    stopTyping = (user) => {
      this.setState({
        is_typing: false,
        typing_user: null
      });
    }

Here’s the code for rendering the custom footer. All it does is show the name of the user who is currently typing in the chat room:

    renderFooter = () => {
      const { is_typing, typing_user } = this.state;
      if (is_typing) {
        return (
          <View style={styles.footerContainer}>
            <Text style={styles.footerText}>
              {typing_user} is typing...
            </Text>
          </View>
        );
      }
      return null;
    }

Read receipt

The last feature that we’re going to implement is the read receipt. This allows the current user to view the last message that another member of the room has read (or get all of the user’s read cursors). This makes use of Chatkit’s readCursor method to fetch the ID of the latest message that a specific user has marked as read (via the setReadCursor method):

    viewLastReadMessage = async (data) => {
      try {
        const cursor = await this.currentUser.readCursor({
          userId: data.userId,
          roomId: this.room_id
        });

        const viewed_message = this.state.messages.find(msg => msg._id == cursor.position);

        await this.setState({
          viewed_user: data.name,
          is_last_viewed_message_modal_visible: true,
          viewed_message: viewed_message.text
        });
      } catch (view_last_msg_err) {
        console.log("error viewing last message: ", view_last_msg_err);
      }
    }

Users can only view the message if it has already been loaded in their chat screen. If the message doesn’t show up then it means that the member they selected hasn’t read any of the recent messages that the current user is able to view.## Running the Chat app

At this point, you can now run the app. Start by running the server and exposing it to the internet using ngrok:

    cd server
    yarn start
    ~/Downloads/ngrok http 5000

Update the login and chat screen with your ngrok URL:

    // src/screens/Login.js, src/screens/Group.js, src/screens/Chat.js
    const CHAT_SERVER = "YOUR NGROK HTTPS URL";

Next, delete the node_modules/react-native-gifted-chat/node_modules/react-native-video folder. This is because we also have React Native Video as a dependency for the app. It’s conflicting with Gifted Chat’s copy, so we need to delete it.

Finally, run the app:

    react-native run-android
    react-native run-ios

Before you log a user in, be sure to visit <a href="http://localhost:5000/users" target="_blank">http://localhost:5000/users</a> on your browser to populate the users array in the server.

Conclusion

In this tutorial, you learned how to use various **Chatkit **features within a React Native app. Specifically, you learned how to implement roles and permissions, read receipts, file uploads, and typing indicators.

You can find the code in this GitHub repo.

Building a Music Streaming App using React Native

Building a Music Streaming App using React Native

In this post, I’ll walk through the process of building a music streaming similar to Spotify.

In this post, I’ll walk through the process of building a music streaming similar to Spotify.

React Native is a framework for building native apps using React and Javascript.

What’s really cool is that the exact same code is going to work for both iOS and Android, and the apps are going to be 100 % native (no WebViews or anything).

We’re going to build the ‘Now Playing’ screen, which looks like this on Spotify:

Somebody stole my car radio, so now I’m gonna steal Spotify’s UI.

Since I’m not a designer, and I really like Spotify’s clean design, we’re going to use this as a reference for our building our player. In the third image, I’ve opened up a screenshot in Keynote, and added rulers all over the image to accurately measure the positions, margins, font sizes etc. for all the different elements in the UI. We’ll use these measurements to style our own app’s UI.

Now that we have a (stolen) design, we’re ready to start coding. So let’s create a new React Native project. Open a up a terminal window and run the following commands :

$ npm install [email protected]
$ react-native init ReactMusic

Phew! That took a while, didn’t it? We’re almost ready now, just a few more commands to run :

$ react-native run-ios      # Launch an iOS emulator and run the app
$ android avd &             # Launch an Android emulator
$ react-native run-android  # Run the app on the Android emulator
$ subl .                    # Open up the project in Sublime Text

If the last command doesn’t work for you, just open up the directory ‘ReactMusic’ in any editor of your choice, or do this. Your app should be up and running on both emulators, and your screen should look something like this :

Open up index.ios.js and index.android.js. You’ll notice that they have the same code. We’re going to get rid of all of it and start from scratch. Let’s create a directory called app inside the project’s root directory. Then create a file app/App.js and with the following code :

import React, { Component } from 'react';

import {
  View,
  Text,
} from 'react-native';

export default class App extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={{color: 'white'}}>
          Hello React Native!
        </Text>
      </View>
    );
  }
}

const styles = {
  container: {
    flex: 1,
    backgroundColor: 'rgb(4,4,4)',
  },
}

Now, we can remove all the code from index.ios.js and index.android.js, and simply render the component App in both of them :

import React, { Component } from 'react';
import { AppRegistry } from 'react-native';
import App from './app/App';

AppRegistry.registerComponent('ReactMusic', () => App);

If you reload the emulators (Cmd+R for iOS, and Fn+F2 for Android), you should now see a black screen with some white text on it. You can also set up Live Reloading in the emulators to automatically reload the Javascript every time you save a file after making a change.

If we go back and take another look at the UI, we can see that it is made up of 5 main parts :

We’re going to create one component for each of these parts, starting with the Header, which is really just a title and two buttons. We’re going to use TouchableOpacity to render the buttons. Create a directory ‘img’ in the root directory of the project to store icons and images. You can get the icons for the header from Google’s Material Icons collection. Download the icons ‘keyboard arrow down’ and ‘queue music’ and copy the files from the ‘ios’ directory of the icon-set to the ‘img’ directory of the project. You can learn more about rendering images and icons here. Here’s the code for Header :

const Header = ({ message, onDownPress, onQueuePress, onMessagePress }) => (
  <View style={styles.container}>
    <TouchableOpacity onPress={onDownPress}>
      <Image style={styles.button}
        source={require('../img/ic_keyboard_arrow_down_white.png')} />
    </TouchableOpacity>
    <Text onPress={onMessagePress}
      style={styles.message}>{message.toUpperCase()}</Text>
    <TouchableOpacity onPress={onQueuePress}>
      <Image style={styles.button}
        source={require('../img/ic_queue_music_white.png')} />
    </TouchableOpacity>
  </View>
);

Link to full code

I’ve left out the imports and the styling for the sake of brevity. Follow the link in the description below the gist for the full code. You can put this code in app/Header.js, and then import and use the Header component inside app/App.js :

import Header from './Header';

class App extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Header message="Playing from Charts" />
      </View>
    );
  }
}

Next up, we have a really simple component for displaying the album art :

const AlbumArt = ({ url, onPress}) => (
  <View style={styles.container}>
    <TouchableOpacity onPress={onPress}>
      <Image style={styles.image} source={{uri: url}} />
    </TouchableOpacity>
  </View>
);

Here is the full code.

Next, we have the track title and artist :

const TrackDetails = ({
  title,
  artist,
  onAddPress,
  onMorePress,
  onTitlePress,
  onArtistPress,
}) => (
  <View style={styles.container}>
    <TouchableOpacity onPress={onAddPress}>
      <Image style={styles.button}
        source={require('../img/ic_add_circle_outline_white.png')} />
    </TouchableOpacity>
    <View style={styles.detailsWrapper}>
      <Text style={styles.title} onPress={onTitlePress}>{title}</Text>
      <Text style={styles.artist} onPress={onArtistPress}>{artist}</Text>
    </View>
    <TouchableOpacity onPress={onMorePress}>
      <View style={styles.moreButton}>
        <Image style={styles.moreButtonIcon}
          source={require('../img/ic_more_horiz_white.png')} />
      </View>
    </TouchableOpacity>
  </View>
);

Here is the full code.

For the Seek Bar, we’ll use react-native-slider, which has better cross platform styling options.

$ npm install --save react-native-slider

Then, we can implement the Seek Bar :

var Slider = require('react-native-slider');

function pad(n, width, z=0) {
  n = n + '';
  return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
}

const minutesAndSeconds = (position) => ([
  pad(Math.floor(position / 60), 2),
  pad(position % 60, 2),
]);

const SeekBar = ({
  trackLength,
  currentPosition,
  onSeek,
  onSlidingStart,
}) => {
  const elapsed = minutesAndSeconds(currentPosition);
  const remaining = minutesAndSeconds(trackLength - currentPosition);
  return (
    <View style={styles.container}>
      <View style={{flexDirection: 'row'}}>
        <Text style={styles.text}>
          {elapsed[0] + ":" + elapsed[1]}
        </Text>
        <View style={{flex: 1}} />
        <Text style={[styles.text, {width: 40}]}>
          {trackLength > 1 && "-" + remaining[0] + ":" + remaining[1]}
        </Text>
      </View>
      <Slider
        maximumValue={Math.max(trackLength, 1, currentPosition + 1)}
        onSlidingStart={onSlidingStart}
        onSlidingComplete={onSeek}
        value={currentPosition}
        style={styles.slider}
        minimumTrackTintColor='#fff'
        maximumTrackTintColor='rgba(255, 255, 255, 0.14)'
        thumbStyle={styles.thumb}
        trackStyle={styles.track}/>
    </View>
  );
};

Here is the full code

Let’s also add a component for the controls :

const Controls = ({
  paused,
  shuffleOn,
  repeatOn,
  onPressPlay,
  onPressPause,
  onBack,
  onForward,
  onPressShuffle,
  onPressRepeat,
  forwardDisabled,
}) => (
  <View style={styles.container}>
    <TouchableOpacity activeOpacity={0.0} onPress={onPressShuffle}>
      <Image style={[styles.secondaryControl, shuffleOn ? [] : styles.off]}
        source={require('../img/ic_shuffle_white.png')}/>
    </TouchableOpacity>
    <View style={{width: 40}} />
    <TouchableOpacity onPress={onBack}>
      <Image source={require('../img/ic_skip_previous_white_36pt.png')}/>
    </TouchableOpacity>
    <View style={{width: 20}} />
    {!paused ?
      <TouchableOpacity onPress={onPressPause}>
        <View style={styles.playButton}>
          <Image source={require('../img/ic_pause_white_48pt.png')}/>
        </View>
      </TouchableOpacity> :
      <TouchableOpacity onPress={onPressPlay}>
        <View style={styles.playButton}>
          <Image source={require('../img/ic_play_arrow_white_48pt.png')}/>
        </View>
      </TouchableOpacity>
    }
    <View style={{width: 20}} />
    <TouchableOpacity onPress={onForward}
      disabled={forwardDisabled}>
      <Image style={[forwardDisabled && {opacity: 0.3}]}
        source={require('../img/ic_skip_next_white_36pt.png')}/>
    </TouchableOpacity>
    <View style={{width: 40}} />
    <TouchableOpacity activeOpacity={0.0} onPress={onPressRepeat}>
      <Image style={[styles.secondaryControl, repeatOn ? [] : styles.off]}
        source={require('../img/ic_repeat_white.png')}/>
    </TouchableOpacity>
  </View>
);

Here is the full code.

Finally, we can put all these stateless components together in App.js to check out the UI and play around with it :

import Header from './Header';
import AlbumArt from './AlbumArt';
import TrackDetails from './TrackDetails';
import SeekBar from './SeekBar';
import Controls from './Controls';

class App extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Header message="Playing from Charts" />
        <AlbumArt url="http://36.media.tumblr.com/14e9a12cd4dca7a3c3c4fe178b607d27/tumblr_nlott6SmIh1ta3rfmo1_1280.jpg" />
        <TrackDetails title="Stressed Out"
          artist="Twenty One Pilots" />
        <SeekBar trackLength={204} currentPosition={156} />
        <Controls />
      </View>
    );
  }
}

Here’s a comparison, with a screenshot from Spotify on the left and our app on the right :

Not too bad, eh? Now to acutally play the audio, we’ll use react-native-video. Here’s how to use it:

$ npm install react-native-video — save
$ npm install -g rnpm
$ rnpm link react-native-video
$ react-native run-ios
$ react-native run-android

Now let’s hook up the play and pause buttons in the component called Player :

export default class Player extends Component {
  constructor(props) {
    super(props);
    this.state = {
      paused: true,
      totalLength: 1,
      currentPosition: 0,
      selectedTrack: 0,
    };
  }

  setDuration(data) {
    this.setState({totalLength: Math.floor(data.duration)});
  }

  setTime(data) {
    this.setState({currentPosition: Math.floor(data.currentTime)});
  }

  seek(time) {
    time = Math.round(time);
    this.refs.audioElement && this.refs.audioElement.seek(time);
    this.setState({
      currentPosition: time,
      paused: false,
    });
  }

  render() {
    const track = this.props.tracks[this.state.selectedTrack];
    const video = (
      <Video source={{uri: track.audioUrl}} // Can be a URL or a local file.
        ref="audioElement"
        paused={this.state.paused}               // Pauses playback entirely.
        onLoad={this.setDuration.bind(this)}    // Callback when video loads
        onProgress={this.setTime.bind(this)}    // Callback every ~250ms with currentTime
        style={styles.audioElement} />
    );

    return (
      <View style={styles.container}>
        <StatusBar hidden={true} />
        <Header message="Playing From Charts" />
        <AlbumArt url={track.albumArtUrl} />
        <TrackDetails title={track.title} artist={track.artist} />
        <SeekBar
          onSeek={this.seek.bind(this)}
          trackLength={this.state.totalLength}
          onSlidingStart={() => this.setState({paused: true})}
          currentPosition={this.state.currentPosition} />
        <Controls
          onPressPlay={() => this.setState({paused: false})}
          onPressPause={() => this.setState({paused: true})}
          paused={this.state.paused}/>
        {video}
      </View>
    );
  }
}

Full code is here

And we can use it in the app by defining a few tracks :

import React, { Component } from 'react';
import Player from './Player';

export const TRACKS = [
  {
    title: 'Stressed Out',
    artist: 'Twenty One Pilots',
    albumArtUrl: "http://36.media.tumblr.com/14e9a12cd4dca7a3c3c4fe178b607d27/tumblr_nlott6SmIh1ta3rfmo1_1280.jpg",
    audioUrl: "http://russprince.com/hobbies/files/13%20Beethoven%20-%20Fur%20Elise.mp3",
  },
  {
    title: 'Love Yourself',
    artist: 'Justin Bieber',
    albumArtUrl: "http://arrestedmotion.com/wp-content/uploads/2015/10/JB_Purpose-digital-deluxe-album-cover_lr.jpg",
    audioUrl: 'http://oranslectio.files.wordpress.com/2013/12/39-15-mozart_-adagio-fugue-in-c-minor-k-546.mp3',
  },
  {
    title: 'Hotline Bling',
    artist: 'Drake',
    albumArtUrl: 'https://upload.wikimedia.org/wikipedia/commons/c/c9/Drake_-_Hotline_Bling.png',
    audioUrl: 'http://russprince.com/hobbies/files/13%20Beethoven%20-%20Fur%20Elise.mp3',
  },
];

export default class App extends Component {
  render() {
    return <Player tracks={TRACKS} />
  }
}

With a little more work, we can connect all the buttons. After everything, we will reach

That’s it! You can find the full code for this blog post here

Styling in React Native

Styling in React Native

Styling in React Native: Explore the best ways to style a React Native application

Explore the best ways to style a React Native application

If you are just started with React Native or even you are experienced with it, you may find the styling a little bit challenging for the first time. that happens when you come especially from the web background and when you try to write React Native style as you usually do when you write CSS on the web and you find out what you expected. the method that React Native use for styling use CSS properties and it’s actually not the same as normal CSS. anyway, we are going to see how we do styling in React Native, in addition, to React Native style method styling method, we will explore the different ways to style a React Native application.

React Native style method

React Native give us two powerful ways by default to style our application :

Style props

You can add styling to your component using style props you simply add style props to your element it accepts an object of properties.

import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';
export default class App extends Component<Props> {
render() {
return (
<View style={{flex:1,justifyContent:"center",backgroundColor:"#fff", alignItems:"center"}}>
<View style={{width:200,height:150,backgroundColor:"red",padding:10}}>
<Text style={{fontSize:20, color:"#666"}}>Styled with style props</Text>
</View>
</View>
);
}
}

If you look at our code, we use the CSS properties with the capital letter, notice that some properties are not supported in react-native, an error will throw when you try to use any unsupported property, CSS3 animations are not supported instead React Native leave that to the animation API

React Native use Flexbox for layout, it’s a great tool to define the layout of your React Native app, elsewhere it’s not working as the same as in CSS but genuinely it’s easy to use and flexible.

import React, {Component} from 'react';
import { Text, View} from 'react-native';
export default class App extends Component<Props> {
render() {
return (
<View style={{flex:1,justifyContent:"center",backgroundColor:"#fff", alignItems:"stretch"}}>
<View style={{flex:1,backgroundColor:"red"}}>
<Text style={{fontSize:20, color:"#fff"}}>Item number 1</Text>
</View>
<View style={{flex:1,backgroundColor:"blue"}}>
<Text style={{fontSize:20, color:"#fff"}}>Item number 1</Text>
</View>
<View style={{flex:1,backgroundColor:"purple"}}>
<Text style={{fontSize:20, color:"#fff"}}>Item number 1</Text>
</View>
<View style={{flex:1,backgroundColor:"orange"}}>
<Text style={{fontSize:20, color:"#fff"}}>Item number 1</Text>
</View>
</View>
);
}
}

React native flexbox is a great way to deal with the layout manipulation it so easy to design your layout but you need some basic understanding of flexbox to get your head it, you can check this great article for a better understanding.

Using StyleSheet

if you have a large code base or you want to set many properties to your elements, writing our styling rules directly inside style props will make our code more complex that’s why React Native give us another way that let us write a concise code using StyleSheet method:

First, make sure to import StyleSheet from react-native

import { StyleSheet} from 'react-native';

And try to assign some style properties using create method that takes an object of properties.

const styles = StyleSheet.create({
container: {
flex:1,
justifyContent:"center",
backgroundColor:"#fff",
alignItems:"stretch"
},
title: {
fontSize:20,
color:"#fff"
},
item1: {
backgroundColor:"orange",
flex:1
},
item2: {
backgroundColor:"purple",
flex:1
},
item3: {
backgroundColor:"yellow",
flex:1
},
item4: {
backgroundColor:"red",
flex:1
},
});

And then we pass the styles object to our component via the style props:

<View style={styles.container}>
<View style={styles.item1}>
<Text style={{fontSize:20, color:"#fff"}}>Item number 1</Text>
</View>
<View style={styles.item2}>
<Text style={{fontSize:20, color:"#fff"}}>Item number 1</Text>
</View>
<View style={styles.item3}>
<Text style={{fontSize:20, color:"#fff"}}>Item number 1</Text>
</View>
<View style={styles.item4}>
<Text style={{fontSize:20, color:"#fff"}}>Item number 1</Text>
</View>
</View>

Our code looks more concise with StyleSheet method and the result still the same:

The styling method that React Native use has really great features that allow as to do some dynamic styling but it’s limited especially when it comes to applying some CSS properties that aren’t supported by React Native, for example, applying box-shadow to your components you may have to do the following :

const Card=()=>(

 <View style={styles.card}>

  <Text>Hello!</Text>
</View>
)

//our style

 const styles=StyleSheer.create({

card:{
width:100,
height:120,
shadowColor: '#000000',
    shadowOffset: {
      width: 0,
      height: 3
    },
    shadowRadius: 5,
    shadowOpacity: 1.0} 
})

Whereas if we have to do the same and adding shadow in CSS:

.card{
width:100px,
height:120px,
box-shadow:0 0 5 #000000;
}

It’s easier to do it with CSS, we all would love to the same in React native, unfortunately, we can’t write CSS directly in React Native. but no worries the good news is styled-component support React Native 💣

styled-component in React Native

Yes, you can use styled-component with React native so you can write your styles in React Native as you write normal CSS. and it’s easy to include it in your project and it doesn’t need any linking just run this following command inside the root directory of your app to install it:

yarn add styled-components

And then simply start using it in your components:

import React, {Component} from 'react';
import { StyleSheet,Text, View} from 'react-native';
import styled from 'styled-components'
const Container=styled.View`
    flex:1;
    padding:50px 0;
    justify-content:center;
    background-color:#f4f4f4;
    align-items:center
`
const Title=styled.Text`
font-size:20px;
text-align:center;
 color:red;
`
const Item=styled.View`
flex:1;
border:1px solid #ccc;
margin:2px 0;
border-radius:10px;
box-shadow:0 0 10px #ccc;
background-color:#fff;
width:80%;
padding:10px;
 
`

export default class App extends Component {
  render() {
    return (
      <Container>
             <Item >
             <Title >Item number 1</Title>
             </Item>
             <Item >
             <Title >Item number 2</Title>
             </Item>
             <Item >
             <Title >Item number 3</Title>
             </Item>
             <Item >
             <Title >Item number  4</Title>
             </Item>
      </Container>
    );
  }
}

So cool 😃. now you can totally write CSS with React Native and we give that to [styled-components]([https://www.styled-components.com/docs/basics#react-native)](https://www.styled-components.com/docs/basics#react-native) "https://www.styled-components.com/docs/basics#react-native)") , I prefer to use styled-components to style my React Native elements the day since I found out that they support React Native it gives me more freedom to make the style I want so easily and you better use [styled-components]([https://www.styled-components.com/docs/basics#react-native)](https://www.styled-components.com/docs/basics#react-native) "https://www.styled-components.com/docs/basics#react-native)") from now and the get the benefits of CSS to make nice UI it makes your code cleaner, you can separate your styles to a single file far from your components that will make your code more organized. in another hand you can use the great features that styled-components provide us such as theming and passing props so you can make a dynamic style like the following:

import React, {Component} from 'react';
import { StyleSheet,Text, View} from 'react-native';
import styled from 'styled-components'
const Container=styled.View`
    flex:1;
    padding:50px 0;
    justify-content:center;
    background-color:#f4f4f4;
    align-items:center
`
const Title=styled.Text`
font-size:20px;
text-align:center;
 color:red;
`
const Item=styled.View`
flex:1;
border:1px solid #ccc;
margin:2px 0;
border-radius:10px;
box-shadow:0 0 10px #ccc;
height:200px;
// execute a specific style based on the props
background-color:${props=>props.transparent?"red":"blue"};
width:80%;
padding:10px;
 
`
const Shape=styled.View`
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
`

export default class App extends Component {
  render() {
    return (
      <Container>
             <Item transparent>{/*pass the props to the components*/}
             <Title >Item number 1</Title>
             </Item>
             <Item  primary>
             <Title >Item number 1</Title>
             </Item>
             <Item  transparent>
             <Title >Item number 1</Title>
             </Item>
             <Item  primary>
             <Title >Item number 1</Title>
             </Item>
      </Container>
    );
  }
}

And here the result:

So awesome 💪!!

Sometimes you may want to draw some complex shapes that include circles and some specific style or gradient backgrounds for example (CSS gradient background not supported in React Native). you might think of using some methods that CSS provide like clip-path or any other method that allows you to make complex shapes in CSS, unfortunately, those methods are not supported in React Native at this time even if you use styled-components that lets you use the CSS properties into React Native at this situation, we have to use some alternative solutions like using SVG

Using react-native-svg to draw specific shapes

React Native community brings react-native-svg that allows you to use the SVG in React Native. you can add it to your projectusing yarn or npm:

// using yarn

yarn add react-native

// npm 
npm i react-native 

And then make sure to link it by running the following command line:

react-native link react-native-svg

And now let’s start something with it:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow
 * @lint-ignore-every XPLATJSCOPYRIGHT1
 */

import React, { Component } from "react";
import { StyleSheet, Text, View } from "react-native";
import styled from "styled-components";
import Svg, {
  Circle,
  Ellipse,
  G,
  TSpan,
  TextPath,
  Path,
  Polygon,
  Polyline,
  Line,
  Rect,
  Use,
  Image,
  Symbol,
  Defs,
  LinearGradient,
  RadialGradient,
  Stop,
  ClipPath,
  Pattern,
  Mask
} from "react-native-svg";

const Container = styled.View`
  flex: 1;
  padding: 50px 0;
  justify-content: center;
  background-color: #f4f4f4;
  align-items: center;
`;
const Title = styled.Text`
  font-size: 20px;
  text-align: center;
  color: red;
`;
const Item = styled.View`
  flex: 1;
  border: 1px solid #ccc;
  margin: 2px 0;
  border-radius: 10px;
  box-shadow: 0 0 10px #ccc;
  height: 200px;
  background-color: ${props => (props.transparent ? "red" : "blue")};
  width: 80%;
  padding: 10px;
`;

export default class App extends Component {
  render() {
    return (
      <Container>
        <Svg height="150" width="300">
          <Defs>
            <LinearGradient id="grad" x1="0" y1="0" x2="170" y2="0">
              <Stop offset="0" stopColor="rgb(255,255,0)" stopOpacity="0" />
              <Stop offset="1" stopColor="red" stopOpacity="1" />
            </LinearGradient>
          </Defs>
          <Ellipse cx="150" cy="75" rx="85" ry="55" fill="url(#grad)" />
        </Svg>

        <Svg height="100" width="100">
          <Defs>
            <RadialGradient
              id="grad"
              cx="50%"
              cy="50%"
              rx="50%"
              ry="50%"
              fx="50%"
              fy="50%"
              gradientUnits="userSpaceOnUse"
            >
              <Stop offset="0%" stopColor="#ff0" stopOpacity="1" />
              <Stop offset="100%" stopColor="#00f" stopOpacity="1" />
            </RadialGradient>
            <ClipPath id="clip">
              <G scale="0.9" x="10">
                <Circle cx="40" cy="30" r="20" />
                <Ellipse cx="60" cy="70" rx="20" ry="10" />
                <Rect x="65" y="15" width="50" height="50" />
                <Polygon points="20,60 20,80 50,70" />
                <Text
                  x="50"
                  y="30"
                  fontSize="32"
                  fonWeight="bold"
                  textAnchor="middle"
                  scale="1.2"
                >
                  Q
                </Text>
              </G>
            </ClipPath>
          </Defs>
          <Rect
            x="0"
            y="0"
            width="100"
            height="100"
            fill="url(#grad)"
            clipPath="url(#clip)"
          />
        </Svg>
        <Svg height="100" width="300">
          <Defs>
            <G id="shape">
              <G>
                <Circle cx="50" cy="50" r="50" />
                <Rect x="50" y="50" width="60" height="50" />
                <Circle cx="50" cy="50" r="5" fill="#c00" />
              </G>
            </G>
          </Defs>
          <Use href="#shape" x="20" y="0" />
          <Use href="#shape" x="170" y="0" />
        </Svg>
      </Container>
    );
  }
}

Examples from react-native-svg docs](https://github.com/react-native-community/react-native-svg#clippath) "https://github.com/react-native-community/react-native-svg#clippath)")

The examples above are from react-native-svg docs](https://github.com/react-native-community/react-native-svg) "https://github.com/react-native-community/react-native-svg)")

As you see you can go so far with react-native-svg in making specific shapes and gradient backgrounds and the things you usually do with SVG. you can check the official docs to see the available options.

Wrapping up

You can still use the React Native style method to make what you need but it remains complex when we want to make some complex styles so you need to use some alternative tools like react-native-svg to do that, for normal styling I like to use styled components it makes doing things easier for me and I hope the React Native community bring more supports of other style methods that will give us the freedom of making things easily