diff --git a/lib/domain/repositories/audio_repository.dart b/lib/domain/repositories/audio_repository.dart index 7cd6643..b326484 100644 --- a/lib/domain/repositories/audio_repository.dart +++ b/lib/domain/repositories/audio_repository.dart @@ -19,6 +19,7 @@ abstract class AudioRepository { Future> pause(); Future> skipToNext(); Future> skipToPrevious(); + Future setIndex(int index); Future> setShuffleMode(ShuffleMode shuffleMode); Future setLoopMode(LoopMode loopMode); diff --git a/lib/presentation/pages/queue_page.dart b/lib/presentation/pages/queue_page.dart index 578363e..a6d6787 100644 --- a/lib/presentation/pages/queue_page.dart +++ b/lib/presentation/pages/queue_page.dart @@ -1,7 +1,10 @@ +import 'dart:math'; + import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:mobx/mobx.dart'; import 'package:provider/provider.dart'; +import 'package:reorderables/reorderables.dart'; import '../../domain/entities/song.dart'; import '../state/audio_store.dart'; @@ -15,68 +18,59 @@ class QueuePage extends StatelessWidget { print('QueuePage.build'); final AudioStore audioStore = Provider.of(context); + final ObservableStream queueIndexStream = audioStore.queueIndexStream; + final initialIndex = max(((queueIndexStream?.value) ?? 0) - 2, 0); + final ScrollController _scrollController = ScrollController(initialScrollOffset: initialIndex * 72.0); + return Scaffold( body: SafeArea( - child: LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) => - Observer( - builder: (BuildContext context) { - print('QueuePage.build -> Observer.build'); - final ObservableStream> queueStream = - audioStore.queueStream; - final ObservableStream queueIndexStream = - audioStore.queueIndexStream; + child: Observer( + builder: (BuildContext context) { + print('QueuePage.build -> Observer.build'); + final ObservableStream> queueStream = audioStore.queueStream; - switch (queueStream.status) { - case StreamStatus.active: - return ReorderableListView( - children: queueStream.value.asMap().entries.map((e) { - final index = e.key; - final song = e.value; - return Dismissible( - key: ValueKey(song.path), - child: AlbumArtListTile( - title: song.title, - subtitle: '${song.artist} • ${song.album}', - albumArtPath: song.albumArtPath, - highlight: index == queueIndexStream.value, - ), - onDismissed: (direction) { - audioStore.removeQueueIndex(index); - Scaffold.of(context).showSnackBar( - SnackBar( - content: Text('${song.title} dismissed'), + switch (queueStream.status) { + case StreamStatus.active: + final int activeIndex = queueIndexStream.value; + return CustomScrollView( + controller: _scrollController, + slivers: [ + ReorderableSliverList( + delegate: ReorderableSliverChildBuilderDelegate( + (context, int index) { + final song = queueStream.value[index]; + return Dismissible( + key: ValueKey(song.path), + child: AlbumArtListTile( + title: song.title, + subtitle: '${song.artist}', + albumArtPath: song.albumArtPath, + highlight: index == activeIndex, + onTap: () => audioStore.setIndex(index), ), + onDismissed: (direction) { + audioStore.removeQueueIndex(index); + Scaffold.of(context).showSnackBar( + SnackBar( + content: Text('${song.title} removed'), + ), + ); + }, ); }, - ); - }).toList(), - onReorder: (oldIndex, newIndex) => - audioStore.moveQueueItem(oldIndex, newIndex), - ); - return ListView.separated( - itemCount: queueStream.value.length, - itemBuilder: (_, int index) { - final Song song = queueStream.value[index]; - return AlbumArtListTile( - title: song.title, - subtitle: '${song.artist} • ${song.album}', - albumArtPath: song.albumArtPath, - highlight: index == queueIndexStream.value, - ); - }, - separatorBuilder: (BuildContext context, int index) => - const Divider( - height: 4.0, - ), - ); - case StreamStatus.waiting: - case StreamStatus.done: - default: - return Container(); - } - }, - ), + childCount: queueStream.value.length, + ), + onReorder: (oldIndex, newIndex) => + audioStore.moveQueueItem(oldIndex, newIndex), + ) + ], + ); + case StreamStatus.waiting: + case StreamStatus.done: + default: + return Container(); + } + }, ), ), ); diff --git a/lib/presentation/state/audio_store.dart b/lib/presentation/state/audio_store.dart index 5f0f548..37b60da 100644 --- a/lib/presentation/state/audio_store.dart +++ b/lib/presentation/state/audio_store.dart @@ -75,31 +75,30 @@ abstract class _AudioStore with Store { @observable ObservableStream loopModeStream; - @action Future playSong(int index, List songList) async { _audioRepository.playSong(index, songList); } - @action Future play() async { _audioRepository.play(); } - @action Future pause() async { _audioRepository.pause(); } - @action Future skipToNext() async { _audioRepository.skipToNext(); } - @action Future skipToPrevious() async { _audioRepository.skipToPrevious(); } + Future setIndex(int index) async { + _audioRepository.setIndex(index); + } + Future setShuffleMode(ShuffleMode shuffleMode) async { _audioRepository.setShuffleMode(shuffleMode); } diff --git a/lib/system/audio/audio_handler.dart b/lib/system/audio/audio_handler.dart index 37de04a..0c87741 100644 --- a/lib/system/audio/audio_handler.dart +++ b/lib/system/audio/audio_handler.dart @@ -49,7 +49,8 @@ class MyAudioHandler extends BaseAudioHandler { _audioPlayer.setShuffleMode(await _playerStateDataSource.shuffleModeStream.first, false); } - if (_playerStateDataSource.queueStream != null && _playerStateDataSource.currentIndexStream != null) { + if (_playerStateDataSource.queueStream != null && + _playerStateDataSource.currentIndexStream != null) { _audioPlayer.loadQueue( queue: await _playerStateDataSource.queueStream.first, initialIndex: await _playerStateDataSource.currentIndexStream.first, @@ -95,6 +96,11 @@ class MyAudioHandler extends BaseAudioHandler { _audioPlayer.addToQueue(SongModel.fromMediaItem(mediaItem)); } + @override + Future removeQueueItemAt(int index) async { + _audioPlayer.removeQueueIndex(index); + } + @override Future customAction(String name, Map arguments) async { switch (name) { @@ -112,8 +118,8 @@ class MyAudioHandler extends BaseAudioHandler { return shuffleAll(); case MOVE_QUEUE_ITEM: return moveQueueItem(arguments['OLD_INDEX'] as int, arguments['NEW_INDEX'] as int); - case REMOVE_QUEUE_ITEM: - return removeQueueIndex(arguments as int); + case SET_INDEX: + return setIndex(arguments['INDEX'] as int); default: } } @@ -155,8 +161,8 @@ class MyAudioHandler extends BaseAudioHandler { _audioPlayer.moveQueueItem(oldIndex, newIndex); } - Future removeQueueIndex(int index) async { - _audioPlayer.removeQueueIndex(index); + Future setIndex(int index) async { + _audioPlayer.setIndex(index); } void _handleSetQueue(List queueItems) { diff --git a/lib/system/audio/audio_manager.dart b/lib/system/audio/audio_manager.dart index deafc10..42f330b 100644 --- a/lib/system/audio/audio_manager.dart +++ b/lib/system/audio/audio_manager.dart @@ -111,6 +111,11 @@ class AudioManagerImpl implements AudioManager { await _audioHandler.skipToPrevious(); } + @override + Future setIndex(int index) async { + await _audioHandler.customAction(SET_INDEX, {'INDEX': index}); + } + @override Future setShuffleMode(ShuffleMode shuffleMode) async { await _audioHandler.customAction(SET_SHUFFLE_MODE, {'SHUFFLE_MODE': shuffleMode}); @@ -178,6 +183,6 @@ class AudioManagerImpl implements AudioManager { @override Future removeQueueIndex(int index) async { - await _audioHandler.customAction(REMOVE_QUEUE_ITEM, {'INDEX': index}); + await _audioHandler.removeQueueItemAt(index); } } diff --git a/lib/system/audio/audio_manager_contract.dart b/lib/system/audio/audio_manager_contract.dart index 40c6c6b..df65101 100644 --- a/lib/system/audio/audio_manager_contract.dart +++ b/lib/system/audio/audio_manager_contract.dart @@ -17,6 +17,7 @@ abstract class AudioManager { Future pause(); Future skipToNext(); Future skipToPrevious(); + Future setIndex(int index); Future setShuffleMode(ShuffleMode shuffleMode); Future setLoopMode(LoopMode loopMode); Future shuffleAll(); diff --git a/lib/system/audio/audio_player_impl.dart b/lib/system/audio/audio_player_impl.dart index 940bc9a..30f3f7d 100644 --- a/lib/system/audio/audio_player_impl.dart +++ b/lib/system/audio/audio_player_impl.dart @@ -98,6 +98,7 @@ class AudioPlayerImpl implements AudioPlayer { if (queue == null || initialIndex >= queue.length) { return; } + _queue = queue; _queueSubject.add(queue); // final smallQueue = queue.sublist(max(initialIndex - 10, 0), min(initialIndex + 140, queue.length)); diff --git a/lib/system/audio/stream_constants.dart b/lib/system/audio/stream_constants.dart index 8f81a43..5abe65d 100644 --- a/lib/system/audio/stream_constants.dart +++ b/lib/system/audio/stream_constants.dart @@ -6,5 +6,5 @@ const String APP_LIFECYCLE_RESUMED = 'APP_LIFECYCLE_RESUMED'; const String SHUFFLE_ALL = 'SHUFFLE_ALL'; const String SET_SHUFFLE_MODE = 'SET_SHUFFLE_MODE'; const String SET_LOOP_MODE = 'SET_LOOP_MODE'; -const String MOVE_QUEUE_ITEM = 'MOVE_QUEUE_ITEM'; -const String REMOVE_QUEUE_ITEM = 'REMOVE_QUEUE_ITEM'; \ No newline at end of file +const String SET_INDEX = 'SET_INDEX'; +const String MOVE_QUEUE_ITEM = 'MOVE_QUEUE_ITEM'; \ No newline at end of file diff --git a/lib/system/repositories/audio_repository_impl.dart b/lib/system/repositories/audio_repository_impl.dart index 72a6c56..653b204 100644 --- a/lib/system/repositories/audio_repository_impl.dart +++ b/lib/system/repositories/audio_repository_impl.dart @@ -103,4 +103,9 @@ class AudioRepositoryImpl implements AudioRepository { Future setLoopMode(LoopMode loopMode) async { await _audioManager.setLoopMode(loopMode); } + + @override + Future setIndex(int index) async { + await _audioManager.setIndex(index); + } } diff --git a/pubspec.lock b/pubspec.lock index 118126b..acfd31f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -578,6 +578,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.1" + reorderables: + dependency: "direct main" + description: + name: reorderables + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.2" rxdart: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 6db94b5..cdf71a6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -32,6 +32,7 @@ dependencies: path: path_provider: ^1.6.18 provider: ^4.0.4 + reorderables: ^0.3.2 sqlite3_flutter_libs: ^0.3.0 dev_dependencies: