فرآیند کامل ساخت پروژه React Native Music Player
فرآیند کامل ساخت پروژه React Native Music Player
مرحله ۱: نصب پیشنیازها
۱.۱ نصب Node.js
به سایت nodejs.org بروید
نسخه LTS را دانلود و نصب کنید
پس از نصب، ترمینال را باز کنید و نسخه را بررسی کنید:
node --version npm --version
۱.۲ نصب React Native CLI
npm install -g react-native-cli
۱.۳ نصب Android Studio (برای توسعه اندروید)
از developer.android.com/studio دانلود کنید
در حین نصب، Android SDK و Virtual Device را حتما انتخاب کنید
۱.۴ نصب Xcode (برای توسعه iOS - فقط مک)
از App Store دانلود کنید
مرحله ۲: راهاندازی ویرایشگر کد
۲.۱ نصب Visual Studio Code
از code.visualstudio.com دانلود کنید
پس از نصب، این extensions را نصب کنید:
۲.۲ Extension های ضروری VS Code:
React Native Tools (مایکروسافت)
ES7+ React/Redux/React-Native snippets
Auto Rename Tag
Bracket Pair Colorizer
ESLint
Prettier - Code formatter
Material Icon Theme (اختیاری)
مرحله ۳: ایجاد پروژه جدید
۳.۱ ایجاد پروژه
npx react-native init MusicPlayerApp
cd MusicPlayerApp
۳.۲ بررسی ساختار پروژه
MusicPlayerApp/ ├── android/ # کدهای مخصوص اندروید ├── ios/ # کدهای مخصوص iOS ├── node_modules/ # پکیجهای نصب شده ├── src/ # (ایجاد کنید) کدهای اصلی برنامه ├── App.js # فایل اصلی برنامه ├── package.json # تنظیمات پروژه └── index.js # نقطه شروع برنامه
مرحله ۴: نصب پکیجهای مورد نیاز
۴.۱ نصب expo-av برای پخش صدا
npm install expo-av
۴.۲ نصب پکیجهای کمکی
npm install @react-native-community/slider npm install react-native-vector-icons
۴.۳ لینک کردن پکیجها (برای React Native قدیمی)
npx react-native link
مرحله ۵: ساختار پروژه
۵.۱ ایجاد پوشهها و فایلها
mkdir src mkdir src/components mkdir src/data mkdir src/utils
۵.۲ فایلهای اصلی پروژه:
App.js - کامپوننت اصلی
src/data/songs.js - دادههای آهنگها
src/components/SongList.js - کامپوننت لیست آهنگها
src/components/Player.js - کامپوننت پلیر
src/components/SongItem.js - کامپوننت آیتم آهنگ
مرحله ۶: پیادهسازی کد
۶.۱ فایل src/data/songs.js:
export const songsData = [ { id: '1', title: 'Song One', artist: 'Artist One', poster: 'https://via.placeholder.com/150/0000FF/808080?text=Song+1', audioUrl: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3', duration: '3:45' }, { id: '2', title: 'Song Two', artist: 'Artist Two', poster: 'https://via.placeholder.com/150/FF0000/FFFFFF?text=Song+2', audioUrl: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3', duration: '4:20' }, { id: '3', title: 'Song Three', artist: 'Artist Three', poster: 'https://via.placeholder.com/150/00FF00/000000?text=Song+3', audioUrl: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-3.mp3', duration: '2:55' } ];
6.۲ فایل src/components/SongItem.js:
import React from 'react'; import { View, Text, TouchableOpacity, Image, StyleSheet } from 'react-native'; const SongItem = ({ item, onPress, isPlaying, isCurrent }) => { return ( <TouchableOpacity style={[ styles.container, isCurrent && styles.currentContainer ]} onPress={() => onPress(item)} > <Image source={{ uri: item.poster }} style={styles.poster} /> <View style={styles.infoContainer}> <Text style={styles.title}>{item.title}</Text> <Text style={styles.artist}>{item.artist}</Text> </View> <View style={styles.statusContainer}> {isCurrent && ( <Text style={styles.playingText}> {isPlaying ? '🔊' : '⏸️'} </Text> )} <Text style={styles.duration}>{item.duration}</Text> </View> </TouchableOpacity> ); }; const styles = StyleSheet.create({ container: { flexDirection: 'row', alignItems: 'center', padding: 12, marginHorizontal: 16, marginVertical: 4, backgroundColor: '#fff', borderRadius: 8, shadowColor: '#000', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.1, shadowRadius: 3, elevation: 2, }, currentContainer: { backgroundColor: '#e3f2fd', borderColor: '#2196f3', borderWidth: 1, }, poster: { width: 50, height: 50, borderRadius: 6, }, infoContainer: { flex: 1, marginLeft: 12, }, title: { fontSize: 16, fontWeight: '600', color: '#333', }, artist: { fontSize: 14, color: '#666', marginTop: 2, }, statusContainer: { alignItems: 'flex-end', }, playingText: { fontSize: 16, marginBottom: 4, }, duration: { fontSize: 12, color: '#999', }, }); export default SongItem;
6.۳ فایل src/components/Player.js:
import React from 'react'; import { View, Text, TouchableOpacity, Image, StyleSheet, ActivityIndicator } from 'react-native'; const Player = ({ currentSong, isPlaying, isLoading, onPlayPause, onStop }) => { if (!currentSong) return null; return ( <View style={styles.container}> <Image source={{ uri: currentSong.poster }} style={styles.poster} /> <View style={styles.infoContainer}> <Text style={styles.title}>{currentSong.title}</Text> <Text style={styles.artist}>{currentSong.artist}</Text> </View> <View style={styles.controlsContainer}> {isLoading ? ( <ActivityIndicator size="small" color="#2196f3" /> ) : ( <> <TouchableOpacity style={styles.controlButton} onPress={onPlayPause} > <Text style={styles.controlText}> {isPlaying ? '⏸️' : '▶️'} </Text> </TouchableOpacity> <TouchableOpacity style={styles.controlButton} onPress={onStop} > <Text style={styles.controlText}>⏹️</Text> </TouchableOpacity> </> )} </View> </View> ); }; const styles = StyleSheet.create({ container: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#fff', padding: 16, margin: 16, borderRadius: 12, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 3, }, poster: { width: 60, height: 60, borderRadius: 8, }, infoContainer: { flex: 1, marginLeft: 16, }, title: { fontSize: 18, fontWeight: 'bold', color: '#333', }, artist: { fontSize: 14, color: '#666', marginTop: 4, }, controlsContainer: { flexDirection: 'row', alignItems: 'center', }, controlButton: { padding: 8, marginLeft: 8, }, controlText: { fontSize: 24, }, }); export default Player;
6.۴ فایل App.js اصلی:
import React, { useState, useRef, useEffect } from 'react'; import { View, Text, FlatList, StyleSheet, StatusBar } from 'react-native'; import { Audio } from 'expo-av'; import { songsData } from './src/data/songs'; import SongItem from './src/components/SongItem'; import Player from './src/components/Player'; const App = () => { const [songs] = useState(songsData); const [currentSong, setCurrentSong] = useState(null); const [isPlaying, setIsPlaying] = useState(false); const [sound, setSound] = useState(null); const [isLoading, setIsLoading] = useState(false); useEffect(() => { setupAudio(); return () => { if (sound) { sound.unloadAsync(); } }; }, []); const setupAudio = async () => { try { await Audio.requestPermissionsAsync(); await Audio.setAudioModeAsync({ allowsRecordingIOS: false, staysActiveInBackground: true, playsInSilentModeIOS: true, shouldDuckAndroid: true, playThroughEarpieceAndroid: false, }); } catch (error) { console.error('Error setting up audio:', error); } }; const playSong = async (song) => { try { setIsLoading(true); // Stop current song if playing if (sound) { await sound.stopAsync(); await sound.unloadAsync(); } // Load and play new song const { sound: newSound } = await Audio.Sound.createAsync( { uri: song.audioUrl }, { shouldPlay: true } ); setSound(newSound); setCurrentSong(song); setIsPlaying(true); // Set up playback status listener newSound.setOnPlaybackStatusUpdate((status) => { if (status.didJustFinish) { setIsPlaying(false); } }); } catch (error) { console.error('Error playing song:', error); alert('Error playing song. Please check your internet connection.'); } finally { setIsLoading(false); } }; const togglePlayPause = async () => { if (!sound) return; try { if (isPlaying) { await sound.pauseAsync(); setIsPlaying(false); } else { await sound.playAsync(); setIsPlaying(true); } } catch (error) { console.error('Error toggling play/pause:', error); } }; const stopSong = async () => { if (!sound) return; try { await sound.stopAsync(); await sound.unloadAsync(); setSound(null); setCurrentSong(null); setIsPlaying(false); } catch (error) { console.error('Error stopping song:', error); } }; const renderSongItem = ({ item }) => ( <SongItem item={item} onPress={playSong} isPlaying={isPlaying} isCurrent={currentSong?.id === item.id} /> ); return ( <View style={styles.container}> <StatusBar barStyle="dark-content" backgroundColor="#f5f5f5" /> <Text style={styles.header}>🎵 Music Player</Text> <Player currentSong={currentSong} isPlaying={isPlaying} isLoading={isLoading} onPlayPause={togglePlayPause} onStop={stopSong} /> <FlatList data={songs} renderItem={renderSongItem} keyExtractor={(item) => item.id} style={styles.list} showsVerticalScrollIndicator={false} /> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#f5f5f5', paddingTop: 60, }, header: { fontSize: 28, fontWeight: 'bold', textAlign: 'center', marginBottom: 20, color: '#333', }, list: { flex: 1, }, }); export default App;
مرحله ۷: اجرای برنامه
۷.۱ اجرا روی اندروید:
npx react-native run-android
۷.۲ اجرا روی iOS:
npx react-native run-ios
۷.۳ اجرا با Metro Bundler:
npx react-native start
مرحله ۸: تست برنامه
۸.۱ تستهای اولیه:
لیست آهنگها نمایش داده میشود ✅
با کلیک روی آهنگ، پخش میشود ✅
کنترلهای play/pause/stop کار میکنند ✅
آهنگ جاری در لیست مشخص میشود ✅
نکات مهم:
برای توسعه بهتر در VS Code:
از
Ctrl+Shift+Pو تایپ "React Native: Start Packager" استفاده کنیداز extension React Native Tools برای دیباگ استفاده کنید
برای reload برنامه
Rرا دو بار فشار دهید
اگر با خطا مواجه شدید:
# پاک کردن cache npx react-native start --reset-cache # حذف node_modules و نصب مجدد rm -rf node_modules npm install # برای اندروید cd android ./gradlew clean cd ..
این پروژه یک پلیر موسیقی ساده اما کامل است که میتوانید آن را توسعه دهید!
خیر، نصب Android Studio اجباری نیست. راههای جایگزین وجود دارد:
راههای جایگزین بدون Android Studio:
۱. استفاده از Expo (توصیه شده برای شروع)
# نصب Expo CLI npm install -g expo-cli # ایجاد پروژه جدید npx create-expo-app MusicPlayerApp cd MusicPlayerApp # نصب پکیجهای مورد نیاز npx expo install expo-av # اجرای برنامه npx expo start
مزایای Expo:
نیاز به Android Studio ندارد
روی دستگاه فیزیکی قابل تست است
build آسانتر
مدیریت خودکار dependencies
۲. استفاده از دستگاه فیزیکی
برای اندروید:
-
USB debugging را فعال کنید
به Settings > About Phone بروید
۷ بار روی Build Number کلیک کنید
Developer options را فعال کنید
USB debugging را روشن کنید
برنامه را اجرا کنید:
npx react-native run-android
برای iOS (فقط مک):
npx react-native run-ios
۳. استفاده از خدمات cloud برای build
با Expo:
# build برای اندروید npx expo build:android # build برای iOS npx expo build:ios
پروژه اصلاح شده برای Expo:
App.js برای Expo:
import React, { useState, useEffect } from 'react'; import { View, Text, FlatList, TouchableOpacity, Image, StyleSheet, ActivityIndicator } from 'react-native'; import { Audio } from 'expo-av'; const songsData = [ { id: '1', title: 'Song One', artist: 'Artist One', poster: 'https://via.placeholder.com/150/0000FF/808080?text=Song+1', audioUrl: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3' }, { id: '2', title: 'Song Two', artist: 'Artist Two', poster: 'https://via.placeholder.com/150/FF0000/FFFFFF?text=Song+2', audioUrl: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3' } ]; export default function App() { const [currentSong, setCurrentSong] = useState(null); const [isPlaying, setIsPlaying] = useState(false); const [sound, setSound] = useState(null); const [isLoading, setIsLoading] = useState(false); useEffect(() => { return sound ? () => sound.unloadAsync() : undefined; }, [sound]); const playSong = async (song) => { try { setIsLoading(true); if (sound) { await sound.stopAsync(); await sound.unloadAsync(); } const { sound: newSound } = await Audio.Sound.createAsync( { uri: song.audioUrl }, { shouldPlay: true } ); setSound(newSound); setCurrentSong(song); setIsPlaying(true); newSound.setOnPlaybackStatusUpdate((status) => { if (status.didJustFinish) { setIsPlaying(false); } }); } catch (error) { console.log('Error playing song:', error); } finally { setIsLoading(false); } }; const togglePlayPause = async () => { if (!sound) return; if (isPlaying) { await sound.pauseAsync(); setIsPlaying(false); } else { await sound.playAsync(); setIsPlaying(true); } }; const renderItem = ({ item }) => ( <TouchableOpacity style={[ styles.item, currentSong?.id === item.id && styles.selectedItem ]} onPress={() => playSong(item)} > <Image source={{ uri: item.poster }} style={styles.poster} /> <View style={styles.info}> <Text style={styles.title}>{item.title}</Text> <Text style={styles.artist}>{item.artist}</Text> </View> {currentSong?.id === item.id && isLoading && ( <ActivityIndicator size="small" color="#0000ff" /> )} </TouchableOpacity> ); return ( <View style={styles.container}> <Text style={styles.header}>Music Player</Text> {currentSong && ( <View style={styles.player}> <Image source={{ uri: currentSong.poster }} style={styles.currentPoster} /> <View style={styles.playerInfo}> <Text style={styles.currentTitle}>{currentSong.title}</Text> <Text style={styles.currentArtist}>{currentSong.artist}</Text> </View> <TouchableOpacity onPress={togglePlayPause} style={styles.playButton}> <Text style={styles.playButtonText}> {isPlaying ? '⏸️' : '▶️'} </Text> </TouchableOpacity> </View> )} <FlatList data={songsData} renderItem={renderItem} keyExtractor={item => item.id} /> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, paddingTop: 50, backgroundColor: '#f5f5f5', }, header: { fontSize: 24, fontWeight: 'bold', textAlign: 'center', marginBottom: 20, }, player: { flexDirection: 'row', alignItems: 'center', backgroundColor: 'white', padding: 15, margin: 10, borderRadius: 10, }, currentPoster: { width: 50, height: 50, borderRadius: 5, }, playerInfo: { flex: 1, marginLeft: 10, }, currentTitle: { fontSize: 16, fontWeight: 'bold', }, currentArtist: { fontSize: 14, color: 'gray', }, playButton: { padding: 10, }, playButtonText: { fontSize: 20, }, item: { flexDirection: 'row', alignItems: 'center', backgroundColor: 'white', padding: 15, marginHorizontal: 10, marginVertical: 5, borderRadius: 8, }, selectedItem: { backgroundColor: '#e3f2fd', }, poster: { width: 40, height: 40, borderRadius: 5, }, info: { flex: 1, marginLeft: 10, }, title: { fontSize: 16, fontWeight: '600', }, artist: { fontSize: 14, color: 'gray', }, });
فرآیند ساده شده با Expo:
۱. نصب:
npm install -g expo-cli npx create-expo-app MusicPlayerApp cd MusicPlayerApp
۲. نصب پکیج صدا:
npx expo install expo-av
۳. کد را در App.js جایگزین کنید
۴. اجرا:
npx expo start
۵. تست روی دستگاه:
Expo Go را از بازارها نصب کنید
QR code را با اپلیکیشن Expo Go اسکن کنید
پیشنهاد: اگر تازه کار را شروع کردهاید، حتماً از Expo استفاده کنید. بعداً اگر نیاز به قابلیتهای پیشرفته داشتید، میتوانید به React Native CLI مهاجرت کنید.

نظرات (۰)
هیچ نظری هنوز ثبت نشده است