2021-03-10 23:02:41 +00:00
|
|
|
import 'dart:io';
|
|
|
|
|
|
|
|
import 'package:audioplayers/audioplayers.dart';
|
|
|
|
import 'package:comunic/utils/date_utils.dart';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
|
|
|
/// Audio player widget
|
|
|
|
///
|
|
|
|
/// @author Pierre Hubert
|
|
|
|
|
|
|
|
class AudioPlayerWidget extends StatefulWidget {
|
|
|
|
final File file;
|
|
|
|
|
|
|
|
const AudioPlayerWidget(this.file);
|
|
|
|
|
|
|
|
@override
|
|
|
|
_AudioPlayerWidgetState createState() => _AudioPlayerWidgetState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _AudioPlayerWidgetState extends State<AudioPlayerWidget> {
|
|
|
|
AudioPlayer _player;
|
|
|
|
|
|
|
|
Duration _mediaDuration;
|
|
|
|
Duration _mediaPosition;
|
|
|
|
|
|
|
|
double get _max => _mediaDuration?.inMilliseconds?.toDouble() ?? 0.0;
|
|
|
|
|
|
|
|
double get _value => _mediaPosition?.inMilliseconds?.toDouble() ?? 0.0;
|
|
|
|
|
|
|
|
bool get _playing =>
|
|
|
|
_player != null && _player.state == AudioPlayerState.PLAYING;
|
|
|
|
|
|
|
|
@override
|
|
|
|
void dispose() {
|
|
|
|
super.dispose();
|
|
|
|
|
|
|
|
if (_player != null) _player.dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return Material(
|
|
|
|
textStyle: TextStyle(color: Colors.white),
|
|
|
|
color: Colors.transparent,
|
|
|
|
child: Column(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
|
children: <Widget>[
|
|
|
|
Spacer(),
|
|
|
|
Icon(Icons.audiotrack, color: Colors.white),
|
|
|
|
Spacer(),
|
|
|
|
Slider(
|
|
|
|
value: _value,
|
|
|
|
onChanged: (newValue) =>
|
|
|
|
_player.seek(Duration(milliseconds: newValue.toInt())),
|
|
|
|
max: _max,
|
|
|
|
activeColor: Colors.white,
|
|
|
|
min: 0,
|
|
|
|
),
|
|
|
|
Spacer(),
|
|
|
|
Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
|
children: <Widget>[
|
|
|
|
Spacer(),
|
|
|
|
Text(formatDuration(_mediaPosition ?? Duration())),
|
|
|
|
Spacer(),
|
|
|
|
_AudioButton(
|
|
|
|
icon: Icons.play_arrow, onTap: _play, visible: !_playing),
|
|
|
|
_AudioButton(icon: Icons.pause, onTap: _pause, visible: _playing),
|
|
|
|
_AudioButton(icon: Icons.stop, onTap: _stop, visible: _playing),
|
|
|
|
Spacer(),
|
2021-03-10 23:06:29 +00:00
|
|
|
_playing ? Container() : Text(formatDuration(_mediaDuration ?? Duration())),
|
2021-03-10 23:02:41 +00:00
|
|
|
Spacer(),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
Spacer(),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
void _play() async {
|
|
|
|
if (_player == null) {
|
|
|
|
_player = AudioPlayer();
|
|
|
|
|
|
|
|
_player.onDurationChanged.listen((newDuration) {
|
|
|
|
setState(() => _mediaDuration = newDuration);
|
|
|
|
});
|
|
|
|
|
|
|
|
_player.onAudioPositionChanged.listen((newDuration) {
|
|
|
|
setState(() => _mediaPosition = newDuration);
|
|
|
|
});
|
|
|
|
|
|
|
|
_player.onPlayerStateChanged.listen((event) => setState(() {}));
|
|
|
|
|
|
|
|
_player.onSeekComplete.listen((event) => setState(() {}));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_player.state != AudioPlayerState.PAUSED)
|
|
|
|
_player.play(widget.file.absolute.path, isLocal: true);
|
|
|
|
else
|
|
|
|
_player.resume();
|
|
|
|
}
|
|
|
|
|
|
|
|
void _pause() async {
|
|
|
|
_player.pause();
|
|
|
|
}
|
|
|
|
|
|
|
|
void _stop() {
|
|
|
|
_player.stop();
|
|
|
|
_player.seek(Duration());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class _AudioButton extends StatelessWidget {
|
|
|
|
final IconData icon;
|
|
|
|
final void Function() onTap;
|
|
|
|
final bool visible;
|
|
|
|
|
|
|
|
const _AudioButton({
|
|
|
|
Key key,
|
|
|
|
@required this.icon,
|
|
|
|
@required this.onTap,
|
|
|
|
@required this.visible,
|
|
|
|
}) : assert(icon != null),
|
|
|
|
assert(onTap != null),
|
|
|
|
assert(visible != null),
|
|
|
|
super(key: key);
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
if (!visible) return Container();
|
|
|
|
|
|
|
|
return IconButton(icon: Icon(icon, color: Colors.white), onPressed: onTap);
|
|
|
|
}
|
|
|
|
}
|