♻️ Improve progress display
This commit is contained in:
parent
2134500089
commit
249c8fbf80
@ -11,6 +11,10 @@ import 'package:spotify/spotify.dart' hide Playlist;
|
|||||||
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
||||||
|
|
||||||
class AudioPlayerProvider extends GetxController {
|
class AudioPlayerProvider extends GetxController {
|
||||||
|
Rx<Duration> durationTotal = Rx(Duration.zero);
|
||||||
|
Rx<Duration> durationCurrent = Rx(Duration.zero);
|
||||||
|
Rx<Duration> durationBuffered = Rx(Duration.zero);
|
||||||
|
|
||||||
RxBool isPlaying = false.obs;
|
RxBool isPlaying = false.obs;
|
||||||
|
|
||||||
Rx<AudioPlayerState> state = Rx(AudioPlayerState(
|
Rx<AudioPlayerState> state = Rx(AudioPlayerState(
|
||||||
@ -54,6 +58,11 @@ class AudioPlayerProvider extends GetxController {
|
|||||||
state.value = state.value.copyWith(playlist: playlist);
|
state.value = state.value.copyWith(playlist: playlist);
|
||||||
await _updatePlaylist(playlist);
|
await _updatePlaylist(playlist);
|
||||||
}),
|
}),
|
||||||
|
audioPlayer.durationStream.listen((value) => durationTotal.value = value),
|
||||||
|
audioPlayer.positionStream
|
||||||
|
.listen((value) => durationCurrent.value = value),
|
||||||
|
audioPlayer.bufferedPositionStream
|
||||||
|
.listen((value) => durationBuffered.value = value),
|
||||||
];
|
];
|
||||||
|
|
||||||
_readSavedState();
|
_readSavedState();
|
||||||
|
@ -39,13 +39,6 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
|||||||
bool get _isFetchingActiveTrack => _query.isQueryingTrackInfo.value;
|
bool get _isFetchingActiveTrack => _query.isQueryingTrackInfo.value;
|
||||||
PlaylistMode get _loopMode => _playback.state.value.loopMode;
|
PlaylistMode get _loopMode => _playback.state.value.loopMode;
|
||||||
|
|
||||||
double _bufferProgress = 0;
|
|
||||||
|
|
||||||
Duration _durationCurrent = Duration.zero;
|
|
||||||
Duration _durationTotal = Duration.zero;
|
|
||||||
|
|
||||||
List<StreamSubscription>? _subscriptions;
|
|
||||||
|
|
||||||
Future<void> _togglePlayState() async {
|
Future<void> _togglePlayState() async {
|
||||||
if (!audioPlayer.isPlaying) {
|
if (!audioPlayer.isPlaying) {
|
||||||
await audioPlayer.resume();
|
await audioPlayer.resume();
|
||||||
@ -57,32 +50,6 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
|||||||
|
|
||||||
double? _draggingValue;
|
double? _draggingValue;
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_durationCurrent = audioPlayer.position;
|
|
||||||
_durationTotal = audioPlayer.duration;
|
|
||||||
_bufferProgress = audioPlayer.bufferedPosition.inMilliseconds.toDouble();
|
|
||||||
_subscriptions = [
|
|
||||||
audioPlayer.durationStream
|
|
||||||
.listen((dur) => setState(() => _durationTotal = dur)),
|
|
||||||
audioPlayer.positionStream
|
|
||||||
.listen((dur) => setState(() => _durationCurrent = dur)),
|
|
||||||
audioPlayer.bufferedPositionStream.listen((dur) =>
|
|
||||||
setState(() => _bufferProgress = dur.inMilliseconds.toDouble())),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
if (_subscriptions != null) {
|
|
||||||
for (final subscription in _subscriptions!) {
|
|
||||||
subscription.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final size = MediaQuery.of(context).size;
|
final size = MediaQuery.of(context).size;
|
||||||
@ -134,7 +101,8 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
const Gap(24),
|
const Gap(24),
|
||||||
Column(
|
Obx(
|
||||||
|
() => Column(
|
||||||
children: [
|
children: [
|
||||||
SliderTheme(
|
SliderTheme(
|
||||||
data: SliderThemeData(
|
data: SliderThemeData(
|
||||||
@ -146,20 +114,25 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
|||||||
overlayShape: SliderComponentShape.noOverlay,
|
overlayShape: SliderComponentShape.noOverlay,
|
||||||
),
|
),
|
||||||
child: Slider(
|
child: Slider(
|
||||||
secondaryTrackValue: _bufferProgress.abs(),
|
secondaryTrackValue: _playback
|
||||||
|
.durationBuffered.value.inMilliseconds
|
||||||
|
.abs()
|
||||||
|
.toDouble(),
|
||||||
value: _draggingValue?.abs() ??
|
value: _draggingValue?.abs() ??
|
||||||
_durationCurrent.inMilliseconds.toDouble().abs(),
|
_playback.durationCurrent.value.inMilliseconds
|
||||||
|
.toDouble()
|
||||||
|
.abs(),
|
||||||
min: 0,
|
min: 0,
|
||||||
max: max(
|
max: max(
|
||||||
_durationTotal.inMilliseconds.abs(),
|
_playback.durationCurrent.value.inMilliseconds.abs(),
|
||||||
_durationTotal.inMilliseconds.abs(),
|
_playback.durationTotal.value.inMilliseconds.abs(),
|
||||||
).toDouble(),
|
).toDouble(),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() => _draggingValue = value);
|
setState(() => _draggingValue = value);
|
||||||
},
|
},
|
||||||
onChangeEnd: (value) {
|
onChangeEnd: (value) {
|
||||||
print('Seek to $value ms');
|
audioPlayer
|
||||||
audioPlayer.seek(Duration(milliseconds: value.toInt()));
|
.seek(Duration(milliseconds: value.toInt()));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -167,17 +140,19 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
|||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
_durationCurrent.toHumanReadableString(),
|
_playback.durationCurrent.value
|
||||||
|
.toHumanReadableString(),
|
||||||
style: GoogleFonts.robotoMono(fontSize: 12),
|
style: GoogleFonts.robotoMono(fontSize: 12),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
_durationTotal.toHumanReadableString(),
|
_playback.durationTotal.value.toHumanReadableString(),
|
||||||
style: GoogleFonts.robotoMono(fontSize: 12),
|
style: GoogleFonts.robotoMono(fontSize: 12),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).paddingSymmetric(horizontal: 8, vertical: 4),
|
).paddingSymmetric(horizontal: 8, vertical: 4),
|
||||||
],
|
],
|
||||||
).paddingSymmetric(horizontal: 24),
|
).paddingSymmetric(horizontal: 24),
|
||||||
|
),
|
||||||
const Gap(24),
|
const Gap(24),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
@ -107,7 +107,7 @@ class _SyncedLyricsState extends State<SyncedLyrics> {
|
|||||||
)
|
)
|
||||||
: Padding(
|
: Padding(
|
||||||
padding: idx == _lyric!.lyrics.length - 1
|
padding: idx == _lyric!.lyrics.length - 1
|
||||||
? const EdgeInsets.all(8.0).copyWith(bottom: 100)
|
? const EdgeInsets.all(8.0).copyWith(bottom: 80)
|
||||||
: const EdgeInsets.all(8.0),
|
: const EdgeInsets.all(8.0),
|
||||||
child: AnimatedDefaultTextStyle(
|
child: AnimatedDefaultTextStyle(
|
||||||
duration: const Duration(milliseconds: 250),
|
duration: const Duration(milliseconds: 250),
|
||||||
@ -141,8 +141,9 @@ class _SyncedLyricsState extends State<SyncedLyrics> {
|
|||||||
),
|
),
|
||||||
).animate(target: isActive ? 1 : 0).scale(
|
).animate(target: isActive ? 1 : 0).scale(
|
||||||
duration: 300.ms,
|
duration: 300.ms,
|
||||||
begin: const Offset(0.9, 0.9),
|
begin: const Offset(1, 1),
|
||||||
end: const Offset(1.3, 1.3),
|
end: const Offset(1.25, 1.25),
|
||||||
|
curve: Curves.easeInOut,
|
||||||
);
|
);
|
||||||
}).paddingSymmetric(horizontal: 12),
|
}).paddingSymmetric(horizontal: 12),
|
||||||
),
|
),
|
||||||
|
@ -44,9 +44,6 @@ class _BottomPlayerState extends State<BottomPlayer>
|
|||||||
bool get _isPlaying => _playback.isPlaying.value;
|
bool get _isPlaying => _playback.isPlaying.value;
|
||||||
bool get _isFetchingActiveTrack => _query.isQueryingTrackInfo.value;
|
bool get _isFetchingActiveTrack => _query.isQueryingTrackInfo.value;
|
||||||
|
|
||||||
Duration _durationCurrent = Duration.zero;
|
|
||||||
Duration _durationTotal = Duration.zero;
|
|
||||||
|
|
||||||
List<StreamSubscription>? _subscriptions;
|
List<StreamSubscription>? _subscriptions;
|
||||||
|
|
||||||
Future<void> _togglePlayState() async {
|
Future<void> _togglePlayState() async {
|
||||||
@ -63,10 +60,6 @@ class _BottomPlayerState extends State<BottomPlayer>
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_subscriptions = [
|
_subscriptions = [
|
||||||
audioPlayer.durationStream
|
|
||||||
.listen((dur) => setState(() => _durationTotal = dur)),
|
|
||||||
audioPlayer.positionStream
|
|
||||||
.listen((dur) => setState(() => _durationCurrent = dur)),
|
|
||||||
_playback.state.listen((state) {
|
_playback.state.listen((state) {
|
||||||
if (state.playlist.medias.isNotEmpty && !_isLifted) {
|
if (state.playlist.medias.isNotEmpty && !_isLifted) {
|
||||||
_animationController.animateTo(1);
|
_animationController.animateTo(1);
|
||||||
@ -109,12 +102,12 @@ class _BottomPlayerState extends State<BottomPlayer>
|
|||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
if (_durationCurrent != Duration.zero)
|
if (_playback.durationCurrent.value != Duration.zero)
|
||||||
TweenAnimationBuilder<double>(
|
TweenAnimationBuilder<double>(
|
||||||
tween: Tween(
|
tween: Tween(
|
||||||
begin: 0,
|
begin: 0,
|
||||||
end: _durationCurrent.inMilliseconds /
|
end: _playback.durationCurrent.value.inMilliseconds /
|
||||||
max(_durationTotal.inMilliseconds, 1),
|
max(_playback.durationTotal.value.inMilliseconds, 1),
|
||||||
),
|
),
|
||||||
duration: const Duration(milliseconds: 1000),
|
duration: const Duration(milliseconds: 1000),
|
||||||
builder: (context, value, _) => LinearProgressIndicator(
|
builder: (context, value, _) => LinearProgressIndicator(
|
||||||
|
Loading…
Reference in New Issue
Block a user