flutter 想要一款简洁美观的多端本地音乐播放器该怎么办?

flutter 想要一款简洁美观的多端本地音乐播放器该怎么办?

本地音乐播放器在现在来说好像没什么人在用了吧,大家都直接使用某易,某Q来播放音乐,这些软件虽然很强大,但是不能播放本地保存的会员音乐,即使你本地有周杰伦的mp3音乐,在某Q播放器里面好像也不能播放,并且这些软件里面,丑陋的广告满天飞,让人头大。于是,我决定自己开发一款本地音乐播放器,界面要简洁、大气、美观,具备基本的本地音乐播放功能,一套代码实现跨平台多端运行。

渐变背景颜色实现

首先实现一个渐变色背景,使用Container组件的decoration属性,设置BoxDecoration来实现渐变色效果,在colors里面放上你喜欢的颜色。我实现的是比较粉嫩的背景颜色。

dart 复制代码
Container(
    alignment: Alignment.center,
    decoration: const BoxDecoration(
      gradient: LinearGradient(
        begin: Alignment.topLeft,
        colors: [
          Color.fromARGB(255, 252, 248, 228),
          Color.fromARGB(255, 255, 221, 233),
     ],
   ),
 ),
),

底部导航按钮、切换页面

我们给播放器实现两个界面,第一个界面,也就是首页用来选择本地音乐存储路径,第二个界面展示播放界面。通过bottomNavigationBar组件实现底部导航功能栏,点击按钮切换界面。使用sizebox组件来控制显示区域大小,配合Row横向布局组件包裹IconButton按钮,点击按钮更改index页面索引,触发页面切换效果。

dart 复制代码
  bottomNavigationBar: SizedBox(
      height: 50,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Expanded(
            child: IconButton(
                onPressed: () {
                  index.value = 0;
                },
                icon: const Icon(Icons.home)),
          ),
          Expanded(
            child: IconButton(
                onPressed: () {
                  index.value = 1;
                },
                icon: const Icon(Icons.audiotrack)),
          ),
        ],
      ),
    ),

使用ValueListenableBuilder组件监听index页面索引,在点击按钮时index更改,触发pages显示页面重绘,实现点击切换页面效果。

dart 复制代码
var pages = [
  const MyApp1(),
  const MyApp2(),
];

var index = ValueNotifier<int>(0);

ValueListenableBuilder(
    builder: (BuildContext context, value, Widget? child) {
        return pages[index.value];
      },
    valueListenable: index,
),

首页布局、本地音乐路径选取

主页放置一个按钮用来指定本地音乐文件路径,音乐文件路径选择我们使用file_picker插件,这个插件可以实现跨平台的文件选择功能,支持单个、多个文件选择,也可以指定路径,这个插件在前边的文章介绍过,大家有兴趣可以翻翻看看。

通过file_picker插件将指定的本地音乐文件路径存入musiclist中,当中播放列表,后边播放的时候要用到。只放置一个文件选择按钮有点单调,所以再放一段文字填充一下吧。

dart 复制代码
List<String> musiclist = [""];

class MyApp1 extends StatelessWidget {
  const MyApp1({super.key});

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              "Music player",
              style: TextStyle(fontSize: 50),
            ),
            const Padding(padding: EdgeInsets.all(12)),
            TextButton(
              onPressed: () async {
                FilePickerResult? result =
                    await FilePicker.platform.pickFiles(allowMultiple: true);
                if (result != null) {
                  for (var i = 0; i < result.files.length; i++) {
                    debugPrint(result.files[i].path);
                    musiclist.add(result.files[i].path.toString());
                  }
                } else {
                  // User canceled the picker
                }
              },
              child: const Text("Choose Music Files"),
            ),
          ],
        ),
      ),
    );
  }
}

audioplayers插件

audioplayers插件可同时播放多个音频文件,适用于Android,iOS,Linux,macOS,Windows和Web,使用audioplayers插件可以实现音乐播放、暂停、停止、下一曲、上一曲等基本操作,也可以获取当前播放进度。播放的音乐文件可以是网络上资源、也可以是本地资源,很是方便。我们还可以监控播放进度和播放状态。

audioplayers插件初始化:AudioPlayer audioPlayer = AudioPlayer();  音乐暂停:audioPlayer.pause();  音乐停止:audioPlayer.stop();  音乐播放:audioPlayer.play();

我们使用musiclist存储要播放的音乐路径,使用DeviceFileSource可以设置播放本地音乐文件,只需要指定音乐路径即可。

dart 复制代码
AudioPlayer audioPlayer = AudioPlayer();

Future<void> pause() async {
  audioPlayer.pause();
}

Future<void> stop() async {
  audioPlayer.stop();
}

Future<void> playlocalfile() async {
  audioPlayer.play(
    DeviceFileSource(
      musiclist[musicindex],
    ),
  );
}

音乐播放界面布局

接着来实现播放页面。播放界面由上到下依次放置一个小汽车动画、音浪动画,这两个动画在音乐播放时会动起来,音乐暂停会停止播放。进度条也是必须具备的,随着音乐的播放指示进度。接着就是音乐曲目展示区域,显示正在播放的歌曲名。最后就是播放按键的实现了,具备基本的上一曲、下一曲、播放、暂停功能。

小汽车动画和波浪动画

动画部分我们依旧使用lottie来实现,前边的文章也有介绍过lottie动画,大家有兴趣可以翻翻我的主页。先去lottie主页找几个免费的、适合的动画下载下来,我找了一个小汽车动画、音浪动画、进度条动画,看起来还不错。

因为要控制动画的播放和暂停,所以会更改组件的状态,所以我们要用StatefulWidget有状态组件。在StatefulWidget有状态组件初始化时设置动画控制器topanimaController,后边都通过topanimaController来控制动画的播放。

dart 复制代码
class TopAnimLottie extends StatefulWidget {
  const TopAnimLottie({super.key});

  @override
  State<TopAnimLottie> createState() => _TopAnimLottieState();
}

class _TopAnimLottieState extends State<TopAnimLottie>
    with TickerProviderStateMixin {
  @override
  void initState() {
    super.initState();
    topanimaController = AnimationController(vsync: this);
  }

  @override
  void dispose() {
    topanimaController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Lottie.asset(
        'assets/animation_lmdkfr6e.json',
        width: 150,
        height: 120,
        controller: topanimaController,
        onLoaded: (composition) {
          setState(() {
            topanimaController.duration = composition.duration;
          });
        },
      ),
    );
  }
}

进度条

因为进度条动画的播放要随着音乐进度更改,同时要能进能退,所以进度条动画与上边的小汽车动画在实现上略有不同,但依旧都是lottie动画。  在有状态组件中放置一个Timer.periodic定时器,每一秒计算一下播放进度,根据播放进度控制进度条动画的位置。使用audioPlayer.getCurrentPosition可以获得当前播放进度,使用audioPlayer.getDuration()可以获得全部播放时长。计算出播放进度后,利用proresscontroller.animateTo(left);控制进度条播放。

dart 复制代码
import 'dart:async';

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

import 'audioplay.dart';

late AnimationController proresscontroller;

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

  @override
  State<Progressbar> createState() => _ProgressbarState();
}

double left = 0;

class _ProgressbarState extends State<Progressbar>
    with TickerProviderStateMixin {
  @override
  void initState() {
    super.initState();
    proresscontroller = AnimationController(vsync: this);
    var timer = Timer.periodic(const Duration(seconds: 1), (timer) async {
      var cu = await audioPlayer.getCurrentPosition();
      var all = await audioPlayer.getDuration();
      var culist = cu.toString().split(":");
      var alllist = all.toString().split(":");
      var cud = double.parse(culist[1]) * 60 + double.parse(culist[2]);
      var alld = double.parse(alllist[1]) * 60 + double.parse(alllist[2]);
      left = cud / alld;
      proresscontroller.animateTo(left);
    });
  }

  @override
  void dispose() {
    proresscontroller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Lottie.asset(
        'assets/91938-green-progress-bar-without-popover.json',
        width: 200,
        controller: proresscontroller,
        onLoaded: (composition) {
          setState(() {
            proresscontroller.duration = composition.duration;
          });
        },
      ),
    );
  }
}

播放按键

播放按钮使用IconButton组件,在相应的上一曲、下一曲、播放、暂停按钮中实现对应的操作逻辑。点击上一曲或者下一曲按钮时,在前边定义的musiclist列表中切换要播放的音乐文件路径,实现歌曲切换。同时控制小汽车、音浪、进度条动画的播放状态。

播放或者暂停按钮使用flag来进行一个判断,根据播放状态来控制要展示的按钮图标,同时控制动画的播放和暂停。

dart 复制代码
var flag = true;

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

  @override
  State<MyApp2> createState() => _MyApp2State();
}

class _MyApp2State extends State<MyApp2> {
  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        const Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TopAnimLottie(),
            DownAnimLottie(),
            Padding(
              padding: const EdgeInsets.fromLTRB(0, 12, 0, 8),
              child: Progressbar(),
            ),
          ],
        ),
        Padding(
          padding: const EdgeInsets.all(10.0),
          child: Text(
            musiclist[musicindex]
                .split("\\")[musiclist[musicindex].split("\\").length - 1],
            style: const TextStyle(fontSize: 18),
          ),
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            IconButton(
              onPressed: () async {
                if (musicindex > 0) {
                  musicindex = musicindex - 1;
                }
                flag = false;
                await stop();
                proresscontroller.reset();
                playlocalfile();
                downanimaController.reset();
                downanimaController.repeat();
                topanimaController.reset();
                topanimaController.repeat();
                setState(() {});
              },
              icon: const Icon(Icons.skip_previous),
            ),
            Container(
              child: flag
                  ? IconButton(
                      onPressed: () {
                        flag = false;
                        setState(() {});
                        playlocalfile();
                        downanimaController.reset();
                        downanimaController.repeat();
                        topanimaController.reset();
                        topanimaController.repeat();
                      },
                      icon: const Icon(Icons.play_arrow))
                  : IconButton(
                      onPressed: () {
                        flag = true;
                        pause();
                        setState(() {});
                        topanimaController.stop();
                        downanimaController.stop();
                      },
                      icon: const Icon(Icons.pause)),
            ),
            IconButton(
              onPressed: () async {
                if (musicindex + 1 < musiclist.length) {
                  musicindex = musicindex + 1;
                }
                flag = false;
                proresscontroller.reset();
                await stop();
                playlocalfile();
                downanimaController.reset();
                downanimaController.repeat();
                topanimaController.reset();
                topanimaController.repeat();
                setState(() {});
              },
              icon: const Icon(Icons.skip_next),
            )
          ],
        ),
      ],
    );
  }
}

总结

以上就是我自己开发的音乐播放app啦,只是实现了本地的音乐播放功能,简单的加了一些动画,能满足最基本的播放音乐的需求,当然,还有很多功能没有实现,距离商业音乐播放器还有超级大的差距,后边如果有时间,再继续完善吧。大家可以多多提提建议。后边有时间把代码公开一下,有兴趣继续开发的小伙伴,我们可以一起合作开发哦。

相关推荐
undefined&&懒洋洋10 分钟前
Web和UE5像素流送、通信教程
前端·ue5
666xiaoniuzi2 小时前
深入理解 C 语言中的内存操作函数:memcpy、memmove、memset 和 memcmp
android·c语言·数据库
大前端爱好者2 小时前
React 19 新特性详解
前端
随云6322 小时前
WebGL编程指南之着色器语言GLSL ES(入门GLSL ES这篇就够了)
前端·webgl
随云6322 小时前
WebGL编程指南之进入三维世界
前端·webgl
寻找09之夏3 小时前
【Vue3实战】:用导航守卫拦截未保存的编辑,提升用户体验
前端·vue.js
多多米10054 小时前
初学Vue(2)
前端·javascript·vue.js
柏箱4 小时前
PHP基本语法总结
开发语言·前端·html·php
新缸中之脑4 小时前
Llama 3.2 安卓手机安装教程
前端·人工智能·算法
hmz8564 小时前
最新网课搜题答案查询小程序源码/题库多接口微信小程序源码+自带流量主
前端·微信小程序·小程序