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