写在前头

本来是要做一个仿网易云音乐的flutter项目,但是因为最近事情比较多,项目周期跨度会比较长,因此分几个步骤来完成。这是仿网易云音乐项目系列文章的第一篇。没有完全照搬网易云音乐的UI,借鉴了其中的黑胶唱机动画。

先贴上项目地址 github.com/KinsomyJS/f…

初步效果图

思路

这个界面实现起来其实是比较简单的,大致分为如下几个部分:

  • 1.背景的高斯模糊效果
  • 2.黑胶唱头的旋转动画
  • 3.黑胶唱片的旋转动画
  • 4.下部控制器和进度条部分

我们一个个来说实现过程。

实践

整个界面是一个堆叠视图,最下面是一个背景图片,上面覆盖一层高斯模糊半透明遮罩,再上层是title,黑胶唱机和控制器。

1. 背景高斯模糊

首先使用stack组件用来包裹堆叠视图,在里面有两个container,第一个是背景网络图片,第二个就是一个BackdropFilter

  1. Stack(
  2. children: <Widget>[
  3. new Container(
  4. decoration: new BoxDecoration(
  5. image: new DecorationImage(
  6. image: new NetworkImage(coverArt),
  7. fit: BoxFit.cover,
  8. colorFilter: new ColorFilter.mode(
  9. Colors.black54,
  10. BlendMode.overlay,
  11. ),
  12. ),
  13. ),
  14. ),
  15. new Container(
  16. child: new BackdropFilter(
  17. filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
  18. child: Opacity(
  19. opacity: 0.6,
  20. child: new Container(
  21. decoration: new BoxDecoration(
  22. color: Colors.grey.shade900,
  23. ),
  24. ),
  25. ),
  26. )),
  27. ...
  28. ]
  29. 复制代码

这里的高斯模糊sigmaX和sigmaY的值选择了10,然后透明度为0.6,颜色为grey.shade900。

2.黑胶唱头的旋转动画

关于动画的知识这里就不做详细介绍了,可以参考官方文档传送门

自定义动画组件在needle_anim.dart文件里。
这里将动画和组件解耦,分别定义了动画过程类PivotTransition,顾名思义围绕一个支点旋转,继承自AnimatedWidget

支点定在child组件的topcenter位置。
注意turns不能为空,需要根据turns的值计算旋转绕过的周长,围绕Z轴旋转。

  1. class PivotTransition extends AnimatedWidget {
  2. /// 创建旋转变换
  3. /// turns不能为空.
  4. PivotTransition({
  5. Key key,
  6. this.alignment: FractionalOffset.topCenter,
  7. @required Animation<double> turns,
  8. this.child,
  9. }) : super(key: key, listenable: turns);
  10. /// The animation that controls the rotation of the child.
  11. /// If the current value of the turns animation is v, the child will be
  12. /// rotated v * 2 * pi radians before being painted.
  13. Animation<double> get turns => listenable;
  14. /// The pivot point to rotate around.
  15. final FractionalOffset alignment;
  16. /// The widget below this widget in the tree.
  17. final Widget child;
  18. @override
  19. Widget build(BuildContext context) {
  20. final double turnsValue = turns.value;
  21. final Matrix4 transform = new Matrix4.rotationZ(turnsValue * pi * 2.0);
  22. return new Transform(
  23. transform: transform,
  24. alignment: alignment,
  25. child: child,
  26. );
  27. }
  28. }
  29. 复制代码

接下来就是自定义黑胶唱头组件。

  1. final _rotateTween = new Tween<double>(begin: -0.15, end: 0.0);
  2. new Container(
  3. child: new PivotTransition(
  4. turns: _rotateTween.animate(controller_needle),
  5. alignment: FractionalOffset.topLeft,
  6. child: new Container(
  7. width: 100.0,
  8. child: new Image.asset("images/play_needle.png"),
  9. ),
  10. ),
  11. ),
  12. 复制代码

将png图片包裹在container内作为child参数传递给PivotTransition

外部使用的时候传入一个Tween,起始位置为-0.15 ~ 0.0。

3.黑胶唱片的旋转动画

这部分代码在record_anim.dart文件内。使用了package:flutter/animation.dart提供的RotationTransition做旋转,很简单。

  1. class RotateRecord extends AnimatedWidget {
  2. RotateRecord({Key key, Animation<double> animation})
  3. : super(key: key, listenable: animation);
  4. Widget build(BuildContext context) {
  5. final Animation<double> animation = listenable;
  6. return new Container(
  7. margin: new EdgeInsets.symmetric(vertical: 10.0),
  8. height: 250.0,
  9. width: 250.0,
  10. child: new RotationTransition(
  11. turns: animation,
  12. child: new Container(
  13. decoration: BoxDecoration(
  14. shape: BoxShape.circle,
  15. image: DecorationImage(
  16. image: NetworkImage(
  17. "https://images-na.ssl-images-amazon.com/images/I/51inO4DBH0L._SS500.jpg"),
  18. ),
  19. ),
  20. )),
  21. );
  22. }
  23. }
  24. 复制代码

接着自定义旋转动画的控制逻辑。旋转一圈用时十五秒钟,速度为线性匀速,同时会重复旋转动画。

  1. controller_record = new AnimationController(
  2. duration: const Duration(milliseconds: 15000), vsync: this);
  3. animation_record =
  4. new CurvedAnimation(parent: controller_record, curve: Curves.linear);
  5. animation_record.addStatusListener((status) {
  6. if (status == AnimationStatus.completed) {
  7. controller_record.repeat();
  8. } else if (status == AnimationStatus.dismissed) {
  9. controller_record.forward();
  10. }
  11. });
  12. 复制代码

4.下部控制器和进度条部分

播放流媒体音频使用了三方组件audioplayers,具体代码在player_page.dart文件内,封装了一个player组件,接受了一系列参数包括音频路径,播放操作回调等。该组件支持本地资源和网络资源,这里用网络音频资源做demo。

  1. const Player(
  2. {@required this.audioUrl,
  3. @required this.onCompleted,
  4. @required this.onError,
  5. @required this.onNext,
  6. @required this.onPrevious,
  7. this.key,
  8. this.volume: 1.0,
  9. this.onPlaying,
  10. this.color: Colors.white,
  11. this.isLocal: false});
  12. 复制代码

在initState方法里初始化AudioPlayer对象。".."是dart的级联操作符。

  1. audioPlayer = new AudioPlayer();
  2. audioPlayer
  3. ..completionHandler = widget.onCompleted
  4. ..errorHandler = widget.onError
  5. ..durationHandler = ((duration) {
  6. setState(() {
  7. this.duration = duration;
  8. if (position != null) {
  9. this.sliderValue = (position.inSeconds / duration.inSeconds);
  10. }
  11. });
  12. })
  13. ..positionHandler = ((position) {
  14. setState(() {
  15. this.position = position;
  16. if (duration != null) {
  17. this.sliderValue = (position.inSeconds / duration.inSeconds);
  18. }
  19. });
  20. });
  21. 复制代码

开始播放代码

  1. audioPlayer.play(
  2. widget.audioUrl,
  3. isLocal: widget.isLocal,
  4. volume: widget.volume,
  5. );
  6. 复制代码

开始播放后,durationHandler会回调音频总时长,positionHandler会回调播放进度,两个回调都返回一个Duration对象。根据这两个duration对象可以计算机播放进度的百分比,这里使用Slider组件做进度条。

  1. new Slider(
  2. onChanged: (newValue) {
  3. if (duration != null) {
  4. int seconds = (duration.inSeconds * newValue).round();
  5. print("audioPlayer.seek: $seconds");
  6. audioPlayer.seek(new Duration(seconds: seconds));
  7. }
  8. },
  9. value: sliderValue ?? 0.0,
  10. activeColor: widget.color,
  11. ),
  12. 复制代码

总结

整体实现是非常简单的,只要对flutter的组件有所了解就能很快写出来,后面还会加入歌词滚动功能来丰富界面。

具体项目可以到 github.com/KinsomyJS/f… 查看,也欢迎star持续关注。

参考资料

  1. 官方文档
  2. pub: audioplayers

Flutter仿网易云音乐:播放界面的更多相关文章

  1. 2.Android高仿网易云音乐-引导界面和广告界面实现

    效果图 效果图依次为图片广告,视频广告,引导界面. 系列文章目录导航 目录 1.实现分析 广告界面就是显示图片和视频,所以可以放一个图片控件,视频控件,然后跳过按钮,提示按钮,WiFi预加载提示都是放 ...

  2. Android高仿网易云音乐-启动界面实现和动态权限处理

    效果 实现分析 基本上没有什么难点,就是布局,然后显示用户协议对话框,动态处理权限,判断是否显示引导界面,是否显示广告界面等. 布局 <?xml version="1.0" ...

  3. 《云阅》一个仿网易云音乐UI,使用Gank.Io及豆瓣Api开发的开源项目

    CloudReader 一款基于网易云音乐UI,使用GankIo及豆瓣api开发的符合Google Material Desgin阅读类的开源项目.项目采取的是Retrofit + RxJava + ...

  4. C# WPF 低仿网易云音乐(PC)歌词控件

    原文:C# WPF 低仿网易云音乐(PC)歌词控件 提醒:本篇博客记录了修改的过程,废话比较多,需要项目源码和看演示效果的直接拉到文章最底部~ 网易云音乐获取歌词的api地址 http://music ...

  5. C# WPF 低仿网易云音乐(PC)Banner动画控件

    原文:C# WPF 低仿网易云音乐(PC)Banner动画控件 由于技术有限没能做到一模一样的动画,只是粗略地做了一下.动画有点生硬,还有就是没做出网易云音乐的立体感.代码非常简单粗暴,而且我也写有很 ...

  6. iOS 开发仿网易云音乐歌词海报

    使用网易云音乐也是一个巧合,我之前一直使用QQ音乐听歌,前几天下 app 手机内存告急.于是就把QQ音乐给卸载掉了,正好晚上朋友圈里有一个朋友用网易云音乐分享了一首歌曲,于是我也就尝试下载了网易云音乐 ...

  7. 新鲜出炉高仿网易云音乐 APP

    我的引语 晚上好,我是吴小龙同学,我的公众号「一分钟GitHub」会推荐 GitHub 上好玩的项目,一分钟 get 一个优秀的开源项目,挖掘开源的价值,欢迎关注我. 项目中成长是最快的,如何成长,就 ...

  8. android仿网易云音乐引导页、仿书旗小说Flutter版、ViewPager切换、爆炸菜单、风扇叶片效果等源码

    Android精选源码 复现网易云音乐引导页效果 高仿书旗小说 Flutter版,支持iOS.Android Android Srt和Ass字幕解析器 Material Design ViewPage ...

  9. WPF仿网易云音乐系列(序)

    1.简介 由于之前做了一个播放器,苦于不懂界面设计,只得去借鉴借鉴一些成功的作品,网易云音乐就甚合朕心,哈哈,最后做出来的效果如下: 本系列文章就来和大家讨论以下,如何用WPF去仿制一个网易云音乐来: ...

随机推荐

  1. Shell:Day04.笔记

    grep与正则表达式: 1.grep程序 Linux下有文本处理三剑客 - - grep sed awk grep:文本 行 过滤工具 sed:文本 行 编辑器(流编辑器) awk:报告生成器(做文本 ...

  2. web日志分析的重要性

    虽然不可能对庞大的日志文件进行逐条的阅读,但是在这些日志文件中,确实会包含一些非常重要的信息.例如,在什么时间.有哪些ip地址访问了网站中的什么资源,等等. 通过对日志文件的分析,可以获得如下信息. ...

  3. 批处理 bat 查询局域网内在线电脑IP

    查看自己局域网的IP和物理网卡地址可以在 WIN+R –> 打开cmd 键入 arp -a 可以看到局域网中所有的在线IP COLOR 0A CLS @ECHO Off Title 查询局域网内 ...

  4. 文件的读写 - open

    #写文件,r路径\n 空格\t 缩进# fan=open(r'C:/Users/demiyangping_v/Desktop/fan.txt','w', encoding='utf-8')# fan. ...

  5. Array(数组)对象-->数组的删除

    1.数组的删除: 用delete操作符删除特定的元素 删除元素的位置只是被留空了,为undefined值 举例:删除下面数组中的第二个元素 var arr = [1,2,3,4,5]; /*删除第二个 ...

  6. HTML5实现刷脸支付

    最近刷脸支付很火,老板们当然要追赶时代潮流,于是就有了刷脸支付这个项目.前端实现关键的技术是摄像头录像,拍照和人脸比对,本文来探讨一下如何在html5环境中如何实现刷脸支付以及开发过程中遇到的问题. ...

  7. Python Requests-学习笔记(3)-处理json

    JSON响应内容 Requests中也有一个内置的JSON解码器,助你处理JSON数据: r = requests.get('https://github.com/timeline.json') pr ...

  8. BMI的Python实现

    str1 = float(input('请输入您的身高(单位:米):')) # input默认转化为字符串型 用float转化为浮点型 str2 = float(input('请输入您的体重(单位:千 ...

  9. 小程序wepy2 模拟vant PasswordInput, NumberKeyboard 密码输入框控件

    vant weapp小程序端控件目前是没有PasswordInput,NumberKeyboard的.实现效果: 数字键盘组件代码(keyboard.wpy): <template> &l ...

  10. curl 交叉编译 支持http2和openssl

    touch run.sh chmod 755 run.sh mkdir build cd build ../run.sh run.sh #!/bin/bash #cd /build ../config ...