Make it possible to switch songs by swiping in the currently playing view (#39)

* Add swipe for switching songs

* Wip background scroll widget

* add album background list

* this looks better. I still need to figure out why the wrong picture is shown and how to have the picture move in from the right direction

* use correct picture for album art

* Remove unnecessary scaffold

* remove commented code

* Implement AlbumArt Swiping with PageView

Co-authored-by: Frieder Hannenheim <frieder12.fml@pm.me>
This commit is contained in:
Frieder Hannenheim 2023-01-14 19:00:14 +00:00 committed by GitHub
parent b02bf80649
commit 37e7a03255
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 99 additions and 22 deletions

View file

@ -2,11 +2,11 @@ import 'package:fimber/fimber.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:get_it/get_it.dart';
import 'package:mucke/presentation/widgets/album_art_swipe.dart';
import '../../domain/entities/song.dart';
import '../state/audio_store.dart';
import '../theming.dart';
import '../widgets/album_art.dart';
import '../widgets/album_background.dart';
import '../widgets/currently_playing_header.dart';
import '../widgets/playback_control.dart';
@ -30,9 +30,9 @@ class CurrentlyPlayingPage extends StatelessWidget {
body: SafeArea(
child: GestureDetector(
onVerticalDragEnd: (dragEndDetails) {
if (dragEndDetails.primaryVelocity! < 0) {
if (dragEndDetails.primaryVelocity! < -1) {
_openQueue(context);
} else if (dragEndDetails.primaryVelocity! > 0) {
} else if (dragEndDetails.primaryVelocity! > 1) {
Navigator.pop(context);
}
},
@ -40,7 +40,9 @@ class CurrentlyPlayingPage extends StatelessWidget {
builder: (BuildContext context) {
_log.d('Observer.build');
final Song? song = audioStore.currentSongStream.value;
final int? queueIndex = audioStore.queueIndexStream.value;
if (song == null) return Container();
if (queueIndex == null) return Container();
return Stack(
children: [
@ -70,8 +72,8 @@ class CurrentlyPlayingPage extends StatelessWidget {
horizontal: 8.0,
vertical: 0.0,
),
child: AlbumArt(
song: song,
child: AlbumArtSwipe(
queueIndex: queueIndex,
),
),
),

View file

@ -55,10 +55,12 @@ abstract class _AudioStore with Store {
this._playSmartList,
this._playPlaylist,
this._seekToNext,
this._shuffleAll, this._playPlayable,
this._shuffleAll,
this._playPlayable,
) {
_audioPlayerRepository.managedQueueInfo.queueItemsStream.listen(_setQueue);
_audioPlayerRepository.managedQueueInfo.availableSongsStream.listen((_) => _setAvSongs());
_audioPlayerRepository.managedQueueInfo.availableSongsStream
.listen((_) => _setAvSongs());
_audioPlayerRepository.shuffleModeStream.listen((_) => _setAvSongs());
_audioPlayerRepository.playableStream.listen((_) => _setAvSongs());
}
@ -79,11 +81,13 @@ abstract class _AudioStore with Store {
_audioPlayerRepository.currentSongStream.asObservable();
@observable
late ObservableStream<bool> playingStream = _audioPlayerRepository.playingStream.asObservable();
late ObservableStream<bool> playingStream =
_audioPlayerRepository.playingStream.asObservable();
@observable
late ObservableStream<Duration> currentPositionStream =
_audioPlayerRepository.positionStream.asObservable(initialValue: const Duration(seconds: 0));
late ObservableStream<Duration> currentPositionStream = _audioPlayerRepository
.positionStream
.asObservable(initialValue: const Duration(seconds: 0));
@readonly
late List<QueueItem> _queue = [];
@ -131,11 +135,22 @@ abstract class _AudioStore with Store {
@computed
bool get hasNext =>
(queueIndexStream.value != null && queueIndexStream.value! < _queue.length - 1) ||
(queueIndexStream.value != null &&
queueIndexStream.value! < _queue.length - 1) ||
(loopModeStream.value ?? LoopMode.off) != LoopMode.off;
Future<void> playSong(int index, List<Song> songList, Playable playable) async {
_playSongs(songs: songList, initialIndex: index, playable: playable, keepInitialIndex: true);
@computed
bool get hasPrevious =>
(queueIndexStream.value != null && queueIndexStream.value! > 0) ||
(loopModeStream.value ?? LoopMode.off) != LoopMode.off;
Future<void> playSong(
int index, List<Song> songList, Playable playable) async {
_playSongs(
songs: songList,
initialIndex: index,
playable: playable,
keepInitialIndex: true);
}
Future<void> play() async => _audioPlayerRepository.play();
@ -144,9 +159,11 @@ abstract class _AudioStore with Store {
Future<void> skipToNext() async => _seekToNext();
Future<void> skipToPrevious() async => _audioPlayerRepository.seekToPrevious();
Future<void> skipToPrevious() async =>
_audioPlayerRepository.seekToPrevious();
Future<void> seekToIndex(int index) async => _audioPlayerRepository.seekToIndex(index);
Future<void> seekToIndex(int index) async =>
_audioPlayerRepository.seekToIndex(index);
Future<void> seekToPosition(double position) async =>
_audioPlayerRepository.seekToPosition(position);
@ -154,15 +171,20 @@ abstract class _AudioStore with Store {
Future<void> setShuffleMode(ShuffleMode shuffleMode) async =>
_audioPlayerRepository.setShuffleMode(shuffleMode);
Future<void> setLoopMode(LoopMode loopMode) async => _audioPlayerRepository.setLoopMode(loopMode);
Future<void> setLoopMode(LoopMode loopMode) async =>
_audioPlayerRepository.setLoopMode(loopMode);
Future<void> shuffleAll(ShuffleMode shuffleMode) async => _shuffleAll(shuffleMode);
Future<void> shuffleAll(ShuffleMode shuffleMode) async =>
_shuffleAll(shuffleMode);
Future<void> addToQueue(List<Song> songs) async => _audioPlayerRepository.addToQueue(songs);
Future<void> addToQueue(List<Song> songs) async =>
_audioPlayerRepository.addToQueue(songs);
Future<void> playNext(List<Song> songs) async => _audioPlayerRepository.playNext(songs);
Future<void> playNext(List<Song> songs) async =>
_audioPlayerRepository.playNext(songs);
Future<void> appendToNext(List<Song> songs) async => _audioPlayerRepository.addToNext(songs);
Future<void> appendToNext(List<Song> songs) async =>
_audioPlayerRepository.addToNext(songs);
Future<void> moveQueueItem(int oldIndex, int newIndex) async =>
_audioPlayerRepository.moveQueueItem(oldIndex, newIndex);
@ -172,13 +194,15 @@ abstract class _AudioStore with Store {
Future<void> playAlbum(Album album) async => _playAlbum(album);
Future<void> playSmartList(SmartList smartList) async => _playSmartList(smartList);
Future<void> playSmartList(SmartList smartList) async =>
_playSmartList(smartList);
Future<void> playPlaylist(Playlist playlist) async => _playPlaylist(playlist);
Future<void> playArtist(Artist artist, ShuffleMode? shuffleMode) async =>
_playArtist(artist, shuffleMode);
Future<void> playPlayable(Playable playable, ShuffleMode? shuffleMode) async =>
Future<void> playPlayable(
Playable playable, ShuffleMode? shuffleMode) async =>
_playPlayable(playable, shuffleMode);
}

View file

@ -0,0 +1,51 @@
import 'package:fimber/fimber.dart';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:mucke/presentation/widgets/album_art.dart';
import '../state/audio_store.dart';
class AlbumArtSwipe extends StatefulWidget {
const AlbumArtSwipe({Key? key, required this.queueIndex}) : super(key: key);
final int queueIndex;
@override
_AlbumArtSwipeState createState() => _AlbumArtSwipeState(queueIndex: queueIndex);
}
class _AlbumArtSwipeState extends State<AlbumArtSwipe> {
_AlbumArtSwipeState({required this.queueIndex}) : super();
static final _log = FimberLog('AlbumArtSwipe');
int queueIndex;
late PageController controller;
static final AudioStore audioStore = GetIt.I<AudioStore>();
@override
void initState() {
super.initState();
controller = PageController(initialPage: audioStore.queueIndexStream.value!);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
controller = PageController(initialPage: queueIndex);
return PageView.builder(
controller: controller,
itemCount: audioStore.queueLength,
itemBuilder: (_, index) {
return AlbumArt(song: audioStore.queue[index].song);
},
onPageChanged: (value) {
audioStore.seekToIndex(value);
},
);
}
}