マグネテック備忘録

Flutterアプリ開発の備忘録

【Flutter】音楽を鳴らす(audioplayers)



audioplayersとは?

  • 音楽を鳴らすためのパッケージ。
  • 再生、一時停止、再生位置の調整(10秒送りなど)など一通りできる。
  • 着信などの通知オン・目覚まし用なら「ringtone_player」の方がいいかもしれないです。こちらについても記事にまとめているので、良かったら参考にしてください⇒【Flutter】着信音を鳴らす(ringtone_player) - マグネテック備忘録
  • ↓↓audioplayersの公式サイト

pub.dev




準備

1. パッケージの追加

記事作成日時点の最新バージョンを追加。

dependencies:
  font_awesome_flutter: ^10.5.0


後はaudioplayersを使いたいdartファイルの先頭に以下のコードを追加。

import 'package:audioplayers/audioplayers.dart';



2. 音源ファイルの設定

ファイルの追加

  • 使用したい音源を追加します。
  • 「assets」というフォルダを追加して、その中に再生したい音源「sample.mp3」を追加。

sample.mp3を追加

pubspec.yamlの変更

  • 追加したassetフォルダをプログラムから参照できるようにコードにコードを追加。

flutter:
  assets:
    - assets/



3. AudioPlayerの作成

  • audioplayersはインスタンス化したものを操作することで再生などができるようになります。
  • 以降ではここで宣言したplayerという変数を使います。
final player = AudioPlayer();





基本操作

指定したファイルの再生

  • player.play(AssetSource(ファイル名)
// 例:先ほど追加した「sample.mp3」を再生
player.play(AssetSource("sample.mp3"))



一時停止&再開

// 一時停止
player.pause();

//再開
player.resume();



再生位置の変更

  • player.seek(Duration)
  • Durationは経過時間をミリ秒、秒、分単位などで設定できるクラスです。
// 例:再生位置を00:10に変更
player.seek(Duration(seconds: 10));



再生位置が変わったときの処理

  • player.onPositionChanged.listen((Duration d){/*処理*/});
  • Duration d:現在の再生位置
  • seekなどで再生位置を変えた時だけでなく、再生している間にも呼び出され続けます。
// 例:再生位置を秒単位で表示
player.onPositionChanged.listen((Duration d){
   print(d.inSeconds);
});



曲が変わったときの処理

  • player.onDurationChanged.listen((Duration d){/*処理*/});
  • Duration d:曲の再生時間
  • 再生する曲を変えた時などに呼び出されます。
// 例:曲の再生時間をint型変数maxSecに格納
player.onDurationChanged.listen((Duration d) {
  maxSec = d.inSeconds;
});



鳴り終わった後の処理

  • player.onPlayerComplete.listen((event){/*処理*/});
// 例:鳴り終わった後にFinishと表示。
player.onPlayerComplete.listen((event) { 
  print("Finish");
});





デモアプリ(簡易な音楽再生アプリ)

使用した音源

魔王魂様の以下の曲を使用しました。


これらをassetsフォルダに追加し、pubspec.yamlに準備で説明したように設定を行います。
デモアプリ用音源


パッケージの追加

アイコンのために「font_awesome」を使うので追加しておきます。

dependencies:
  font_awesome_flutter: ^10.5.0



ソースコード

import 'package:flutter/material.dart';
import 'package:audioplayers/audioplayers.dart';

void main() {
  const app = MaterialApp(
    home: MyHomePage(),
  );

  runApp(app);
}

final musicLists = [
  'maou_08_burning_heart.mp3',
  'maou_14_shining_star.mp3',
  'maou_39_soleil.mp3'
];

final player = AudioPlayer();
bool isPlaying = false;
int currentSec = 0;
int maxSec = 1;
int n=0;

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  // 再生・停止を切り替え
  void _playMusic() {
    if (!isPlaying) {
      player.resume();
    } else {
      player.pause();
    }

    setState(() {
      isPlaying = !isPlaying; 
    });
  }

  // 再生位置を変更
  void setSeek(int sec){
    setState(() {
      currentSec = sec.clamp(0, maxSec);
      player.seek(Duration(seconds: currentSec));
    });
  }

  // 秒表記⇒分表記
  String getMinuteStr(int sec){

    int minutes = sec ~/ 60;
    int remainingSeconds = sec % 60;

    // 数字を0詰めで2桁に調整
    String formattedMinutes = minutes.toString().padLeft(2, '0');
    String formattedSeconds = remainingSeconds.toString().padLeft(2, '0');

    return '${formattedMinutes}:${formattedSeconds}';
  }

  @override
  void initState() {
    super.initState();

    // 再生位置が変わったとき
    player.onPositionChanged.listen((Duration d){
      setState(() {
        currentSec = d.inSeconds;
      });
    });

    // 曲が変わったとき
    player.onDurationChanged.listen((Duration d) {
      maxSec = d.inSeconds;
    });

    // 鳴り終わったら次の曲へ
    player.onPlayerComplete.listen((event) { 
      n = (n+1) % musicLists.length;
      player.play(AssetSource(musicLists[n]));
    });
}

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: const Text('Music'),
        backgroundColor: Colors.indigo.shade200,
      ),

      body: Container(
        padding: EdgeInsets.all(20),
        child: ListView.builder(
          itemCount: musicLists.length,
          itemBuilder: (context, index) {
            return ElevatedButton(
              onPressed: () {

                // 再生中にする
                setState(() {
                  isPlaying = true;
                });

                // nを設定
                n = index;

                // 再生
                player.play(AssetSource(musicLists[index]));
              },
              child: Text(musicLists[index]),
            );
          }
        ),
      ),

    
      bottomNavigationBar: Container(
        color: Colors.blueGrey.shade100,
        height: 200.0, // 下部に表示するWidgetの高さ
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: <Widget>[
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: <Widget>[

                // 前の曲へ
                IconButton(
                  icon: const Icon(Icons.skip_previous),
                  iconSize: 50.0,
                  onPressed: () {
                    if(n==0){
                      n = musicLists.length -1;
                    }
                    else{
                      n-=1;
                    }
                    player.play(AssetSource(musicLists[n]));
                  },
                ),

                // 10秒前へ
                IconButton(
                  icon: const Icon(Icons.replay_10),
                  iconSize: 50.0,
                  onPressed: () {
                    setSeek(currentSec-10);
                  },
                ),

                // 再生・停止切り替え
                IconButton(
                  icon: Icon(isPlaying ? Icons.pause : Icons.play_arrow),
                  iconSize: 64.0,
                  onPressed: () => _playMusic(),
                ),

                // 10秒後へ
                IconButton(
                  icon: const Icon(Icons.forward_10),
                  iconSize: 50.0,
                  onPressed: () {
                    setSeek(currentSec+10);
                  },
                ),

                // 次の曲へ
                IconButton(
                  icon: const Icon(Icons.skip_next),
                  iconSize: 50.0,
                  onPressed: () {
                    n = (n+1) % musicLists.length;
                    player.play(AssetSource(musicLists[n]));
                  },
                ),
              ],
            ),

            Slider(
              value: currentSec.toDouble(),
              min: 0,
              max: maxSec.toDouble(),
              divisions: maxSec,
              onChanged: (double value) {
                setSeek(value.toInt());
              },
            ),

            Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: <Widget>[
                Text("${musicLists[n]}"),
                Text("${getMinuteStr(currentSec)}/${getMinuteStr(maxSec)}"),
              ]
            ),
          ],
        ),
      ),
    );
  }
}



実行画面

実装した機能は以下の通りです。

  • 再生・停止の切り替え
  • 再生位置をスライダーとテキストで表示
  • スライダーで再生位置の変更
  • 10秒送り/戻し
  • ファイル名をタッチで曲変更。
  • 矢印ボタンで前後の曲に移動。
  • 曲が鳴り終わったら次の曲へ。

実行画面