How to create a music streaming app - Expo(RN)
Tags: reactnative, expo, music, firebase
Published: 2020-04-25 14:48:21

I started working on a music streaming app a week ago and it wasn't easy as i thought. Expo provides tools needed to build the app but putting them together and making them worked as one is the hard part.
In this article i am going to go through the basics of streaming music in expo app.

## Prerequisites
1. Expo (React native)
2. Firebase

We are going to use **firebase** to host the songs.

### The Playlist
In our App.js

```javascript
import React from 'react';
import { Text, FlatList, Image, View } from 'react-native';
import { TouchableOpacity } from 'react-native-gesture-handler';

import { Audio } from 'expo-av';
import firebase from 'firebase';
import { FontAwesome } from '@expo/vector-icons';

export default class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      allSongs: [],
      currentSongData: {},
      playingStatus: 'nosound',
      paused: false
    };
  }

  componentDidMount() {
    firebase
      .database()
      .ref('trending/')
      .on('value', snapshot => {
        let array = [];

        snapshot.forEach(child => {
          array.push(child.toJSON());
        });
        this.setState({
          allSongs: array
        });
      });
  }

  render() {
    return (
      <React.Fragment>
        {/* THE PLAYLIST */}
        <Text>Playlist</Text>

        {this.state.allSongs && (
          <FlatList
            contentContainerStyle={styles.containerContent}
            data={this.state.allSongs}
            keyExtractor={itemObj => itemObj.id.toString()}
            renderItem={itemObj => {
              const { item } = itemObj;

              return (
                <TouchableOpacity
                  onPress={() => {
                    this.setState({
                      currentSongData: item
                    });
                  }}
                >
                  <Text>{item.songTitle}</Text>
                  <Text>{item.artist}</Text>
                </TouchableOpacity>
              );
            }}
          />

        {/* THE PLAYER - player code goes here */}

        )}
      </React.Fragment>
    );
  }
}
```
We set our inital states for data
```javascript
constructor(props) {
    super(props);

    this.state = {
      allSongs: [],
      currentSongData: {}
    };
  }
```

Then make a firebase call when the component mounts
```javascript
componentDidMount() {
    firebase
      .database()
      .ref('trending/')
      .on('value', snapshot => {
        let array = [];

        snapshot.forEach(child => {
          array.push(child.toJSON());
        });
        this.setState({
          allSongs: array
        });
      });
  }
```
We user the __forEach__ method so that we can make the values and push them to an empty array.
After, we update our state with the new array 
```javascript
this.setState({
   allSongs: array
});
```
In our render method, we use a __FlatList__ to return each value in the array.
```javascript
{this.state.allSongs && (
          <FlatList
            contentContainerStyle={styles.containerContent}
            data={this.state.allSongs}
            keyExtractor={itemObj => itemObj.id.toString()}
            renderItem={itemObj => {
              const { item } = itemObj;

              return (
                <TouchableOpacity
                  onPress={() => {
                    this.setState({
                      currentSongData: item
                    });
                  }}
                >
                  <Text>{item.songTitle}</Text>
                  <Text>{item.artist}</Text>
                </TouchableOpacity>
              );
            }}
          />
        )}
```
## The Player
```javascript
let song = this.state.currenSongData
<View>
        <TouchableOpacity>
          <View>
            <Image
              style={{
                height: 40,
                width: 40,
                borderRadius: 3,
                backgroundColor: 'gray'
              }}
              source={{ uri: song.image }}
            />
          </View>
          {song && (
            <View>
              <View>
                <Text>{`${song.songTitle} · `}</Text>
                <Text>{song.artist}</Text>
              </View>
              <View>
                <FontAwesome
                  color={purple}
                  name="play"
                  size={14}
                />
                <Text style={styles.device}>NOW PLAYING</Text>
              </View>
            </View>
          )}
          <TouchableOpacity
            onPress={() => togglePlay()}
          >
            <FontAwesome color={colors.white} name={iconPlay} size={28} />
          </TouchableOpacity>
        </TouchableOpacity>
      </View>
```
This is the interface, we display the cover art of the song, song title and artist.

### Playing the song

#### funtions

__togglePlay()__
```javascript
let togglePlay = async () => {
      this.setState(prev => ({
        paused: !prev.paused
      }));
      this._playAndPause();
    };
```

__this.playAndPause()__
```javascript
 _playAndPause = () => {
    switch (this.state.playingStatus) {
      case 'nosound':
        this._playRecording();
        break;
      case 'donepause':
      case 'playing':
        this._pauseAndPlayRecording();
        break;
    }
  };
```

__this.playRecording()__
```javascript
async _playRecording() {
    // alert('playing it');
    const { sound } = await Audio.Sound.createAsync(
      { uri: this.state.currentSongData.songLink },
      {
        shouldPlay: true,
        isLooping: false
      },
      this._updateScreenForSoundStatus
    );
    this.sound = sound;
    this.setState({
      playingStatus: 'playing'
    });
  }
```

**_pauseAndPlayRecording()**
```javascript
 async _pauseAndPlayRecording() {
    if (this.sound != null) {
      if (this.state.playingStatus == 'playing') {
        console.log('pausing...');
        await this.sound.pauseAsync();
        console.log('paused!');
        this.setState({
          playingStatus: 'donepause'
        });
      } else {
        console.log('playing...');
        await this.sound.playAsync();
        console.log('playing!');
        this.setState({
          playingStatus: 'playing'
        });
      }
    }
  }
```

**_updateScreenForSoundStatus()**
```javascript
 _updateScreenForSoundStatus = status => {
    if (status.isPlaying && this.state.playingStatus !== 'playing') {
      this.setState({ playingStatus: 'playing' });
    } else if (!status.isPlaying && this.state.playingStatus === 'playing') {
      this.setState({ playingStatus: 'donepause' });
    }
  };
```

**_syncPauseAndPlayRecording()**
```javascript
  _syncPauseAndPlayRecording() {
    if (this.sound != null) {
      if (this.state.playingStatus == 'playing') {
        this.sound.pauseAsync();
      } else {
        this.sound.playAsync();
      }
    }
  }
```

This is a technical writing and you need knowledge in expo and react native to understand.
However you can send me a message on twitter full for source code (github repo)
Twitter : [@cirlorm_io](https://twitter.com/cirlorm_io)

Photo by Nadine Shaabana on Unsplash
Photo by NeONBRAND on Unsplash