自己封装flutter音频播放器
flutter 封装音频类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/conf
·
主要使用插件 just_audio: ^0.5.6
flutter 封装音频类
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;
}
}
使用方式
// 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),
更多推荐
已为社区贡献3条内容
所有评论(0)