artist page, currently playing cosmetics, new playback functions

This commit is contained in:
Moritz Weber 2021-01-21 21:49:29 +01:00
parent 4c325396bc
commit 90a77385b5
23 changed files with 466 additions and 244 deletions

View file

@ -1,3 +1,5 @@
import '../entities/album.dart';
import '../entities/artist.dart';
import '../entities/loop_mode.dart';
import '../entities/playback_state.dart';
import '../entities/shuffle_mode.dart';
@ -15,6 +17,9 @@ abstract class AudioRepository {
Future<void> skipToPrevious();
Future<void> setIndex(int index);
Future<void> playAlbum(Album album);
Future<void> playArtist(Artist artist, {ShuffleMode shuffleMode = ShuffleMode.none});
Future<void> setShuffleMode(ShuffleMode shuffleMode);
Future<void> setLoopMode(LoopMode loopMode);

View file

@ -4,8 +4,11 @@ import 'package:provider/provider.dart';
import '../../domain/entities/album.dart';
import '../../domain/entities/artist.dart';
import '../state/audio_store.dart';
import '../state/music_data_store.dart';
import '../theming.dart';
import '../widgets/artist_albums.dart';
import '../widgets/artist_header.dart';
import 'album_details_page.dart';
class ArtistDetailsPage extends StatelessWidget {
@ -16,17 +19,57 @@ class ArtistDetailsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final MusicDataStore musicDataStore = Provider.of<MusicDataStore>(context);
final AudioStore audioStore = Provider.of<AudioStore>(context);
return Observer(
builder: (BuildContext context) => Scaffold(
appBar: AppBar(
title: Text(artist.name),
),
body: SafeArea(
child: ArtistAlbumList(
albums: musicDataStore.sortedArtistAlbums,
onTap: (Album album) => _tapAlbum(album, context, musicDataStore),
),
builder: (BuildContext context) => SafeArea(
child: CustomScrollView(
slivers: [
ArtistHeader(artist: artist),
SliverList(
delegate: SliverChildListDelegate(
[
Padding(
padding: const EdgeInsets.only(
left: HORIZONTAL_PADDING,
right: HORIZONTAL_PADDING,
bottom: 8.0,
),
child: ElevatedButton(
child: const Text('SHUFFLE'),
onPressed: () => audioStore.shuffleArtist(artist),
),
),
const Padding(
padding: EdgeInsets.only(
left: HORIZONTAL_PADDING + 2,
right: HORIZONTAL_PADDING + 2,
bottom: 4.0,
),
child: Text(
'Albums',
style: TEXT_HEADER,
),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: HORIZONTAL_PADDING,
vertical: 4.0,
),
child: Container(
height: 1.0,
color: Colors.white10,
),
),
],
),
),
ArtistAlbumSliverList(
albums: musicDataStore.sortedArtistAlbums,
onTap: (Album album) => _tapAlbum(album, context, musicDataStore),
onTapPlay: (Album album) => audioStore.playAlbum(album),
),
],
),
),
);

View file

@ -39,115 +39,119 @@ class CurrentlyPlayingPage extends StatelessWidget {
_log.info('Observer.build');
final Song song = audioStore.currentSong;
return AlbumBackground(
song: song,
gradient: const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0x55000000),
Color(0x22FFFFFF),
Color(0x22FFFFFF),
Color(0x88000000),
Color(0xBB000000),
],
stops: [
0.0,
0.1,
0.5,
0.65,
1.0,
],
),
child: Padding(
padding: const EdgeInsets.only(
left: 12.0,
right: 12.0,
top: 8.0,
return Stack(
children: [
AlbumBackground(
song: song,
gradient: const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0x55000000),
Color(0x22FFFFFF),
Color(0x22FFFFFF),
Color(0x88000000),
Color(0xBB000000),
],
stops: [
0.0,
0.1,
0.55,
0.75,
1.0,
],
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
IconButton(
icon: const Icon(Icons.expand_more),
onPressed: () {
Navigator.pop(context);
},
),
Expanded(
child: GestureDetector(
onTap: () => _openQueue(context),
child: Container(
color: Colors.transparent,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'Next up'.toUpperCase(),
style: TEXT_SMALL_HEADLINE,
),
NextSong(
queue: audioStore.queueStream.value,
index: audioStore.queueIndexStream.value,
)
],
Padding(
padding: const EdgeInsets.only(
left: 12.0,
right: 12.0,
top: 8.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
IconButton(
icon: const Icon(Icons.expand_more),
onPressed: () {
Navigator.pop(context);
},
),
Expanded(
child: GestureDetector(
onTap: () => _openQueue(context),
child: Container(
color: Colors.transparent,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'Next up'.toUpperCase(),
style: TEXT_SMALL_HEADLINE,
),
NextSong(
queue: audioStore.queueStream.value,
index: audioStore.queueIndexStream.value,
)
],
),
),
),
),
),
IconButton(
icon: const Icon(Icons.more_vert),
onPressed: () {},
)
],
mainAxisAlignment: MainAxisAlignment.spaceBetween,
),
const Spacer(
flex: 1,
),
Expanded(
flex: 1000,
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 0.0,
),
child: AlbumArt(
song: song,
IconButton(
icon: const Icon(Icons.more_vert),
onPressed: () {},
)
],
mainAxisAlignment: MainAxisAlignment.spaceBetween,
),
const Spacer(
flex: 1,
),
Expanded(
flex: 1000,
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 0.0,
),
child: AlbumArt(
song: song,
),
),
),
),
),
const Spacer(
flex: 60,
),
const Padding(
padding: EdgeInsets.only(left: 2.0, right: 2.0),
child: SongCustomizationButtons(),
),
const Spacer(
flex: 30,
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 2.0),
child: PlaybackControl(),
),
const Spacer(
flex: 30,
),
const Padding(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 10.0),
child: TimeProgressIndicator(),
),
const Spacer(
flex: 100,
),
],
const Spacer(
flex: 60,
),
const Padding(
padding: EdgeInsets.only(left: 2.0, right: 2.0),
child: SongCustomizationButtons(),
),
const Spacer(
flex: 30,
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 2.0),
child: PlaybackControl(),
),
const Spacer(
flex: 30,
),
const Padding(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 10.0),
child: TimeProgressIndicator(),
),
const Spacer(
flex: 100,
),
],
),
),
),
],
);
},
),

View file

@ -14,6 +14,7 @@ class HomePage extends StatefulWidget {
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
print('HomePage.build');
return SafeArea(
child: Padding(

View file

@ -34,53 +34,57 @@ class QueuePage extends StatelessWidget {
switch (queueStream.status) {
case StreamStatus.active:
final int activeIndex = queueIndexStream.value;
return AlbumBackground(
song: audioStore.currentSong,
gradient: const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0x55000000),
Color(0x55000000),
],
stops: [
0.0,
1.0,
],
),
child: 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'),
),
);
},
);
},
childCount: queueStream.value.length,
),
onReorder: (oldIndex, newIndex) =>
audioStore.moveQueueItem(oldIndex, newIndex),
)
],
),
return Stack(
children: [
AlbumBackground(
song: audioStore.currentSong,
gradient: const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0x55000000),
Color(0x55000000),
],
stops: [
0.0,
1.0,
],
),
),
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'),
),
);
},
);
},
childCount: queueStream.value.length,
),
onReorder: (oldIndex, newIndex) =>
audioStore.moveQueueItem(oldIndex, newIndex),
)
],
),
],
);
case StreamStatus.waiting:
case StreamStatus.done:

View file

@ -1,6 +1,8 @@
import 'package:meta/meta.dart';
import 'package:mobx/mobx.dart';
import '../../domain/entities/album.dart';
import '../../domain/entities/artist.dart';
import '../../domain/entities/loop_mode.dart';
import '../../domain/entities/playback_state.dart';
import '../../domain/entities/shuffle_mode.dart';
@ -112,4 +114,12 @@ abstract class _AudioStore with Store {
Future<void> removeQueueIndex(int index) async {
_audioRepository.removeQueueIndex(index);
}
Future<void> playAlbum(Album album) async {
_audioRepository.playAlbum(album);
}
Future<void> shuffleArtist(Artist artist) async {
_audioRepository.playArtist(artist, shuffleMode: ShuffleMode.plus);
}
}

View file

@ -49,7 +49,13 @@ abstract class _MusicDataStore with Store {
bool isUpdatingDatabase = false;
@computed
List<Album> get sortedArtistAlbums => artistAlbumStream.value.toList()..sort((a, b) => -a.pubYear.compareTo(b.pubYear));
List<Album> get sortedArtistAlbums => artistAlbumStream.value.toList()..sort((a, b) {
if (b.pubYear == null)
return -1;
if (a.pubYear == null)
return 1;
return -a.pubYear.compareTo(b.pubYear);
});
@action
Future<void> updateDatabase() async {

View file

@ -82,4 +82,6 @@ const TextStyle TEXT_SMALL_HEADLINE = TextStyle(
const TextStyle TEXT_SMALL_SUBTITLE = TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.w300,
);
);
const double HORIZONTAL_PADDING = 16.0;

View file

@ -14,25 +14,19 @@ class AlbumBackground extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
image: DecorationImage(
image: getAlbumImage(song.albumArtPath),
fit: BoxFit.cover,
),
),
child: Container(
height: double.infinity,
decoration: BoxDecoration(
gradient: gradient,
),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 96.0, sigmaY: 96.0),
child: Container(
child: child,
color: Colors.white.withOpacity(0.0),
return ImageFiltered(
imageFilter: ImageFilter.blur(sigmaX: 96.0, sigmaY: 96.0),
child: ShaderMask(
shaderCallback: (Rect bounds) => gradient.createShader(bounds),
blendMode: BlendMode.srcATop,
child: Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
image: DecorationImage(
image: getAlbumImage(song.albumArtPath),
fit: BoxFit.cover,
),
),
),
),

View file

@ -5,7 +5,7 @@ import '../utils.dart' as utils;
class AlbumListTileExtended extends StatelessWidget {
const AlbumListTileExtended(
{Key key, this.title, this.subtitle, this.albumArtPath, this.onTap, this.highlight = false})
{Key key, this.title, this.subtitle, this.albumArtPath, this.onTap, this.highlight = false, this.onTapPlay})
: super(key: key);
final String title;
@ -13,52 +13,56 @@ class AlbumListTileExtended extends StatelessWidget {
final String albumArtPath;
final Function onTap;
final bool highlight;
final Function onTapPlay;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => onTap(),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Row(
children: [
SizedBox(
height: 72,
width: 72,
child: Container(
decoration: BoxDecoration(borderRadius: BorderRadius.circular(4.0)),
clipBehavior: Clip.antiAlias,
child: Image(
image: utils.getAlbumImage(albumArtPath),
fit: BoxFit.cover,
child: Container(
color: Colors.transparent,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Row(
children: [
SizedBox(
height: 72,
width: 72,
child: Container(
decoration: BoxDecoration(borderRadius: BorderRadius.circular(4.0)),
clipBehavior: Clip.antiAlias,
child: Image(
image: utils.getAlbumImage(albumArtPath),
fit: BoxFit.cover,
),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0),
maxLines: 2,
),
Text(
subtitle,
style: TEXT_SMALL_SUBTITLE,
),
],
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0),
maxLines: 2,
),
Text(
subtitle,
style: TEXT_SMALL_SUBTITLE,
),
],
),
),
),
),
IconButton(
icon: Icon(Icons.play_circle_fill_rounded),
iconSize: 40.0,
onPressed: () => null,
),
],
IconButton(
icon: Icon(Icons.play_circle_fill_rounded),
iconSize: 40.0,
onPressed: () => onTapPlay(),
),
],
),
),
),
);

View file

@ -3,25 +3,29 @@ import 'package:flutter/material.dart';
import '../../domain/entities/album.dart';
import 'album_list_tile_extended.dart';
class ArtistAlbumList extends StatelessWidget {
const ArtistAlbumList({Key key, this.albums, this.onTap}) : super(key: key);
class ArtistAlbumSliverList extends StatelessWidget {
const ArtistAlbumSliverList({Key key, this.albums, this.onTap, this.onTapPlay}) : super(key: key);
final List<Album> albums;
final Function onTap;
final Function onTapPlay;
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: albums.length,
itemBuilder: (_, int index) {
final Album album = albums[index];
return AlbumListTileExtended(
title: album.title,
subtitle: album.pubYear.toString(),
albumArtPath: album.albumArtPath,
onTap: () => onTap(album),
);
},
return SliverList(
delegate: SliverChildBuilderDelegate(
(_, int index) {
final Album album = albums[index];
return AlbumListTileExtended(
title: album.title,
subtitle: album.pubYear.toString(),
albumArtPath: album.albumArtPath,
onTap: () => onTap(album),
onTapPlay: () => onTapPlay(album),
);
},
childCount: albums.length,
),
);
}
}

View file

@ -0,0 +1,72 @@
import 'package:flutter/material.dart';
import '../../domain/entities/artist.dart';
class ArtistHeader extends StatelessWidget {
const ArtistHeader({Key key, this.artist}) : super(key: key);
final Artist artist;
@override
Widget build(BuildContext context) {
const double height = 144.0;
return SliverAppBar(
brightness: Brightness.dark,
pinned: true,
expandedHeight: height,
backgroundColor: Theme.of(context).primaryColor,
iconTheme: const IconThemeData(
color: Colors.white,
),
leading: IconButton(
icon: const Icon(Icons.chevron_left),
onPressed: () => Navigator.pop(context),
),
actions: [
IconButton(
icon: const Icon(Icons.more_vert),
onPressed: () => Navigator.pop(context),
),
],
titleSpacing: 48.0,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
titlePadding: const EdgeInsets.only(
bottom: 0.0,
top: 0.0,
left: 48.0,
right: 48.0,
),
title: Container(
alignment: Alignment.center,
height: height * 0.66,
// color: Colors.red,
child: Text(
artist.name,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24.0,
color: Colors.white,
),
textAlign: TextAlign.center,
maxLines: 3,
),
),
background: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
// Colors.deepPurpleAccent,
Colors.transparent,
Theme.of(context).scaffoldBackgroundColor,
// Colors.green,
],
),
),
),
),
);
}
}

View file

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:mucke/presentation/theming.dart';
import '../theming.dart';
class Header extends StatelessWidget {
const Header({Key key}) : super(key: key);

View file

@ -20,14 +20,12 @@ class ShuffleAllButton extends StatelessWidget {
vertical: verticalPad,
horizontal: horizontalPad,
),
child: RaisedButton.icon(
child: ElevatedButton.icon(
icon: const Icon(Icons.shuffle),
label: const Text('SHUFFLE ALL'),
onPressed: () => audioStore.shuffleAll(),
color: Theme.of(context).accentColor,
highlightColor: Theme.of(context).highlightColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24.0),
style: ElevatedButton.styleFrom(
primary: Theme.of(context).accentColor,
),
),
),

View file

@ -8,6 +8,8 @@ import '../../domain/entities/playback_event.dart';
import '../../domain/entities/shuffle_mode.dart';
import '../datasources/music_data_source_contract.dart';
import '../datasources/player_state_data_source.dart';
import '../models/album_model.dart';
import '../models/artist_model.dart';
import '../models/playback_event_model.dart';
import '../models/queue_item_model.dart';
import '../models/song_model.dart';
@ -123,6 +125,13 @@ class MyAudioHandler extends BaseAudioHandler {
return moveQueueItem(arguments['OLD_INDEX'] as int, arguments['NEW_INDEX'] as int);
case SET_INDEX:
return setIndex(arguments['INDEX'] as int);
case PLAY_ALBUM:
return playAlbum(arguments['ALBUM'] as AlbumModel);
case PLAY_ARTIST:
return playArtist(
arguments['ARTIST'] as ArtistModel,
arguments['SHUFFLE_MODE'] as ShuffleMode,
);
default:
}
}
@ -163,6 +172,23 @@ class MyAudioHandler extends BaseAudioHandler {
_audioPlayer.setIndex(index);
}
Future<void> playAlbum(AlbumModel album) async {
_audioPlayer.setShuffleMode(ShuffleMode.none, false);
final List<SongModel> songs = await _musicDataSource.getAlbumSongStream(album).first;
_audioPlayer.playSongList(songs, 0);
}
Future<void> playArtist(ArtistModel artist, ShuffleMode shuffleMode) async {
_audioPlayer.setShuffleMode(shuffleMode, false);
final List<SongModel> songs = await _musicDataSource.getArtistSongStream(artist).first;
final rng = Random();
final index = rng.nextInt(songs.length);
_audioPlayer.playSongList(songs, index);
}
void _handleSetQueue(List<QueueItemModel> queueItems) {
_playerStateDataSource.setQueue(queueItems);
@ -195,8 +221,7 @@ class MyAudioHandler extends BaseAudioHandler {
}
void _handlePosition(Duration position, SongModel song) {
if (song == null || position == null)
return;
if (song == null || position == null) return;
final int pos = position.inMilliseconds;

View file

@ -5,6 +5,8 @@ import 'package:audio_service/audio_service.dart';
import '../../domain/entities/loop_mode.dart';
import '../../domain/entities/playback_state.dart' as entity;
import '../../domain/entities/shuffle_mode.dart';
import '../models/album_model.dart';
import '../models/artist_model.dart';
import '../models/playback_state_model.dart';
import '../models/song_model.dart';
import 'audio_manager_contract.dart';
@ -134,4 +136,17 @@ class AudioManagerImpl implements AudioManager {
Future<void> removeQueueIndex(int index) async {
await _audioHandler.removeQueueItemAt(index);
}
@override
Future<void> playAlbum(AlbumModel albumModel) async {
await _audioHandler.customAction(PLAY_ALBUM, {'ALBUM': albumModel});
}
@override
Future<void> playArtist(ArtistModel artistModel, ShuffleMode shuffleMode) async {
await _audioHandler.customAction(PLAY_ARTIST, {
'ARTIST': artistModel,
'SHUFFLE_MODE': shuffleMode,
});
}
}

View file

@ -1,6 +1,8 @@
import '../../domain/entities/loop_mode.dart';
import '../../domain/entities/playback_state.dart';
import '../../domain/entities/shuffle_mode.dart';
import '../models/album_model.dart';
import '../models/artist_model.dart';
import '../models/song_model.dart';
abstract class AudioManager {
@ -22,4 +24,7 @@ abstract class AudioManager {
Future<void> addToQueue(SongModel songModel);
Future<void> moveQueueItem(int oldIndex, int newIndex);
Future<void> removeQueueIndex(int index);
Future<void> playAlbum(AlbumModel albumModel);
Future<void> playArtist(ArtistModel artistModel, ShuffleMode shuffleMode);
}

View file

@ -3,4 +3,6 @@ const String SHUFFLE_ALL = 'SHUFFLE_ALL';
const String SET_SHUFFLE_MODE = 'SET_SHUFFLE_MODE';
const String SET_LOOP_MODE = 'SET_LOOP_MODE';
const String SET_INDEX = 'SET_INDEX';
const String MOVE_QUEUE_ITEM = 'MOVE_QUEUE_ITEM';
const String MOVE_QUEUE_ITEM = 'MOVE_QUEUE_ITEM';
const String PLAY_ALBUM = 'PLAY_ALBUM';
const String PLAY_ARTIST = 'PLAY_ARTIST';

View file

@ -55,7 +55,7 @@ class LocalMusicFetcherImpl implements LocalMusicFetcher {
return _flutterAudioQuery.getArtwork(
type: ResourceType.ALBUM,
id: id.toString(),
size: const Size(500.0, 500.0),
size: const Size(480.0, 480.0),
);
}
return Uint8List(0);
@ -71,6 +71,8 @@ class LocalMusicFetcherImpl implements LocalMusicFetcher {
final artistNames = Set<String>.from(albums.map((album) => album.artist));
final artists = await _getFilteredArtists(artistNames);
assert(false);
return {
'SONGS': songs,
'ALBUMS': albums,

View file

@ -71,8 +71,20 @@ class MusicDataDao extends DatabaseAccessor<MoorDatabase>
(t) => OrderingTerm(expression: t.title),
]))
.watch()
.map((moorAlbumList) =>
moorAlbumList.map((moorAlbum) => AlbumModel.fromMoor(moorAlbum)).toList());
.map((moorAlbumList) {
return moorAlbumList.map((moorAlbum) => AlbumModel.fromMoor(moorAlbum)).toList();
});
}
@override
Stream<List<SongModel>> getArtistSongStream(ArtistModel artist) {
return (select(albums)..where((tbl) => tbl.artist.equals(artist.name)))
.join([innerJoin(songs, songs.albumId.equalsExp(albums.id))])
.map((row) => row.readTable(songs))
.watch()
.map(
(moorSongList) => moorSongList.map((moorSong) => SongModel.fromMoor(moorSong)).toList(),
);
}
@override

View file

@ -5,6 +5,7 @@ import '../models/song_model.dart';
abstract class MusicDataSource {
Stream<List<SongModel>> get songStream;
Stream<List<SongModel>> getAlbumSongStream(AlbumModel album);
Stream<List<SongModel>> getArtistSongStream(ArtistModel artist);
Future<List<SongModel>> getSongs();
Future<SongModel> getSongByPath(String path);

View file

@ -1,9 +1,13 @@
import '../../domain/entities/album.dart';
import '../../domain/entities/artist.dart';
import '../../domain/entities/loop_mode.dart';
import '../../domain/entities/playback_state.dart';
import '../../domain/entities/shuffle_mode.dart';
import '../../domain/entities/song.dart';
import '../../domain/repositories/audio_repository.dart';
import '../audio/audio_manager_contract.dart';
import '../models/album_model.dart';
import '../models/artist_model.dart';
import '../models/song_model.dart';
class AudioRepositoryImpl implements AudioRepository {
@ -83,4 +87,14 @@ class AudioRepositoryImpl implements AudioRepository {
Future<void> setIndex(int index) async {
await _audioManager.setIndex(index);
}
@override
Future<void> playAlbum(Album album) async {
await _audioManager.playAlbum(album as AlbumModel);
}
@override
Future<void> playArtist(Artist artist, {ShuffleMode shuffleMode = ShuffleMode.none}) async {
await _audioManager.playArtist(artist as ArtistModel, shuffleMode);
}
}

View file

@ -22,7 +22,7 @@ import 'package:mucke/presentation/widgets/next_indicator.dart';
import 'package:mucke/presentation/widgets/previous_button.dart';
import 'package:mucke/presentation/widgets/play_pause_button.dart';
import 'package:mucke/presentation/widgets/album_art.dart';
import 'package:mucke/presentation/widgets/shuffle_all_button.dart';
import 'package:mucke/presentation/widgets/primary_button.dart';
import 'package:mucke/presentation/widgets/shuffle_button.dart';
import 'package:mucke/presentation/widgets/injection_widget.dart';
import 'package:mucke/presentation/widgets/next_song.dart';
@ -45,8 +45,6 @@ import 'package:mucke/presentation/pages/albums_page.dart';
import 'package:mucke/presentation/pages/album_details_page.dart';
import 'package:mucke/presentation/pages/currently_playing.dart';
import 'package:mucke/presentation/pages/artists_page.dart';
import 'package:mucke/core/usecase.dart';
import 'package:mucke/core/error/failures.dart';
import 'package:mucke/domain/repositories/audio_repository.dart';
import 'package:mucke/domain/repositories/music_data_repository.dart';
import 'package:mucke/domain/entities/shuffle_mode.dart';