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