zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

flutter播放上一首和下一首,自动播放下一首

flutter 播放 自动播放
2023-09-14 09:04:29 时间

参考文章
自己的代码如下

import 'dart:async';
import 'package:audioplayer/audioplayer.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

//音乐播放器
typedef void OnError(Exception exception);

const kUrl =
    "https://www.mediacollege.com/downloads/sound-effects/nature/forest/rainforest-ambient.mp3";
// const kUrl = "https://s3.amazonaws.com/scifri-segments/scifri201711241.mp3";

enum PlayerState { stopped, playing, paused }

class AudioPlay extends StatefulWidget {
  const AudioPlay({Key key}) : super(key: key);

  @override
  _AudioPlayState createState() => _AudioPlayState();
}

class _AudioPlayState extends State<AudioPlay> {
  Duration duration;
  Duration position;

  AudioPlayer audioPlayer;

  String localFilePath;

  PlayerState playerState = PlayerState.stopped;
  bool prePlayState = false;
  List<String> _songs = [
    "https://www.mediacollege.com/downloads/sound-effects/nature/forest/rainforest-ambient.mp3",
    "https://s3.amazonaws.com/scifri-segments/scifri201711241.mp3"
  ];
  int curIndex = 0;

  get isPlaying => playerState == PlayerState.playing;

  get isPaused => playerState == PlayerState.paused;

  get durationText =>
      duration != null ? duration.toString().split('.').first : '';

  get positionText =>
      position != null ? position.toString().split('.').first : '';

  bool isMuted = false;

  StreamSubscription _positionSubscription;
  StreamSubscription _audioPlayerStateSubscription;

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

  @override
  void dispose() {
    _positionSubscription.cancel();
    _audioPlayerStateSubscription.cancel();
    audioPlayer.stop();
    super.dispose();
  }

  void initAudioPlayer() {
    audioPlayer = AudioPlayer();
    _positionSubscription = audioPlayer.onAudioPositionChanged
        .listen((p) => setState(() => position = p));
    _audioPlayerStateSubscription =
        audioPlayer.onPlayerStateChanged.listen((s) {
      if (s == AudioPlayerState.PLAYING) {
        setState(() => duration = audioPlayer.duration);
      } else if (s == AudioPlayerState.STOPPED) {
        onComplete();
        setState(() {

          position = duration;
          //0:00:09.643000和0:00:09.643000
          print("${duration}和${position}");

          //如果播放完就自动播放下一首,点击下一首按键也会调用到这个方法
          if (position == duration) {
            nextPlay();
          }
          //这个是专门给播放上一首按键调用的方法,因为调用了一次nextPlay(),这里就要调用两次prePlay()
          if (prePlayState) {
            prePlay();
            prePlay();
            prePlayState = false;
          }
        });
      }
    }, onError: (msg) {
      setState(() {
        playerState = PlayerState.stopped;
        duration = Duration(seconds: 0);
        position = Duration(seconds: 0);
      });
    });
  }

  // //播放
  // Future play() async {
  //   await audioPlayer.play(kUrl);
  //   setState(() {
  //     playerState = PlayerState.playing;
  //   });
  // }

  //播放
  Future play() async {
    await audioPlayer.play(_songs[curIndex]);
    setState(() {
      playerState = PlayerState.playing;
    });
  }

// // 播放很多歌
//   void playSongs(List<String> songs, {int index}) {
//     this._songs = songs;
//     if (index != null) curIndex = index;
//     play();
//   }

  //在播放时点击下一首是实现不了效果的,需要先停止再播放才能播放下一首
  //所以这个方法放在stop方法里面的下一首
  void nextPlay() {
    curIndex++;
    if (curIndex >= _songs.length) {
      curIndex = 0;
    }
    play();
  }

  //用于点击的下一首
  void nextPlayButton() {
    stop();
  }

//放在stop方法的上一首
  void prePlay() {
    curIndex--;
    if (curIndex < 0) {
      curIndex = _songs.length - 1;
    }
    play();
  }

  //用于点击的上一首
  void prePlayButton() {
    prePlayState = true;
    stop();
  }

  Future _playLocal() async {
    await audioPlayer.play(localFilePath, isLocal: true);
    setState(() => playerState = PlayerState.playing);
  }

  //暂停
  Future pause() async {
    await audioPlayer.pause();
    setState(() => playerState = PlayerState.paused);
  }

  //停止
  Future stop() async {
    await audioPlayer.stop();
    setState(() {
      playerState = PlayerState.stopped;
      position = Duration();
    });
  }

  Future mute(bool muted) async {
    await audioPlayer.mute(muted);
    setState(() {
      isMuted = muted;
    });
  }

  void onComplete() {
    setState(() => playerState = PlayerState.stopped);
  }

  // Future _loadFile() async {
  //   final bytes = await _loadFileBytes(kUrl,
  //       onError: (Exception exception) =>
  //           print('_loadFile => exception $exception'));
  //
  //   final dir = await getApplicationDocumentsDirectory();
  //   final file = File('${dir.path}/audio.mp3');
  //
  //   await file.writeAsBytes(bytes);
  //   if (await file.exists())
  //     setState(() {
  //       localFilePath = file.path;
  //     });
  // }

  //这里的方法就是:可以先写一部分,然后再用Material(child: _buildPlayer()),显示其他界面,
  // 可以在_buildPlayer里面放其他方法体
  @override
  Widget build(BuildContext context) {
    final textTheme = Theme.of(context).textTheme;
    return Center(
      child: Center(
        child: Container(
          decoration: BoxDecoration(color: Color(0xFF1F1F1F)),
          //整个界面以列的形式排放
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              //封面,同时把按键放到底部
              Expanded(
                child: Column(
                  children: [
                    Container(
                      // decoration: BoxDecoration(color: Colors.black),
                      child: Text(
                        '',
                      ),
                    ),
                    Container(
                      margin: const EdgeInsets.only(left: 15, right: 15),
                      child: Image.asset(
                        'assets/base_widgets/bg_player.png',
                        fit: BoxFit.fitWidth,
                      ),
                    ),
                    Container(
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Container(
                            margin: const EdgeInsets.only(
                                left: 30, top: 30, bottom: 15),
                            child: Text("Mojito",
                                style: TextStyle(
                                    color: Color(0xFFCCCCCC), fontSize: 20)),
                          ),
                          Row(
                            children: [
                              Container(
                                margin:
                                    const EdgeInsets.only(left: 30, bottom: 12),
                                child: Text(
                                  "周杰倫",
                                  style: TextStyle(color: Color(0xFF999999)),
                                ),
                              ),
                              Container(
                                width: 30,
                                margin:
                                    const EdgeInsets.only(left: 15, bottom: 12),
                                child: Image.asset(
                                  'assets/base_widgets/icon_player_vip.png',
                                  fit: BoxFit.fitWidth,
                                ),
                              ),
                              Container(
                                width: 30,
                                margin:
                                    const EdgeInsets.only(left: 15, bottom: 12),
                                child: Image.asset(
                                  'assets/base_widgets/icon_player_standard.png',
                                  fit: BoxFit.fitWidth,
                                ),
                              ),
                            ],
                          ),
                          Container(
                            margin: const EdgeInsets.only(left: 30, bottom: 12),
                            child: Text("麻烦给我的爱人来一杯Mojito",
                                style: TextStyle(color: Color(0xFF666666))),
                          ),
                          Container(
                            margin: const EdgeInsets.only(left: 30, bottom: 12),
                            child: Text("我喜欢阅读她微醺时的眼眸",
                                style: TextStyle(color: Color(0xFFFFFFFF))),
                          ),
                          Container(
                            margin: const EdgeInsets.only(left: 30, bottom: 12),
                            child: Text("而我的咖啡糖不用太多",
                                style: TextStyle(color: Color(0xFF666666))),
                          ),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
              //接着来到这里叫开始调用方法了
              Material(child: _buildPlayer()),
              if (!kIsWeb)
                localFilePath != null ? Text(localFilePath) : Container(),
              // if (!kIsWeb)
              //   Padding(
              //     padding: const EdgeInsets.all(8.0),
              // child: Row(
              //   mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              //   children: [
              //     RaisedButton(
              //       // onPressed: () => _loadFile(),
              //       child: Text('Download'),
              //     ),
              //     if (localFilePath != null)
              //       RaisedButton(
              //         onPressed: () => _playLocal(),
              //         child: Text('play local'),
              //       ),
              //   ],
              // ),
              // ),
            ],
          ),
        ),
      ),
    );

    // return MaterialApp(
    //     home: Scaffold(
    //       body: Container(
    //         child: ListView(
    //           children: <Widget>[inputTextArea, loginButtonArea, loginSection],
    //         ),
    //       ),
    //     ));
  }

  Widget _buildPlayer() => Container(
        decoration: BoxDecoration(color: Color(0xFF1F1F1F)),
        padding: EdgeInsets.all(16.0),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            buttonMenuSection(),
            if (duration != null)
              //进度条
              Slider(
                  value: position?.inMilliseconds?.toDouble() ?? 0.0,
                  onChanged: (double value) {
                    return audioPlayer.seek((value / 1000).roundToDouble());
                  },
                  min: 0.0,
                  max: duration.inMilliseconds.toDouble()),
            //如果是空的就显示个空进度条
            if (duration == null)
              Slider(
                value: position?.inMilliseconds?.toDouble() ?? 0.0,
              ),
            //进度条
            _buildProgressView(),
            Row(
                mainAxisSize: MainAxisSize.min,
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  //循环
                  Container(
                    width: 40,
                    margin: const EdgeInsets.only(left: 15, right: 15),
                    child: Image.asset(
                      'assets/base_widgets/icon_player_cycle.png',
                      fit: BoxFit.fitWidth,
                    ),
                  ),
                  //上一首
                  GestureDetector(
                    onTap: () {
                      prePlayButton();
                    },
                    child: Container(
                      width: 40,
                      margin: const EdgeInsets.only(left: 15, right: 15),
                      child: Image.asset(
                        'assets/base_widgets/icon_player_previous.png',
                        fit: BoxFit.fitWidth,
                      ),
                    ),
                  ),
                  //播放
                  GestureDetector(
                    onTap: isPlaying ? () => pause() : () => play(),
                    child: Container(
                      width: 50,
                      margin: const EdgeInsets.only(left: 15, right: 15),
                      child: Image.asset(
                        'assets/base_widgets/icon_player_play.png',
                        fit: BoxFit.fitWidth,
                      ),
                    ),
                  ),
                  //下一首
                  GestureDetector(
                    onTap: () {
                      nextPlayButton();
                    },
                    child: Container(
                      width: 40,
                      margin: const EdgeInsets.only(left: 15, right: 15),
                      child: Image.asset(
                        'assets/base_widgets/icon_player_next.png',
                        fit: BoxFit.fitWidth,
                      ),
                    ),
                  ),
                  //均衡器
                  Container(
                    width: 40,
                    margin: const EdgeInsets.only(left: 15, right: 15),
                    child: Image.asset(
                      'assets/base_widgets/icon_player_tuning.png',
                      fit: BoxFit.fitWidth,
                    ),
                  ),
                  // IconButton(
                  //   onPressed: isPlaying ? null : () => play(),
                  //   iconSize: 64.0,
                  //   icon: Icon(Icons.play_arrow),
                  //   color: Colors.cyan,
                  // ),
                  // IconButton(
                  //   onPressed: isPlaying ? () => pause() : null,
                  //   iconSize: 64.0,
                  //   icon: Icon(Icons.pause),
                  //   color: Colors.cyan,
                  // ),
                  // IconButton(
                  //   onPressed: isPlaying || isPaused ? () => stop() : null,
                  //   iconSize: 64.0,
                  //   icon: Icon(Icons.stop),
                  //   color: Colors.cyan,
                  // ),
                ]),
            // if (position != null)
            //静音
            //   _buildMuteButtons(),
            // if (position != null)
          ],
        ),
      );

  Row _buildProgressView() =>
      Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
        Container(
          margin: const EdgeInsets.only(left: 15),
          child: Text(
            position != null
                ? "${positionText ?? ''} "
                : duration != null
                    ? durationText
                    : '',
            style: TextStyle(fontSize: 12.0, color: Colors.white),
          ),
        ),
        Container(
            margin: const EdgeInsets.only(left: 15),
            child: Text(
              position != null
                  ? "${durationText ?? ''}"
                  : duration != null
                      ? durationText
                      : '',
              style: TextStyle(fontSize: 12.0, color: Colors.white),
            )),
        // Padding(
        //   padding: EdgeInsets.all(12.0),
        //圆形进度条
        // child: CircularProgressIndicator(
        //   value: position != null && position.inMilliseconds > 0
        //       ? (position?.inMilliseconds?.toDouble() ?? 0.0) /
        //       (duration?.inMilliseconds?.toDouble() ?? 0.0)
        //       : 0.0,
        //   valueColor: AlwaysStoppedAnimation(Colors.cyan),
        //   backgroundColor: Colors.grey.shade400,
        // ),
        // ),
        // Text(
        //   position != null
        //       ? "${positionText ?? ''} / ${durationText ?? ''}"
        //       : duration != null
        //           ? durationText
        //           : '',
        //   style: TextStyle(fontSize: 24.0, color: Colors.white),
        // )
      ]);

  //是否静音
  Row _buildMuteButtons() {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: <Widget>[
        if (!isMuted)
          FlatButton.icon(
            onPressed: () => mute(true),
            icon: Icon(
              Icons.headset_off,
              color: Colors.cyan,
            ),
            label: Text('Mute', style: TextStyle(color: Colors.cyan)),
          ),
        if (isMuted)
          FlatButton.icon(
            onPressed: () => mute(false),
            icon: Icon(Icons.headset, color: Colors.cyan),
            label: Text('Unmute', style: TextStyle(color: Colors.cyan)),
          ),
      ],
    );
  }

  //上面图片下面文字的那一排按键的方法
  Column menuButtonColumn(String icon, String label) {
    //嵌套函数:把共同的属性写好,方便buttonSection调用
    //设置为主题颜色
    Color color = const Color(0xFF999999);
    //返回列的形式
    return Column(
      //调用该方法后返回这个显示,每个元素以列的方式显示
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.center,
      //有两个元素
      children: [
        Image.asset(
          icon,
          height: 45.0,
          fit: BoxFit.cover,
        ),
        Container(
          //将文字设置为容器,设置边距
          margin: const EdgeInsets.only(top: 8.0), //文字距离上面图标的距离
          child: Text(
            //元素就是一个文字
            label,
            style: TextStyle(
              //设置文字风格
              fontSize: 12.0,
              fontWeight: FontWeight.w400,
              color: color,
            ),
          ),
        )
      ],
    );
  }

  //底部那一排五个按键
  Row buttonMenuSection() {
    //先从行上开始进行分析
    return Row(
      //平均的分配每个列占据的行空间
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      //行上的四个元素
      //调用方法,不然要写很多
      children: [
        menuButtonColumn("assets/base_widgets/icon_player_cover.png", '翻唱'),
        menuButtonColumn(
            "assets/base_widgets/icon_player_equalizer.png", '均衡器'),
        menuButtonColumn(
            "assets/base_widgets/icon_player_member_download.png", '下載'),
        menuButtonColumn("assets/base_widgets/icon_player_comment.png", '評論'),
        menuButtonColumn("assets/base_widgets/icon_player_more.png", '更多'),
      ],
    );
  }
}