Simple Spotify Clone In React Native

A big shoutout to Hitesh Choudhary sir for the amazing react native series. From a complete beginner in react native to building a music app with many concepts, It has been an amazing journey till now and looking forward to learning new things.

Extra Feature by me

I have added a extra feature that is the user can now play the song from any point using the slider. It is a simple thing that i wanted to try out and i did surprisingly by using only few lines of code i was able to do it.

  const skipInSong =async ()=>{
    await TrackPlayer.seekTo(seekPosition)
  }

<Slider 
      value={position}
      minimumValue={0}
      maximumValue={duration}
      thumbTintColor='#fff'
      maximumTrackTintColor='#fff'
      style={styles.sliderContainer}
      onValueChange={(seek)=>{
        skipInSong(seek)
      }}
 />

Step 1 : Creating a music service

Install the react native track player and create a new file for the music player service and create a file that has the list of tracks you want to play.

import TrackPlayer, {Event, RepeatMode} from 'react-native-track-player';
import {playListData} from './sptifyMusic08/constants';

export async function playerSetup() {
  let isStep = false;
  try {
    await TrackPlayer.getCurrentTrack();
    isStep = true;
  } catch (error) {
    await TrackPlayer.setupPlayer();
    isStep = true;
  } finally {
    return isStep;
  }
}

export async function addTrack() {
  await TrackPlayer.add(playListData);
  await TrackPlayer.setRepeatMode(RepeatMode.Queue);
}

export async function playbackService() {
  TrackPlayer.addEventListener(Event.RemotePlay, () => {
    TrackPlayer.play();
  });

  TrackPlayer.addEventListener(Event.RemotePause, () => {
    TrackPlayer.pause();
  });

  TrackPlayer.addEventListener(Event.RemoteNext, () => {
    TrackPlayer.skipToNext();
  });

  TrackPlayer.addEventListener(Event.RemotePrevious, () => {
    TrackPlayer.skipToPrevious();
  });

  TrackPlayer.addEventListener(Event.RemoteSeek, () => {
    TrackPlayer.seekTo();
  });

}

// Import the service inside the index.js
TrackPlayer.registerPlaybackService(() => playbackService);

Step 2 : Setup a player

Setup the player using the playerSetup() we built in the service file

const [isPlayerReady,setIsPlayerReady]=useState(false)
    async function setup(){
        const isSetup=await playerSetup()
        if(isSetup){
            await addTrack()
        }

        setIsPlayerReady(isSetup)
    }

    useEffect(() => {
      setup()
    }, [])

Step 3 : Setting up the Control Center

Make use of 'usePlaybackState()' and TrackPlayer to create different methods to use in control centre.


const ControlCenter = () => {

    const playBackState = usePlaybackState()

    const skipToNext = async ()=>{
        await TrackPlayer.skipToNext()
    }


    const skipToPrevius = async ()=>{
        await TrackPlayer.skipToPrevious()
    }

    const togglePlayback= async (playback: State)=>{
        const currentTrack = await TrackPlayer.getActiveTrackIndex()
        if(currentTrack){
            if(playback===  State.Paused || playback===State.Ready){
                await TrackPlayer.play()
            }else{
                await TrackPlayer.pause()
            }
        }
    }

  return (
    <View style={styles.container}>
        <Pressable onPress={skipToPrevius}>
            <Icon  style={styles.icon} name="skip-previous" size={75} />
        </Pressable>


        <Pressable onPress={()=>togglePlayback(playBackState.state)}>
            <Icon  style={styles.icon}
             name={playBackState.state === State.Playing ? "pause" : "play-arrow"}
              size={75} />
        </Pressable>


        <Pressable onPress={skipToNext}>
            <Icon  style={styles.icon} name="skip-next" size={75} />
        </Pressable>
    </View>
  )
}

Step 4 : Build UI for displaying the track info

  • Make use of typescript for type safety.
type SongProps = PropsWithChildren<{
  track: Track | undefined 
}>

const SongInfo = ({track} : SongProps) => {
  return (
    <View style={styles.container}>
      <View>
        <Text style={styles.name}>{track?.title}</Text>
        <Text style={styles.artist}>
          {track?.artist} . {track?.album}
        </Text>
      </View>
    </View>
  );
};

Create a Slider with react-native/community-slider package

const SongSlider = () => {

  const {position,duration}=useProgress()

  const playbackState=usePlaybackState()

  const skipInSong =async (seekPosition)=>{
    await TrackPlayer.seekTo(seekPosition)
  }

  return (
    <View>
      <Slider 
      value={position}
      minimumValue={0}
      maximumValue={duration}
      thumbTintColor='#fff'
      maximumTrackTintColor='#fff'
      style={styles.sliderContainer}
      onValueChange={(seek)=>{
        skipInSong(seek)
      }}
      />

      <View style={styles.timeContainer} >
        <Text style={styles.time}>
          {new Date(position*1000).toISOString().substring(15,19)}
        </Text>
        <Text style={styles.time}>
          {new Date((duration-position) *1000).toISOString().substring(15,19)}
        </Text>
      </View>
    </View>
  )
}

Step 5 : Combine the UI components

Make use of useTrackPlayerEvents() for updating the info of the current track.

const MusicPlayer = () => {
    const [track,setTrack]=useState<Track|null>()

    useTrackPlayerEvents([Event.PlaybackTrackChanged],async event =>{
        switch(event.type){
            case Event.PlaybackTrackChanged:
                const playing=await TrackPlayer.getTrack(event.nextTrack)
                setTrack(playing)
                break;
        }
    })
    const renderArtWork =()=>{
        return (
            <View style={styles.listArtWrapper}>
                <View style={styles.albumContainer}>
                    <Image 
                        style={styles.albumArtImg}
                        source={{ uri:track?.artwork?.toString() }}
                    />
                </View>
            </View>
        )
    }
  return (
    <View style={styles.container}>
      <FlatList 
      horizontal
      data={playListData}
      renderItem={renderArtWork}
      keyExtractor={song=>song.id.toString()}/>
      <SongInfo track={track} />
      <SongSlider />
      <ControlCenter />
    </View>
  )
}

Thank you for taking your time and reading my blog, Hope this helped you in some and once again thank you Hitesh Choudhary sir.

#reactnative