Skip to content

Commit

Permalink
feat: move simple progress indicator to bottom, add swipe gesture
Browse files Browse the repository at this point in the history
  • Loading branch information
Yesterday17 committed Sep 16, 2024
1 parent e0a7724 commit 8e1a33c
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 74 deletions.
4 changes: 4 additions & 0 deletions lib/services/playback/playback_playing.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ class PlayingTrack extends ChangeNotifier {
Duration position = Duration.zero;
Duration duration = Duration.zero;

double get progress => duration.inMilliseconds == 0
? 0
: position.inMilliseconds / duration.inMilliseconds;

void updatePosition(final Duration position) {
this.position = position;
notifyListeners();
Expand Down
22 changes: 11 additions & 11 deletions lib/services/playback/playback_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ class PlaybackService extends ChangeNotifier {
this.queue = queue
.map((final e) => AnnilAudioSource.fromJson(jsonDecode(e)))
.toList();
setPlayingIndex(playingIndex);
_setPlayingIndex(playingIndex);
}

final loopMode = preferences.getInt('player.loopMode');
Expand Down Expand Up @@ -224,7 +224,7 @@ class PlaybackService extends ChangeNotifier {
final currentIndex = playingIndex;
if (queue.isNotEmpty && currentIndex != null) {
if (shuffleMode == ShuffleMode.on) {
await setPlayingIndex(rng.nextInt(queue.length));
await _setPlayingIndex(rng.nextInt(queue.length));
await play(reload: true);
return;
}
Expand All @@ -233,13 +233,13 @@ class PlaybackService extends ChangeNotifier {
case LoopMode.off:
// to the next song / stop
if (currentIndex > 0) {
await setPlayingIndex(currentIndex - 1);
await _setPlayingIndex(currentIndex - 1);
await play(reload: true);
}
break;
case LoopMode.all:
// to the previous song / last song
await setPlayingIndex(
await _setPlayingIndex(
(currentIndex > 0 ? currentIndex : queue.length) - 1);
await play(reload: true);
break;
Expand All @@ -257,7 +257,7 @@ class PlaybackService extends ChangeNotifier {
final currentIndex = playingIndex;
if (queue.isNotEmpty && currentIndex != null) {
if (shuffleMode == ShuffleMode.on) {
await setPlayingIndex(rng.nextInt(queue.length));
await _setPlayingIndex(rng.nextInt(queue.length));
await play(reload: true);
return;
}
Expand All @@ -266,15 +266,15 @@ class PlaybackService extends ChangeNotifier {
case LoopMode.off:
// to the next song / stop
if (currentIndex < queue.length - 1) {
await setPlayingIndex(currentIndex + 1);
await _setPlayingIndex(currentIndex + 1);
await play(reload: true);
} else {
await stop();
}
break;
case LoopMode.all:
// to the next song / first song
await setPlayingIndex((currentIndex + 1) % queue.length);
await _setPlayingIndex((currentIndex + 1) % queue.length);
await play(reload: true);
break;
case LoopMode.one:
Expand Down Expand Up @@ -306,7 +306,7 @@ class PlaybackService extends ChangeNotifier {

queue.removeAt(index);
if (removeCurrentPlayingTrack) {
await setPlayingIndex(index, notify: false);
await _setPlayingIndex(index, notify: false);
await play(reload: true);
}
notifyListeners();
Expand All @@ -318,7 +318,7 @@ class PlaybackService extends ChangeNotifier {
final to = index % queue.length;
if (to != playingIndex) {
// index changed, set new audio source
await setPlayingIndex(to);
await _setPlayingIndex(to);
await play(reload: true);
} else {
// index not changed, seek to start
Expand All @@ -339,7 +339,7 @@ class PlaybackService extends ChangeNotifier {
ref.read(preferencesProvider).set('player.shuffleMode', shuffleMode.index);
}

Future<void> setPlayingIndex(final int index,
Future<void> _setPlayingIndex(final int index,
{final bool reload = false, final bool notify = true}) async {
loadedAndPaused = false;

Expand All @@ -360,7 +360,7 @@ class PlaybackService extends ChangeNotifier {
queue = songs;
// 2. set playing index
if (songs.isNotEmpty) {
await setPlayingIndex(initialIndex % songs.length,
await _setPlayingIndex(initialIndex % songs.length,
reload: true, notify: false);
} else {
playing.setSource(null);
Expand Down
88 changes: 26 additions & 62 deletions lib/ui/bottom_player/bottom_player_mobile.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:annix/providers.dart';
import 'package:annix/ui/widgets/artist_text.dart';
import 'package:annix/ui/widgets/cover.dart';
import 'package:annix/ui/widgets/buttons/play_pause_button.dart';
import 'package:annix/ui/widgets/swiper.dart';
import 'package:annix/utils/context_extension.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
Expand All @@ -28,69 +28,33 @@ class MobileBottomPlayer extends StatelessWidget {
),
),
height: height,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
child: Stack(
alignment: Alignment.bottomCenter,
children: [
Container(
padding: const EdgeInsets.all(8),
child: const PlayingMusicCover(card: true, animated: false),
),
Expanded(
flex: 1,
child: Consumer(
builder: (final context, final ref, final child) {
final playing = ref.watch(playingProvider);
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
playing.source?.track.title ?? '',
style: context.textTheme.titleSmall,
overflow: TextOverflow.ellipsis,
softWrap: false,
),
ArtistText(
playing.source?.track.artist ?? '',
style: context.textTheme.bodySmall,
),
],
);
},
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Consumer(
builder: (final context, final ref, final child) {
final double? progress =
ref.watch(playingProvider.select((final playing) {
if (playing.source == null) {
return null;
}

if (playing.duration == Duration.zero) {
return 0;
}

return playing.position.inMicroseconds /
playing.duration.inMicroseconds;
}));
if (progress == null) {
return child!;
}

return Stack(
alignment: Alignment.center,
children: [
CircularProgressIndicator(value: progress),
child!,
],
);
},
child: PlayPauseButton.small(),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding: const EdgeInsets.all(8),
child: const PlayingMusicCover(card: true, animated: false),
),
const Expanded(
flex: 1,
child: PlayingTrackSwiper(),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: PlayPauseButton.small(),
),
],
),
Consumer(builder: (context, ref, child) {
final playing = ref.watch(playingProvider);
return LinearProgressIndicator(
value: playing.progress,
minHeight: 2,
);
}),
],
),
);
Expand Down
2 changes: 1 addition & 1 deletion lib/ui/widgets/slide_up.dart
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ class _SlidingUpPanelState extends ConsumerState<SlidingUpPanel> {
if (_scrollableAxis == null) {
if (e.delta.dx.abs() > e.delta.dy.abs()) {
_scrollableAxis = Axis.horizontal;
} else {
} else if (e.delta.dx.abs() < e.delta.dy.abs()) {
_scrollableAxis = Axis.vertical;
}
}
Expand Down
76 changes: 76 additions & 0 deletions lib/ui/widgets/swiper.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import 'package:annix/providers.dart';
import 'package:annix/ui/widgets/artist_text.dart';
import 'package:annix/ui/widgets/slide_up.dart';
import 'package:annix/utils/context_extension.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

class HarderScrollPhysics extends ScrollPhysics {
const HarderScrollPhysics({super.parent});

@override
HarderScrollPhysics applyTo(ScrollPhysics? ancestor) {
return HarderScrollPhysics(parent: buildParent(ancestor));
}

@override
double applyPhysicsToUserOffset(ScrollMetrics position, double offset) {
return offset * 0.7;
}
}

class PlayingTrackSwiper extends HookConsumerWidget {
const PlayingTrackSwiper({super.key});

@override
Widget build(BuildContext context, final ref) {
final player = ref.watch(playbackProvider);
final queue = player.queue;
final playingIndex = player.playingIndex;
final controller = usePageController(initialPage: playingIndex ?? 0);

useEffect(() {
if (playingIndex != null && controller.hasClients) {
controller.jumpToPage(playingIndex);
}
return null;
}, [queue, playingIndex]);

return HorizontalScrollableWidget(
child: PageView.builder(
// if we need to add option to disable swipe
// physics: const NeverScrollableScrollPhysics(),
physics: const HarderScrollPhysics(),
controller: controller,
onPageChanged: (index) {
if (playingIndex != null && index != playingIndex) {
WidgetsBinding.instance.addPostFrameCallback((final _) {
player.jump(index);
});
}
},
itemCount: queue.length,
itemBuilder: (context, index) {
final track = queue[index].track;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
track.title,
style: context.textTheme.titleSmall,
overflow: TextOverflow.ellipsis,
softWrap: false,
),
ArtistText(
track.artist,
style: context.textTheme.bodySmall,
),
],
);
},
),
);
}
}

0 comments on commit 8e1a33c

Please sign in to comment.