some theming and persistence refactoring

This commit is contained in:
Moritz Weber 2021-02-06 14:00:04 +01:00
parent 26620f374f
commit 320028dafa
16 changed files with 81 additions and 63 deletions

View file

@ -5,6 +5,7 @@ import '../entities/song.dart';
abstract class PlayerStateRepository {
Stream<List<Song>> get queueStream;
Stream<int> get currentIndexStream;
Stream<Song> get currentSongStream;
Stream<LoopMode> get loopModeStream;
Stream<ShuffleMode> get shuffleModeStream;
}

View file

@ -37,7 +37,7 @@ class CurrentlyPlayingPage extends StatelessWidget {
child: Observer(
builder: (BuildContext context) {
_log.info('Observer.build');
final Song song = audioStore.currentSong;
final Song song = audioStore.currentSongStream.value;
return Stack(
children: [
@ -53,7 +53,7 @@ class CurrentlyPlayingPage extends StatelessWidget {
Theme.of(context).scaffoldBackgroundColor,
Theme.of(context).scaffoldBackgroundColor,
],
stops: [
stops: const [
0.0,
0.2,
0.55,
@ -102,7 +102,7 @@ class CurrentlyPlayingPage extends StatelessWidget {
),
IconButton(
icon: const Icon(Icons.more_vert),
onPressed: () {},
onPressed: () => null,
)
],
mainAxisAlignment: MainAxisAlignment.spaceBetween,

View file

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import '../theming.dart';
import '../widgets/header.dart';
import '../widgets/highlight.dart';
import '../widgets/shuffle_all_button.dart';
@ -14,21 +15,27 @@ class HomePage extends StatefulWidget {
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
print('HomePage.build');
return SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Column(
children: [
const Header(),
const Highlight(),
const ShuffleAllButton(
verticalPad: 20.0,
horizontalPad: 0.0,
child: Column(
children: [
const Padding(
padding: EdgeInsets.only(top: 8.0, left: HORIZONTAL_PADDING),
child: Header(),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: HORIZONTAL_PADDING),
child: Column(
children: const [
Highlight(),
ShuffleAllButton(
verticalPad: 20.0,
horizontalPad: 0.0,
),
],
),
],
),
),
],
),
);
}

View file

@ -5,6 +5,7 @@ import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:provider/provider.dart';
import '../state/music_data_store.dart';
import '../theming.dart';
class SettingsPage extends StatelessWidget {
const SettingsPage({Key key}) : super(key: key);
@ -25,10 +26,7 @@ class SettingsPage extends StatelessWidget {
),
child: Text(
'Library',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0,
),
style: TEXT_HEADER,
),
),
ListTile(

View file

@ -27,6 +27,8 @@ abstract class _AudioStore with Store {
queueIndexStream = _persistentPlayerStateRepository.currentIndexStream.asObservable();
currentSongStream = _persistentPlayerStateRepository.currentSongStream.asObservable();
shuffleModeStream = _persistentPlayerStateRepository.shuffleModeStream.asObservable();
loopModeStream = _persistentPlayerStateRepository.loopModeStream.asObservable();
@ -37,17 +39,8 @@ abstract class _AudioStore with Store {
final AudioRepository _audioRepository;
final PlayerStateRepository _persistentPlayerStateRepository;
@computed
Song get currentSong {
if (queueStream.value != null && queueIndexStream.value != null) {
if (queueIndexStream.value < queueStream.value.length) {
final song = queueStream.value[queueIndexStream.value];
return song;
}
}
return null;
}
@observable
ObservableStream<Song> currentSongStream;
@observable
ObservableStream<PlaybackState> playbackStateStream;

View file

@ -9,13 +9,20 @@ part of 'audio_store.dart';
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
mixin _$AudioStore on _AudioStore, Store {
Computed<Song> _$currentSongComputed;
final _$currentSongStreamAtom = Atom(name: '_AudioStore.currentSongStream');
@override
Song get currentSong =>
(_$currentSongComputed ??= Computed<Song>(() => super.currentSong,
name: '_AudioStore.currentSong'))
.value;
ObservableStream<Song> get currentSongStream {
_$currentSongStreamAtom.reportRead();
return super.currentSongStream;
}
@override
set currentSongStream(ObservableStream<Song> value) {
_$currentSongStreamAtom.reportWrite(value, super.currentSongStream, () {
super.currentSongStream = value;
});
}
final _$playbackStateStreamAtom =
Atom(name: '_AudioStore.playbackStateStream');
@ -113,13 +120,13 @@ mixin _$AudioStore on _AudioStore, Store {
@override
String toString() {
return '''
currentSongStream: ${currentSongStream},
playbackStateStream: ${playbackStateStream},
currentPositionStream: ${currentPositionStream},
queueStream: ${queueStream},
queueIndexStream: ${queueIndexStream},
shuffleModeStream: ${shuffleModeStream},
loopModeStream: ${loopModeStream},
currentSong: ${currentSong}
loopModeStream: ${loopModeStream}
''';
}
}

View file

@ -62,7 +62,7 @@ ThemeData theme() => ThemeData(
);
const TextStyle TEXT_HEADER = TextStyle(
fontSize: 18.0,
fontSize: 20.0,
fontWeight: FontWeight.bold,
);

View file

@ -18,9 +18,8 @@ class CurrentlyPlayingBar extends StatelessWidget {
return Observer(
builder: (BuildContext context) {
if (audioStore.currentSong != null) {
final Song song = audioStore.currentSong;
final Song song = audioStore.currentSongStream.value;
if (song != null) {
return Column(
verticalDirection: VerticalDirection.up,
children: <Widget>[
@ -66,7 +65,7 @@ class CurrentlyPlayingBar extends StatelessWidget {
),
Container(
child: LinearProgressIndicator(
value: audioStore.currentPositionStream.value / audioStore.currentSong.duration,
value: audioStore.currentPositionStream.value / song.duration,
valueColor: const AlwaysStoppedAnimation<Color>(Colors.white),
backgroundColor: Colors.white10,
),

View file

@ -8,11 +8,11 @@ class Header extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: const <Widget>[
Text('Home', style: TEXT_HEADER),
children: <Widget>[
const Text('Home', style: TEXT_HEADER),
IconButton(
icon: Icon(Icons.more_vert),
onPressed: null,
icon: const Icon(Icons.more_vert),
onPressed: () => null,
),
],
mainAxisAlignment: MainAxisAlignment.spaceBetween,

View file

@ -19,7 +19,7 @@ class LikeButton extends StatelessWidget {
return Observer(
builder: (BuildContext context) {
final Song song = audioStore.currentSong;
final Song song = audioStore.currentSongStream.value;
if (song.likeCount == 0) {
return IconButton(

View file

@ -18,8 +18,7 @@ class SongCustomizationButtons extends StatelessWidget {
return Observer(
builder: (BuildContext context) {
print('building buttons');
final Song song = audioStore.currentSong;
final bool isBlocked = audioStore.currentSong.blocked;
final Song song = audioStore.currentSongStream.value;
return Row(
children: [
IconButton(
@ -37,9 +36,9 @@ class SongCustomizationButtons extends StatelessWidget {
icon: Icon(
Icons.remove_circle_outline_rounded,
size: 20.0,
color: isBlocked ? Colors.white : Colors.white24,
color: song.blocked ? Colors.white : Colors.white24,
),
onPressed: () => musicDataStore.setSongBlocked(song, !isBlocked),
onPressed: () => musicDataStore.setSongBlocked(song, !song.blocked),
),
],
mainAxisAlignment: MainAxisAlignment.center,

View file

@ -16,7 +16,7 @@ class TimeProgressIndicator extends StatelessWidget {
return Observer(
builder: (BuildContext context) {
final int duration = audioStore.currentSong?.duration ?? 1000;
final int duration = audioStore.currentSongStream.value?.duration ?? 1000;
return Row(
children: [

View file

@ -62,6 +62,16 @@ class PlayerStateDao extends DatabaseAccessor<MoorDatabase>
});
}
@override
Stream<SongModel> get currentSongStream {
final query = select(persistentIndex).join([
innerJoin(queueEntries, queueEntries.index.equalsExp(persistentIndex.index)),
innerJoin(songs, songs.path.equalsExp(queueEntries.path))
]);
return query.watchSingle().map((row) => SongModel.fromMoor(row.readTable(songs)));
}
@override
Stream<int> get currentIndexStream {
return select(persistentIndex).watchSingle().map((event) => event?.index);

View file

@ -8,6 +8,8 @@ abstract class PlayerStateDataSource {
Stream<List<SongModel>> get songQueueStream;
Stream<List<QueueItemModel>> get queueStream;
Stream<SongModel> get currentSongStream;
Future<void> setCurrentIndex(int index);
Stream<int> get currentIndexStream;

View file

@ -37,27 +37,31 @@ class MusicDataRepositoryImpl implements MusicDataRepository {
Stream<List<Song>> getAlbumSongStream(Album album) =>
_musicDataSource.getAlbumSongStream(album as AlbumModel);
@override
Stream<List<Album>> getArtistAlbumStream(Artist artist) =>
_musicDataSource.getArtistAlbumStream(artist as ArtistModel);
@override
Future<void> updateDatabase() async {
_log.info('updateDatabase called');
final localMusic = await _localMusicFetcher.getLocalMusic();
await updateArtists(localMusic['ARTISTS'] as List<ArtistModel>);
final albumIdMap = await updateAlbums(localMusic['ALBUMS'] as List<AlbumModel>);
await updateSongs(localMusic['SONGS'] as List<SongModel>, albumIdMap);
await _updateArtists(localMusic['ARTISTS'] as List<ArtistModel>);
final albumIdMap = await _updateAlbums(localMusic['ALBUMS'] as List<AlbumModel>);
await _updateSongs(localMusic['SONGS'] as List<SongModel>, albumIdMap);
_log.info('updateDatabase finished');
}
Future<void> updateArtists(List<ArtistModel> artists) async {
Future<void> _updateArtists(List<ArtistModel> artists) async {
await _musicDataSource.deleteAllArtists();
for (final ArtistModel artist in artists) {
await _musicDataSource.insertArtist(artist);
}
}
Future<Map<int, int>> updateAlbums(List<AlbumModel> albums) async {
Future<Map<int, int>> _updateAlbums(List<AlbumModel> albums) async {
await _musicDataSource.deleteAllAlbums();
final Map<int, int> albumIdMap = {};
@ -85,7 +89,7 @@ class MusicDataRepositoryImpl implements MusicDataRepository {
return albumIdMap;
}
Future<void> updateSongs(List<SongModel> songs, Map<int, int> albumIdMap) async {
Future<void> _updateSongs(List<SongModel> songs, Map<int, int> albumIdMap) async {
final Directory dir = await getApplicationSupportDirectory();
final List<SongModel> songsToInsert = [];
@ -101,9 +105,4 @@ class MusicDataRepositoryImpl implements MusicDataRepository {
}
await _musicDataSource.insertSongs(songsToInsert);
}
@override
Stream<List<Album>> getArtistAlbumStream(Artist artist) {
return _musicDataSource.getArtistAlbumStream(artist as ArtistModel);
}
}

View file

@ -12,6 +12,9 @@ class PlayerStateRepositoryImpl implements PlayerStateRepository {
@override
Stream<int> get currentIndexStream => _playerStateDataSource.currentIndexStream;
@override
Stream<Song> get currentSongStream => _playerStateDataSource.currentSongStream;
@override
Stream<LoopMode> get loopModeStream => _playerStateDataSource.loopModeStream;