کانال نوحه و مداحی ناب

فرآیند کامل ساخت پروژه React Native Music Player

فرآیند کامل ساخت پروژه React Native Music Player

مرحله ۱: نصب پیش‌نیازها

۱.۱ نصب Node.js

  • به سایت nodejs.org بروید

  • نسخه LTS را دانلود و نصب کنید

  • پس از نصب، ترمینال را باز کنید و نسخه را بررسی کنید:

bash
node --version
npm --version

۱.۲ نصب React Native CLI

bash
 
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:

  1. React Native Tools (مایکروسافت)

  2. ES7+ React/Redux/React-Native snippets

  3. Auto Rename Tag

  4. Bracket Pair Colorizer

  5. ESLint

  6. Prettier - Code formatter

  7. Material Icon Theme (اختیاری)

مرحله ۳: ایجاد پروژه جدید

۳.۱ ایجاد پروژه

bash
npx react-native init MusicPlayerApp
cd MusicPlayerApp

۳.۲ بررسی ساختار پروژه

text
 
MusicPlayerApp/
├── android/          # کدهای مخصوص اندروید
├── ios/              # کدهای مخصوص iOS
├── node_modules/     # پکیج‌های نصب شده
├── src/              # (ایجاد کنید) کدهای اصلی برنامه
├── App.js           # فایل اصلی برنامه
├── package.json     # تنظیمات پروژه
└── index.js         # نقطه شروع برنامه

مرحله ۴: نصب پکیج‌های مورد نیاز

۴.۱ نصب expo-av برای پخش صدا

bash
npm install expo-av

۴.۲ نصب پکیج‌های کمکی

bash
npm install @react-native-community/slider
npm install react-native-vector-icons

۴.۳ لینک کردن پکیج‌ها (برای React Native قدیمی)

bash
npx react-native link

مرحله ۵: ساختار پروژه

۵.۱ ایجاد پوشه‌ها و فایل‌ها

bash
mkdir src
mkdir src/components
mkdir src/data
mkdir src/utils

۵.۲ فایل‌های اصلی پروژه:

  1. App.js - کامپوننت اصلی

  2. src/data/songs.js - داده‌های آهنگ‌ها

  3. src/components/SongList.js - کامپوننت لیست آهنگ‌ها

  4. src/components/Player.js - کامپوننت پلیر

  5. src/components/SongItem.js - کامپوننت آیتم آهنگ

مرحله ۶: پیاده‌سازی کد

۶.۱ فایل src/data/songs.js:

javascript
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:

javascript
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:

javascript
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 اصلی:

javascript
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;

مرحله ۷: اجرای برنامه

۷.۱ اجرا روی اندروید:

bash
npx react-native run-android

۷.۲ اجرا روی iOS:

bash
npx react-native run-ios

۷.۳ اجرا با Metro Bundler:

bash
npx react-native start

مرحله ۸: تست برنامه

۸.۱ تست‌های اولیه:

  1. لیست آهنگ‌ها نمایش داده می‌شود ✅

  2. با کلیک روی آهنگ، پخش می‌شود ✅

  3. کنترل‌های play/pause/stop کار می‌کنند ✅

  4. آهنگ جاری در لیست مشخص می‌شود ✅

نکات مهم:

برای توسعه بهتر در VS Code:

  • از Ctrl+Shift+P و تایپ "React Native: Start Packager" استفاده کنید

  • از extension React Native Tools برای دیباگ استفاده کنید

  • برای reload برنامه R را دو بار فشار دهید

اگر با خطا مواجه شدید:

bash
 
# پاک کردن cache
npx react-native start --reset-cache

# حذف node_modules و نصب مجدد
rm -rf node_modules
npm install

# برای اندروید
cd android
./gradlew clean
cd ..

این پروژه یک پلیر موسیقی ساده اما کامل است که می‌توانید آن را توسعه دهید!

 
 
 
 
 
 
 
نصب Android Studio اجباری است؟
 
 

خیر، نصب Android Studio اجباری نیست. راه‌های جایگزین وجود دارد:

راه‌های جایگزین بدون Android Studio:

۱. استفاده از Expo (توصیه شده برای شروع)

bash
# نصب 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

۲. استفاده از دستگاه فیزیکی

برای اندروید:

  1. USB debugging را فعال کنید

    • به Settings > About Phone بروید

    • ۷ بار روی Build Number کلیک کنید

    • Developer options را فعال کنید

    • USB debugging را روشن کنید

  2. برنامه را اجرا کنید:

bash
npx react-native run-android

برای iOS (فقط مک):

bash
npx react-native run-ios

۳. استفاده از خدمات cloud برای build

با Expo:

bash
# build برای اندروید
npx expo build:android

# build برای iOS
npx expo build:ios

پروژه اصلاح شده برای Expo:

App.js برای Expo:

javascript
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:

۱. نصب:

bash
npm install -g expo-cli
npx create-expo-app MusicPlayerApp
cd MusicPlayerApp

۲. نصب پکیج صدا:

bash
npx expo install expo-av

۳. کد را در App.js جایگزین کنید

۴. اجرا:

bash
npx expo start

۵. تست روی دستگاه:

  • Expo Go را از بازارها نصب کنید

  • QR code را با اپلیکیشن Expo Go اسکن کنید

پیشنهاد: اگر تازه کار را شروع کرده‌اید، حتماً از Expo استفاده کنید. بعداً اگر نیاز به قابلیت‌های پیشرفته داشتید، می‌توانید به React Native CLI مهاجرت کنید.

 
 
کانال نوحه و مداحی ناب

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

ارسال نظر

ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی