Flutter学院

Flutter中文学习网站

Flutter仿网易云音乐项目:播放界面


写在前头

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

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

初步效果图

Flutter仿网易云音乐项目:播放界面

 

思路

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

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

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

实践

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

1. 背景高斯模糊

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

Stack(       children: <Widget>[ new Container(           decoration: new BoxDecoration(             image: new DecorationImage(               image: new NetworkImage(coverArt),               fit: BoxFit.cover,               colorFilter: new ColorFilter.mode(                 Colors.black54,                 BlendMode.overlay,               ),             ),           ),         ), new Container(             child: new BackdropFilter(           filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),           child: Opacity(             opacity: 0.6,             child: new Container(               decoration: new BoxDecoration(                 color: Colors.grey.shade900,               ),             ),           ),         )),                  ...     ] 

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

2.黑胶唱头的旋转动画

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

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

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

class PivotTransition extends AnimatedWidget { /// 创建旋转变换 /// turns不能为空. PivotTransition({     Key key, this.alignment: FractionalOffset.topCenter, @required Animation<double> turns, this.child,   }) : super(key: key, listenable: turns); /// The animation that controls the rotation of the child. /// If the current value of the turns animation is v, the child will be /// rotated v * 2 * pi radians before being painted. Animation<double> get turns => listenable; /// The pivot point to rotate around. final FractionalOffset alignment; /// The widget below this widget in the tree. final Widget child; @override Widget build(BuildContext context) { final double turnsValue = turns.value; final Matrix4 transform = new Matrix4.rotationZ(turnsValue * pi * 2.0); return new Transform(       transform: transform,       alignment: alignment,       child: child,     );   } } 

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

final _rotateTween = new Tween<double>(begin: -0.15, end: 0.0); new Container(   child: new PivotTransition(     turns: _rotateTween.animate(controller_needle),     alignment: FractionalOffset.topLeft,     child: new Container(       width: 100.0,       child: new Image.asset("images/play_needle.png"),     ),   ), ), 

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

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

3.黑胶唱片的旋转动画

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

class RotateRecord extends AnimatedWidget {   RotateRecord({Key key, Animation<double> animation})       : super(key: key, listenable: animation);    Widget build(BuildContext context) { final Animation<double> animation = listenable; return new Container(       margin: new EdgeInsets.symmetric(vertical: 10.0),       height: 250.0,       width: 250.0,       child: new RotationTransition(           turns: animation,           child: new Container(             decoration: BoxDecoration(               shape: BoxShape.circle,               image: DecorationImage(                 image: NetworkImage( "https://images-na.ssl-images-amazon.com/images/I/51inO4DBH0L._SS500.jpg"),               ),             ),           )),     );   } } 

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

controller_record = new AnimationController(         duration: const Duration(milliseconds: 15000), vsync: this); animation_record = new CurvedAnimation(parent: controller_record, curve: Curves.linear); animation_record.addStatusListener((status) { if (status == AnimationStatus.completed) {     controller_record.repeat();   } else if (status == AnimationStatus.dismissed) {     controller_record.forward();   } }); 

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

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

const Player(       {@required this.audioUrl, @required this.onCompleted, @required this.onError, @required this.onNext, @required this.onPrevious, this.key, this.volume: 1.0, this.onPlaying, this.color: Colors.white, this.isLocal: false}); 复制代码

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

 audioPlayer = new AudioPlayer();     audioPlayer       ..completionHandler = widget.onCompleted       ..errorHandler = widget.onError       ..durationHandler = ((duration) {         setState(() { this.duration = duration; if (position != null) { this.sliderValue = (position.inSeconds / duration.inSeconds);           }         });       })       ..positionHandler = ((position) {         setState(() { this.position = position; if (duration != null) { this.sliderValue = (position.inSeconds / duration.inSeconds);           }         });       }); 

开始播放代码

audioPlayer.play(     widget.audioUrl,     isLocal: widget.isLocal,     volume: widget.volume,   ); 

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

new Slider(     onChanged: (newValue) { if (duration != null) { int seconds = (duration.inSeconds * newValue).round(); print("audioPlayer.seek: $seconds");         audioPlayer.seek(new Duration(seconds: seconds));       }     },     value: sliderValue ?? 0.0,     activeColor: widget.color,   ), 

总结

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

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

参考资料

  1. 官方文档
  2. pub: audioplayers

 

 

本博客所有文章如无特别注明均为原创。作者:flutter教程网 ,复制或转载请以超链接形式注明转自 Flutter学院
原文地址《Flutter仿网易云音乐项目:播放界面
分享到:更多

0条评论

发表评论