美文网首页
自己封装flutter音频播放器

自己封装flutter音频播放器

作者: X先生_未知数的X | 来源:发表于2022-01-07 14:36 被阅读0次

    版权声明:本文为CSDN博主「华洛」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/sdta25196/article/details/115250786

    1.使用方式

    //  xxx.dart
      //定义控制器
      final EolAudioController controller = EolAudioController();
      // 定义mp3地址
      String _audio = "https://s3.amazonaws.com/scifri-segments/scifri201711241.mp3";
    // 调用
      EolAudio(source: _audio, timer: "00:00", controller: controller),
    

    2.封装音频类

    import 'dart:math';
    
    import 'package:flutter/material.dart';
    import 'package:just_audio/just_audio.dart';
    import 'package:flutter_screenutil/flutter_screenutil.dart';
    import 'package:zsgk/config/theme.dart';
    
    /*
    *
    * @author: 田源
    * @date: 2020-11-30 10:59
    * @description: eol音频组件
    *
    */
    
    class EolAudio extends StatefulWidget {
      /// 音频内容 mp3链接
      final String source;
    
      /// 音频长度
      final String timer;
    
      final EolAudioController controller;
    
      EolAudio({Key key, @required this.timer, @required this.source, @required this.controller}) : super(key: key);
      @override
      _EolAudioState createState() => _EolAudioState();
    }
    
    class _EolAudioState extends State<EolAudio> {
      /// 音频实例
      AudioPlayer audioPlayer = AudioPlayer();
    
      /// 是否已经加载过这个音频
      bool loaded = false;
    
      /// 控制播放中动画效果的显示
      bool isPlay = false;
    
      @override
      void initState() {
        super.initState();
      }
    
      @override
      void dispose() {
        audioPlayer?.dispose();
        super.dispose();
      }
    
      changePlayState(flag) {
        if (!mounted) return;
        setState(() {
          isPlay = flag;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Container(
          height: 100.w,
          padding: EdgeInsets.all(20.w),
          decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.all(Radius.circular(15.w)),
          ),
          child: Row(
            mainAxisSize: MainAxisSize.min,
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              // 播放动效
              Container(
                width: 80.w,
                child: Visibility(
                  visible: isPlay,
                  child: Image.asset(
                    "assets/images/icon/music.gif",
                    width: 80.w,
                    height: 20.w,
                  ),
                  replacement: Image.asset(
                    "assets/images/icon/music.png",
                    width: 80.w,
                    height: 20.w,
                  ),
                ),
              ),
              // 进度条
              Expanded(
                flex: 1,
                child: StreamBuilder<Duration>(
                  stream: audioPlayer.durationStream,
                  builder: (context, snapshot) {
                    final duration = snapshot.data ?? Duration.zero;
                    return StreamBuilder<Duration>(
                      stream: audioPlayer.positionStream,
                      builder: (context, snapshot) {
                        var position = snapshot.data ?? Duration.zero;
                        if (position > duration) {
                          position = duration;
                        }
                        return SeekBar(
                          duration: duration,
                          position: position,
                          timer: widget.timer,
                          onChangeEnd: (newPosition) {
                            audioPlayer.seek(newPosition);
                          },
                        );
                      },
                    );
                  },
                ),
              ),
              // 播放按钮
              Container(
                width: 42.w,
                child: StreamBuilder<PlayerState>(
                  stream: audioPlayer.playerStateStream,
                  builder: (context, snapshot) {
                    final playerState = snapshot.data;
                    final processingState = playerState?.processingState;
                    final playing = playerState?.playing;
                    if (processingState == ProcessingState.loading || processingState == ProcessingState.buffering) {
                      return Transform.scale(
                        scale: 0.7,
                        child: Container(
                          width: 42.w,
                          height: 42.w,
                          child: CircularProgressIndicator(
                            strokeWidth: 5.w,
                            valueColor: AlwaysStoppedAnimation<Color>(Colors.red),
                          ),
                        ),
                      );
                    } else if (playing != true) {
                      return InkWell(
                        onTap: () async {
                          if (widget.controller.activeAudio != this) {
                            widget.controller.activeAudio?.changePlayState(false);
                            widget.controller.audioPlayer?.pause();
                          }
                          // 音频只加载一次
                          if (!loaded) {
                            await audioPlayer.load(
                              AudioSource.uri(Uri.parse(widget.source)),
                            );
                            setState(() {
                              loaded = true;
                            });
                          }
                          audioPlayer.play();
                          changePlayState(true);
                          widget.controller.activeAudio = this;
                          widget.controller.audioPlayer = audioPlayer;
                        },
                        child: Container(
                          child: Icon(
                            Icons.play_circle_outline,
                            color: Colors.red,
                            size: 42.w,
                          ),
                        ),
                      );
                    } else if (processingState != ProcessingState.completed) {
                      return InkWell(
                        onTap: () {
                          changePlayState(false);
                          audioPlayer.pause();
                        },
                        child: Container(
                          child: Icon(
                            Icons.pause_circle_outline,
                            color: Colors.red,
                            size: 42.w,
                          ),
                        ),
                      );
                    } else {
                      return InkWell(
                        onTap: () {
                          changePlayState(false);
                          audioPlayer.seek(Duration.zero, index: 0);
                        },
                        child: Container(
                          child: Icon(
                            Icons.play_circle_outline,
                            color: Colors.red,
                            size: 42.w,
                          ),
                        ),
                      );
                    }
                  },
                ),
              ),
            ],
          ),
        );
      }
    }
    
    class SeekBar extends StatefulWidget {
      /// 音频长度
      final String timer;
      final Duration duration;
      final Duration position;
      final ValueChanged<Duration> onChanged;
      final ValueChanged<Duration> onChangeEnd;
    
      SeekBar({
        @required this.duration,
        @required this.position,
        @required this.timer,
        this.onChanged,
        this.onChangeEnd,
      });
    
      @override
      _SeekBarState createState() => _SeekBarState();
    }
    
    class _SeekBarState extends State<SeekBar> {
      double _dragValue;
    
      @override
      Widget build(BuildContext context) {
        return Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Container(
              child: Text(
                RegExp(r'((^0*[1-9]\d*:)?\d{2}:\d{2})\.\d+$').firstMatch('$_playing')?.group(1) ?? '$_playing"',
                style: TextStyle(color: Colors.red, fontSize: 26.sp),
              ),
            ),
            Container(
              constraints: BoxConstraints(maxWidth: 300.w),
              child: SliderTheme(
                data: SliderThemeData(
                  activeTrackColor: Colors.red,
                  inactiveTrackColor: Color(0xffeeeeee),
                  thumbColor: Colors.red,
                  thumbShape: ARoundSliderThumbShape(enabledThumbRadius: 0.w),
                ),
                child: Slider(
                  min: 0.0,
                  max: widget.duration.inMilliseconds.toDouble(),
                  value: min(_dragValue ?? widget.position.inMilliseconds.toDouble(), widget.duration.inMilliseconds.toDouble()),
                  onChanged: (value) {
                    setState(() {
                      _dragValue = value;
                    });
                    if (widget.onChanged != null) {
                      widget.onChanged(Duration(milliseconds: value.round()));
                    }
                  },
                  onChangeEnd: (value) {
                    if (widget.onChangeEnd != null) {
                      widget.onChangeEnd(Duration(milliseconds: value.round()));
                    }
                    _dragValue = null;
                  },
                ),
              ),
            ),
            Container(
              child: Text(
                widget.timer,
                style: TextStyle(color: Colors.red, fontSize: 26.sp),
              ),
            ),
          ],
        );
      }
    
      Duration get _playing => widget.position;
    }
    
    /// 重写slider指示器
    class ARoundSliderThumbShape extends SliderComponentShape {
      /// Create a slider thumb that draws a circle.
      const ARoundSliderThumbShape({
        this.enabledThumbRadius = 10.0,
        this.disabledThumbRadius,
      });
    
      /// The preferred radius of the round thumb shape when the slider is enabled.
      ///
      /// If it is not provided, then the material default of 10 is used.
      final double enabledThumbRadius;
    
      /// The preferred radius of the round thumb shape when the slider is disabled.
      ///
      /// If no disabledRadius is provided, then it is equal to the
      /// [enabledThumbRadius]
      final double disabledThumbRadius;
      double get _disabledThumbRadius => disabledThumbRadius ?? enabledThumbRadius;
    
      @override
      Size getPreferredSize(bool isEnabled, bool isDiscrete) {
        return Size.fromRadius(isEnabled == true ? enabledThumbRadius : _disabledThumbRadius);
      }
    
      @override
      void paint(
        PaintingContext context,
        Offset center, {
        Animation<double> activationAnimation,
        Animation<double> enableAnimation,
        bool isDiscrete,
        TextPainter labelPainter,
        RenderBox parentBox,
        SliderThemeData sliderTheme,
        TextDirection textDirection,
        double value,
        double textScaleFactor,
        Size sizeWithOverflow,
      }) {
        assert(context != null);
        assert(center != null);
        assert(enableAnimation != null);
        assert(sliderTheme != null);
        assert(sliderTheme.disabledThumbColor != null);
        assert(sliderTheme.thumbColor != null);
    
        final Canvas canvas = context.canvas;
        Rect rect = Rect.fromCenter(center: center, width: 6.w, height: 20.w);
        canvas.drawRect(rect, Paint()..color = Colors.red);
      }
    }
    
    /// 音频插件控制器
    class EolAudioController {
      /// 当前活跃的音频
      _EolAudioState _activeAudio;
    
      _EolAudioState get activeAudio => _activeAudio;
    
      set activeAudio(var value) {
        _activeAudio = value;
      }
    
      /// 当前活跃的音频播放器
      AudioPlayer _audioPlayer;
    
      AudioPlayer get audioPlayer => _audioPlayer;
    
      set audioPlayer(var value) {
        _audioPlayer = value;
      }
    }
    

    相关文章

      网友评论

          本文标题:自己封装flutter音频播放器

          本文链接:https://www.haomeiwen.com/subject/onujcrtx.html