some theming and persistence refactoring
This commit is contained in:
parent
26620f374f
commit
320028dafa
16 changed files with 81 additions and 63 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,22 +15,28 @@ 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(
|
||||
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,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}
|
||||
''';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ ThemeData theme() => ThemeData(
|
|||
);
|
||||
|
||||
const TextStyle TEXT_HEADER = TextStyle(
|
||||
fontSize: 18.0,
|
||||
fontSize: 20.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
);
|
||||
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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: [
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue