flutter 的音乐播放器

import 'dart:io';

import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:kplayer/kplayer.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'keep_alive_page.dart';

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

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

class MusicPlayerState extends State<MusicPlayer>
    with SingleTickerProviderStateMixin {
  List<String> songs = [];
  List<String> faviates = [];
  List<int> _currentIndex = [-1, -1];

  bool _isPlaying = false;
  bool inited = false;
  double timeValue = 0;
  double timeMax = 1000000;
  double vol = 0.5;
  ScrollController songsController = ScrollController();
  ScrollController faviatesController = ScrollController();
  late PlayerController _controller;
  int tabIndex = 0;
  List<Widget> pages = [];
  late SharedPreferences prefs;
  late TabController tabController;

  Future<void> requestPermissions() async {
    final status = await Permission.storage.request();
    if (!status.isGranted) {
      throw 'Storage permission not granted';
    }
  }

  Future<SharedPreferences> _initPrefs() async {
    prefs = await SharedPreferences.getInstance();
    return prefs;
  }

  List<String> noDoubel(List<String> list) {
    Set<String> set = {...list};
    return set.toList(growable: true);
  }

  void initState() {
    super.initState();
    tabController = TabController(length: 2, vsync: this);

    _initPrefs().then((prefs) async {
      songs = (await prefs.getStringList("songs"))!;
      songs = noDoubel(songs);
      faviates = (await prefs.getStringList("faviates"))!;
      setState(() {});
    });
  }

  Future<List<String>> pickFolder() async {
    final result = await FilePicker.platform.getDirectoryPath();
    if (result == null) return [];
    final directory = Directory(result);
    final files = await directory.list().toList();

    return files
        .where((file) => file.path.endsWith('.mp3'))
        .map((file) => file.path)
        .toList();
  }

  void _playOrPause() {
    setState(() {
      _isPlaying = !_isPlaying;
    });
    if (_isPlaying) {
      if (_controller == null) {
        initController();
      } else {
        _controller.play();
      }
      inited = true;
    } else {
      _controller.pause();
    }
  }

  void _nextSong() {
    setState(() {
      _currentIndex[tabController.index] =
          (_currentIndex[tabController.index] + 1) % songs.length;
      if (_controller != null) {
        _controller.dispose();
      }
      initController();
    });
  }

  void _previousSong() {
    setState(() {
      if (_controller != null) {
        _controller.dispose();
      }
      _currentIndex[tabController.index] =
          (_currentIndex[tabController.index] - 1 + songs.length) %
              songs.length;
      initController();
    });
  }

  void _removeSong(int index) {
    setState(() {
      if (tabController.index == 0) {
        songs.removeAt(index);
        if (_currentIndex[tabController.index] >= songs.length) {
          _currentIndex[tabController.index] = songs.length - 1;
        }
        prefs.setStringList("songs", noDoubel(songs));
      } else {
        faviates.removeAt(index);
        if (_currentIndex[tabController.index] >= faviates.length) {
          _currentIndex[tabController.index] = faviates.length - 1;
        }
        prefs.setStringList("faviates", noDoubel(faviates));
      }
    });
    setState(() {});
  }

  Future<void> addSong() async {
    pickFolder().then((files) {
      setState(() {
        songs.addAll(files);
        songs.remove("");

        // pages = [buildAll(songs), buildAll(songs.sublist(1, 11))];
      });
    });
    await prefs.setStringList("songs", noDoubel(songs));
  }

  Future<void> addFavorite(int index) async {
    setState(() {
      if (!faviates.contains(songs[index])) {
        faviates.add(songs[index]);
        faviates.remove("");
      }
    });
    await prefs.setStringList("faviates", noDoubel(faviates));
    await prefs.setStringList("songs", noDoubel(songs));
  }

  void _play(int index) {
    _currentIndex[tabController.index] = index;
    if (inited && _controller != null) {
      _controller.dispose();
    }
    initController();

    setState(() {
      _isPlaying = true;
    });
  }

  void initController() {
    if (tabController.index == 0) {
      _controller = Player.file(songs[_currentIndex[tabController.index]]);
    } else {
      _controller = Player.file(faviates[_currentIndex[tabController.index]]);
    }
    _controller.callback = (event) {
      setState(() {
        if (event == PlayerEvent.end) {
          _nextSong();
        } else if (event == PlayerEvent.position) {
          timeValue = (_controller.position.inMilliseconds.toDouble());
        } else if (event == PlayerEvent.duration) {
          timeMax = _controller.duration.inMilliseconds.toDouble();
        } else if (event == PlayerEvent.end) {
          _nextSong();
        }
      });
    };
    _controller.setVolume(vol);
    inited = true;
  }

  @override
  Widget build(BuildContext context) {
    //  Color iconColor = index == _currentIndex ? Colors.blue : Colors.white70;
    return Scaffold(
      backgroundColor: Colors.black,
      appBar: AppBar(
        backgroundColor: Colors.black,
        toolbarHeight: 22,
        title: const SizedBox(
          height: 20,
          child: Text('i悦',
              style: TextStyle(
                  fontSize: 12,
                  fontWeight: FontWeight.w400,
                  color: Colors.white)),
        ),
        bottom: TabBar(controller: tabController, tabs: [
          Tab(
              icon: Icon(Icons.music_note_outlined,
                  size: 20,
                  color:
                      tabController.index == 0 ? Colors.blue : Colors.white70)),
          Tab(
              icon: Icon(Icons.favorite,
                  size: 20,
                  color:
                      tabController.index == 1 ? Colors.red : Colors.white70)),
        ]),
      ),
      body: buildBody(context),
    );
  }

  Widget buildBody(BuildContext context) {
    return Column(children: [
      Expanded(
          child: TabBarView(
        controller: tabController,
        children: [
          KeepAlivePage(child: buildSongs(context, songs, songsController)),
          KeepAlivePage(
              child: buildSongs(context, faviates, faviatesController))
        ],
      )),
      buildProcessBar(context),
      buildPlayController(context)
    ]);
  }

  Widget buildSongs(BuildContext context, List<String> songItems,
      ScrollController rollController) {
    return ListView.builder(
      controller: rollController,
      physics: const AlwaysScrollableScrollPhysics(),
      scrollDirection: Axis.vertical,
      // primary: true,
      shrinkWrap: true,
      cacheExtent: 120,
      itemCount: songItems.length,

      itemBuilder: (context, index) {
        String songName = "";
        if (Platform.isWindows) {
          songName = songItems[index].split('\\').last;
        } else {
          songName = songItems[index].split('/').last;
        }
        int lindex = songName.lastIndexOf(".");
        if (lindex > 0) {
          songName = songName.substring(0, lindex);
        }
        Color textColor = index == _currentIndex[tabController.index]
            ? Colors.blue
            : Colors.white70;
        Color iconColor = index == _currentIndex[tabController.index]
            ? Colors.blue
            : Colors.white70;
        Color favirateColor =
            tabController.index == 0 && faviates.contains(songs[index])
                ? Colors.red
                : iconColor;
        return Container(
            height: 36,
            color: index == _currentIndex[tabController.index]
                ? Colors.black12
                : Colors.black,
            child: ListTile(
              onTap: () => _play(index),
              minVerticalPadding: 1,
              contentPadding: const EdgeInsets.all(2),
              hoverColor: Colors.black12,
              titleAlignment: ListTileTitleAlignment.top,
              isThreeLine: false,
              selected: index == _currentIndex[tabController.index],
              leading: IconButton(
                highlightColor: iconColor,
                icon: Icon(
                  Icons.delete,
                  size: 20,
                ),
                onPressed: () => _removeSong(index),
              ),
              title: Align(
                  alignment: Alignment.topLeft,
                  child: Text(
                    songName,
                    style: TextStyle(
                      fontSize: 12,
                      color: textColor,
                      overflow: TextOverflow.ellipsis,
                    ),
                  )),
              trailing: tabIndex == 0
                  ? IconButton(
                      icon: Icon(
                        Icons.favorite,
                        size: 20,
                        color: favirateColor,
                      ),
                      onPressed: () => addFavorite(index),
                    )
                  : Container(),
            ));
      },
    );
  }

  Widget buildProcessBar(BuildContext context) {
    return Row(
      children: [
        Expanded(
          child: Slider(
            min: 0,
            max: 1,
            label: "$timeValue / $timeMax",
            value: timeMax < 100 || timeValue / timeMax > 1
                ? 0
                : timeValue / timeMax,
            onChanged: (double value) {
              setState(() {
                timeValue = value;
                double latValue = timeValue * timeMax;
                _controller!.seek(Duration(milliseconds: latValue.toInt()));
              });
            },
          ),
        ),
      ],
    );
  }

  Widget buildPlayController(BuildContext context) {
    return Wrap(
        direction: Axis.horizontal,
        alignment: WrapAlignment.spaceEvenly,
        runAlignment: WrapAlignment.center,
        children: [
          IconButton(
            onPressed: _previousSong,
            icon: const Icon(
              Icons.skip_previous,
              color: Colors.white70,
            ),
          ),
          IconButton(
            onPressed: _playOrPause,
            icon: Icon(
              _isPlaying ? Icons.pause : Icons.play_arrow,
              color: Colors.white70,
            ),
          ),
          IconButton(
            onPressed: _nextSong,
            icon: const Icon(
              Icons.skip_next,
              color: Colors.white70,
            ),
          ),
          IconButton(
            onPressed: addSong,
            icon: const Icon(
              Icons.add,
              color: Colors.white70,
            ),
          ),
          IconButton(
              onPressed: () {},
              icon: const Icon(
                Icons.cable_rounded,
                color: Colors.white70,
              )),
          IconButton(
              onPressed: () {},
              icon: const Icon(
                Icons.volume_up_outlined,
                color: Colors.white70,
              )),
          Slider(
              value: vol,
              min: 0,
              max: 1,
              onChanged: (value) {
                setState(() {
                  vol = value;
                  _controller!.setVolume(vol);
                });
              }),
        ]);
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值