Kouma Ibrahim
Kouma Ibrahim

Reputation: 119

play one song at the time react native track player

I'm working on a music app using react native track player for playing a song, my current code works with multiple songs, and I would like to know how can we implement one song playing. for example, on a screen, we have a list of all the songs when we click on a specific song we can play that song, and we can go back and choose another song and play that one. I have tried to implement but every time I go back to the song list and choose another song the old one will be playing.

here is my code for multiple songs

import Slider from '@react-native-community/slider';
import React, {useEffect, useRef, useState} from 'react';
import {
Animated,
Dimensions,
Image,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import {SafeAreaView} from 'react-native-safe-area-context';
import TrackPlayer, {
State,
usePlaybackState,
useProgress,
} from 'react-native-track-player';
import Ionicons from 'react-native-vector-icons/Ionicons';
import {songType} from '../../../../App';
import Songs from '../DummyData/songs';

interface renderSongTYpe {
item: songType;
}

const {width, height} = Dimensions.get('window');

const setupPlayer = async () => {
console.log('load');
await TrackPlayer.setupPlayer();
await TrackPlayer.add(Songs);
};

const tooglePlayback = async (playbackState: State) => {
const curentTrack = await TrackPlayer.getCurrentTrack();
console.log('cur', curentTrack);
if (curentTrack !== null) {
 if (playbackState === State.Playing) {
   await TrackPlayer.pause();
 } else {
   await TrackPlayer.play();
 }
}
};
export const MusicPlayer = () => {
const playbackState = usePlaybackState() as State;
const progress = useProgress();
const scrollX = useRef(new Animated.Value(0)).current;
const [songIndex, setSongIndex] = useState(0);

console.log('current state', progress.position);
const songSlider = useRef<any>(null);

const skipTo = async (trackId: number) => {
 await TrackPlayer.skip(trackId);
};
useEffect(() => {
 setupPlayer();
 scrollX.addListener(({value}) => {
   // console.log('scroll x', value);
   const index = Math.round(value / width);
   skipTo(index);
   setSongIndex(index);
   if (!songSlider.current) {
     return;
   }
 });

 return () => {
   scrollX.removeAllListeners();
 };
}, []);

const skipToNext = () => {
 songSlider.current.scrollToOffset({
   offset: (songIndex + 1) * width,
 });
};

const skipToPrevious = () => {
 songSlider.current.scrollToOffset({
   offset: (songIndex - 1) * width,
 });
};
const renderSongs = ({item}: renderSongTYpe) => {
 return (
   <Animated.View
     style={{width, justifyContent: 'center', alignItems: 'center'}}>
     {State.Playing ? (
       <View style={styles.artworkwrapper}>
         <Image source={item.img} style={styles.artworkimg} />
       </View>
     ) : (
       <Text>loading</Text>
     )}
   </Animated.View>
 );
};

function millisToMinutesAndSeconds(millis: number) {
 const minutes = Math.floor(millis / 60000);
 const seconds = ((millis % 60000) / 1000).toFixed(0) as unknown as number;

 return `${minutes < 10 ? '0' : ''}${minutes}:${
   seconds < 10 ? '0' : ''
 }${seconds}`;
}

return (
 <SafeAreaView style={styles.container}>
   <View style={styles.mainContainer}>
     <View style={{width}}>
       <Animated.FlatList
         ref={songSlider}
         data={Songs}
         renderItem={renderSongs}
         keyExtractor={item => item.id}
         horizontal
         pagingEnabled
         showsHorizontalScrollIndicator={false}
         onScroll={Animated.event(
           [
             {
               nativeEvent: {
                 contentOffset: {x: scrollX},
               },
             },
           ],
           {useNativeDriver: true},
         )}
       />
     </View>

     <View>
       <Text style={styles.title}> {Songs[songIndex].title}</Text>
       <Text style={styles.artist}> song artiste</Text>
     </View>

     <View>
       <Slider
         thumbTintColor="#ffd369"
         minimumTrackTintColor="#ffd369"
         maximumTrackTintColor="#fff"
         style={styles.progressContainer}
         value={progress.position}
         minimumValue={0}
         maximumValue={progress.duration}
         onSlidingComplete={async value => {
           await TrackPlayer.seekTo(value);
         }}
       />

       <View style={styles.progressLabelContainer}>
         <Text style={styles.progressLabeltxt}>
           {millisToMinutesAndSeconds(progress.position * 1000)}
         </Text>
         <Text style={styles.progressLabeltxt}>
           {millisToMinutesAndSeconds(
             (progress.duration - progress.position) * 1000,
           )}
         </Text>
       </View>
     </View>

     <View style={styles.musicControl}>
       <TouchableOpacity onPress={skipToPrevious}>
         <Ionicons
           name="play-skip-back-outline"
           size={35}
           color="#ffd369"
           style={{marginTop: 15}}
         />
       </TouchableOpacity>
       <TouchableOpacity onPress={() => tooglePlayback(playbackState)}>
         <Ionicons
           name={
             playbackState === State.Playing
               ? 'ios-pause'
               : 'ios-play-circle'
           }
           size={75}
           color="#ffd369"
         />
       </TouchableOpacity>
       <TouchableOpacity onPress={skipToNext}>
         <Ionicons
           name="play-skip-forward-outline"
           size={35}
           color="#ffd369"
           style={{marginTop: 15}}
         />
       </TouchableOpacity>
     </View>
   </View>
   <View style={styles.bottomContainer}>
     <View style={styles.bottomControls}>
       <TouchableOpacity onPress={() => {}}>
         <Ionicons name="heart-outline" size={32} color="#777777" />
       </TouchableOpacity>
       <TouchableOpacity onPress={() => {}}>
         <Ionicons name="repeat" size={32} color="#777777" />
       </TouchableOpacity>
       <TouchableOpacity onPress={() => {}}>
         <Ionicons name="share-outline" size={32} color="#777777" />
       </TouchableOpacity>
       <TouchableOpacity onPress={() => {}}>
         <Ionicons name="ellipsis-horizontal" size={32} color="#777777" />
       </TouchableOpacity>
     </View>
   </View>
 </SafeAreaView>
);
};

const styles = StyleSheet.create({
container: {
 flex: 1,
 backgroundColor: '#222831',
},
mainContainer: {
 flex: 1,
 backgroundColor: '#222831',
 alignItems: 'center',
 justifyContent: 'center',
},

artworkwrapper: {
 width: 300,
 height: 340,
 marginBottom: 25,
 shadowColor: '#ccc',
 shadowOffset: {
   width: 5,
   height: 5,
 },
 shadowOpacity: 0.5,
 shadowRadius: 3.84,

 elevation: 5,
},
artworkimg: {
 width: '100%',
 height: '100%',
 borderRadius: 15,
},

title: {
 fontSize: 18,
 fontWeight: '600',
 textAlign: 'center',
 color: '#eeeeee',
},

artist: {
 fontSize: 16,
 fontWeight: '200',
 textAlign: 'center',
 color: '#eeeeee',
},

progressContainer: {
 width: 350,
 height: 40,
 marginTop: 25,
 flexDirection: 'row',
},
progressLabelContainer: {
 width: 340,
 flexDirection: 'row',
 justifyContent: 'space-between',
},

progressLabeltxt: {
 color: '#fff',
},

musicControl: {
 flexDirection: 'row',
 width: '60%',
 justifyContent: 'space-between',
},
bottomContainer: {
 borderTopColor: '#393E46',
 borderTopWidth: 1,
 width,
 alignItems: 'center',
 paddingVertical: 15,
},
bottomControls: {
 flexDirection: 'row',
 justifyContent: 'space-between',
 width: '80%',
},
});

Upvotes: 2

Views: 1654

Answers (1)

Mustafa Al Ajmi
Mustafa Al Ajmi

Reputation: 85

Have the songs seprated each in its own object then pass it to TrackPlayer.add like this

var songObj = {
 id: 1,
 url: "https://sample.com/song_01.mp3",
 title: "Song 01"
}

await TrackPlayer.add([songObj])

this way you play only one track at a time by pressing at it

Upvotes: 2

Related Questions