This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.
Preface: Just another day for everyone to contribute their hair, this several days in the search on the net search, found that Flutter music player less (there are a few good, the article finally recommended to everyone 😜), or code is not complete can’t run, or UI can’t see, so I stood up, bring the player, so, Brothers give a praise, the hair did not have a few root ðŸ˜
Source code at the end of the article, is a brother to run to see
Without saying more, first on the effect picture (there is a picture of the truth ah) :
There are people who like Jay as much as I do
Notes for reading this article:
-
Plug-ins used:
assets_audio_player: ^ 2.0.14 // For music playback flutter_swiper: any // This is used for the first image, not for the second one Copy the code
-
The article only shows the important code, the complete source code at the end of the article
Body:
1. Play/pause music button with wavy animation
When hit Play, stack multiple ovals of different colors and rotate them.
-
Therefore, according to the effect requirements, we need to achieve three steps:
1. Create a stateless background oval
2. Use to store multiple background circles in Stack, and click processing
3. Add the zoom and rotate AnimationController to control the background circle
-
Define the parameters to be passed in
double _rotation = 0; double _scale = 0.85; Copy the code
-
Create a stateless Blob that receives incoming color and rotation and scaling values
Package processing:
// stateless class Blob extends StatelessWidget { final Color color; // The color passed in final double rotation; // Pass in the rotation Angle final double scale; // Pass in the scale const Blob({this.color, this.rotation = 0.this.scale = 1}); @override Widget build(BuildContext context) { return Transform.scale( scale: scale, child: Transform.rotate( angle: rotation, child: Container( ///Oval card in the background decoration: BoxDecoration( color: color, borderRadius: BorderRadius.only( topLeft: Radius.circular(150), topRight: Radius.circular(240), bottomLeft: Radius.circular(220), bottomRight: Radius.circular(180),),),),),); }}Copy the code
-
Use to store multiple background ellipses in a Stack
First, we define the state, which is one: a bool isPlaying button, play/pause, and the second part is the rotation and scale values that apply to our Blob seconds. We will animate these rotation and scaling values later.
bool isPlaying; @override void initState() { isPlaying = widget.initialIsPlaying; super.initState(); } return Container( // Control the size of the play button width: widget.iconSize + 30, height: widget.iconSize + 30, child: Stack( alignment: Alignment.center, children: [ if(_showWaves) ... [// Create 3 ellipses for stacking Blob(color: Color(0xff0092ff), scale: _scale, rotation: _rotation), Blob( color: Color(0xff4ac7b7), scale: _scale, rotation: _rotation * 2 - 30), Blob( color: Color(0xffa4a6f6), scale: _scale, rotation: _rotation * 3 - 45), ], Container( constraints: BoxConstraints.expand(), child: IconButton( icon: isPlaying ? widget.pauseIcon : widget.playIcon, onPressed: _onToggle, ), decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.white, ), ), ], ), ); Copy the code
-
Animation state control processing
A common way to add animation to a flutter is to create an AnimationController, Need SingleTickerProviderStateMixinmixin (or TickerProviderStateMixin for using multiple controllers), addListener it setState on stateful widgets in reconstruction.
First we add two AnimationControllers to the widget, and then two constants, which are two durations, one for each controller. We’ll mark them static const because they never change.
static const _kToggleDuration = Duration(milliseconds: 300); static const _kRotationDuration = Duration(seconds: 5); AnimationController _rotationController; AnimationController _scaleController; Copy the code
We need to initialize the controller in the initState method. Because we’re going to keep rotating, we’re going to use our repeat method, _rotationController which will also immediately launch our animation. The _scaleController is similar, but can only run when the button is pressed.
@override void initState() { isPlaying = widget.initialIsPlaying; _rotationController = AnimationController(vsync: this, duration: _kRotationDuration) .. addListener(() => setState(_updateRotation)) .. repeat(); _scaleController = AnimationController(vsync: this, duration: _kToggleDuration) .. addListener(() => setState(_updateScale)); super.initState(); }Copy the code
For smoother animation, I also defined two helper methods to update rotation and scaling.
void _updateRotation() => _rotation = _rotationController.value * 2 * pi; void _updateScale() => _scale = (_scaleController.value * 0.2) + 0.85; Copy the code
-
Trigger animation, button click to start animation
void_onToggle() { setState(() { isPlaying = ! isPlaying; _scale = _scale ==1 ? 85. : 1; }); widget.onPressed(); } Copy the code
This completes the button effect
2. Round-robin graph playback
-
Analysis:
1. Format time
2. The music playback function needs to be implemented
3. Switch the song function left and right
4. Locate and process the progress bar
-
Formatting time handling
We will get two parameters, one is the current playing time of the song, and one is the total time of the song
We need to format it
The method is similar to finding integers and the rest
MinuteString gets the current minute
SecondString gets the current number of seconds
String transformString(int seconds) {
String minuteString =
'${(seconds / 60).floor() < 10 ? 0 : ''}${(seconds / 60).floor()}';
String secondString = '${seconds % 60 < 10 ? 0 : ''}${seconds % 60}';
return '$minuteString:$secondString';
}
Copy the code
Use:
Current Playing time:
Text(
transformString(realtimePlayingInfos.currentPosition.inSeconds),
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.bold, fontSize: 17),),Copy the code
Song total time is also the same writing method, but different parameters, detailed look at the source code ha ~
-
Music playback function realization
This is the most important feature of a player
This article uses assets_audio_player, but you can also use other plug-ins if you have other requirements. The principle is similar
Let’s first define static resources:
// Music name and picture name cannot be Chinese!! Chinese is unreadable List<Audio> audioList = [ Audio('assets/daphneOdera.mp3', metas: Metas( title: 'Qilixiang', artist: 'Jay', image: MetasImage.asset('assets/daphneOdera.jpg'))),... Other songs];Copy the code
For the player we need a controller
final AssetsAudioPlayer audioPlayer = AssetsAudioPlayer(); Copy the code
Step 1: We need to initialize it:
@override void initState() { super.initState(); setupPlaylist(); } void setupPlaylist() async { // Initializes the player, as well as static resources audioPlayer.open(Playlist(audios: audioList), autoStart: false, loopMode: LoopMode.playlist); } Copy the code
Step 2: Play and pause control
This time we need to use the plugin’s RealtimePlayingInfos parameter to get static resources
Widget playBar(RealtimePlayingInfos realtimePlayingInfos) { return PlayButton( //PlayButton is the pre-wrapped PlayButton onPressed: () => audioPlayer.playOrPause(), // Play or pause initialIsPlaying:false, iconColor: highlightColor, iconSize:screenHeight * 0.06,); }Copy the code
This simple playback function is complete, the plug-in package is very simple
-
Switch song function left and right
This is also the hardest part of the player, because you have to cut through the song to determine which song is currently playing
Wheel broadcast graph implementation, here using a plug-in, if there is a special need, you can also use PageView to write their own
// Pass in the RealtimePlayingInfos argument Widget swiper(RealtimePlayingInfos realtimePlayingInfos) { // Define the carousel graph return Container( width: screenWidth, height: screenHeight * 0.45, child: Swiper( controller: swiperController, itemCount: audioList.length, itemBuilder: (context, index) { return ClipRRect( borderRadius: BorderRadius.circular(50.0), child: Image.asset( // Get the song cover audioList[index].metas.image.path, fit: BoxFit.cover, ), ); }, onIndexChanged: (newIndex) async { // New parameters are passed to locate the current song when switching left and right audioPlayer.playlistPlayAtIndex(newIndex); }, viewportFraction: 0.75, scale: 0.8,),); }Copy the code
-
Progress bar locating
The current time and total duration are calculated. For details, see the SliderTheme, where two parameters are passed in.
The AudioPlayer.seek () method is used to locate the drag position
Widget slider(RealtimePlayingInfos realtimePlayingInfos) {
return SliderTheme(
data: SliderThemeData(
thumbColor: highlightColor,
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 1),
activeTrackColor: highlightColor,
inactiveTrackColor: Colors.grey[800],
overlayColor: Colors.transparent),
child: Slider.adaptive(
value: realtimePlayingInfos.currentPosition.inSeconds.toDouble(),
max: realtimePlayingInfos.duration.inSeconds.toDouble(),
min: 0,
onChanged: (value) {
audioPlayer.seek(Duration(seconds: value.toInt()));
}));
}
Copy the code
-
Left and right buttons switch song processing
A song on swiperController. Previous ()
Swipercontroller.next () Next song
IconButton( icon: Icon(Icons.skip_previous_rounded), onPressed: () => swiperController.previous(), iconSize: screenHeight * 0.06, color: Colors.white, splashColor: Colors.transparent, highlightColor: Colors.transparent, ), Copy the code
This completes the player of the round – cast image effect
The second effect is mainly some differences in layout, there is no difference in function, mainly the style of the progress bar is a little different, here is a simple analysis:
The main thing is that there’s a little dot when you drag, and the code doesn’t differ much.
Widget slider(RealtimePlayingInfos realtimePlayingInfos) {
return SliderTheme(
data: SliderThemeData(
trackShape: CustomTrackShape(),
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 8)),
child: Slider.adaptive(
value: realtimePlayingInfos.currentPosition.inSeconds.toDouble(),
max: realtimePlayingInfos.duration.inSeconds.toDouble(),
activeColor: Color(0xffe3eb6b),
inactiveColor: Colors.grey[850],
onChanged: (value) {
audioPlayer.seek(Duration(seconds: value.toInt())); })); }Copy the code
The end of this article, see the brothers here to give a little praise, it is a day of hair loss ðŸ˜
Cartoon style music player
Cool wavy style music player
The source code is here