Show the list of musics

This commit is contained in:
Pierre HUBERT 2022-03-24 14:43:28 +01:00
parent 1ff98b7bbf
commit 702ce78a70
2 changed files with 169 additions and 97 deletions

View File

@ -1,7 +1,7 @@
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:music_web_player/config.dart'; import 'package:music_web_player/config.dart';
class MusicEntry { class MusicEntry implements Comparable<MusicEntry> {
final int id; final int id;
final String artist; final String artist;
final String title; final String title;
@ -13,6 +13,16 @@ class MusicEntry {
}); });
String get coverCacheKey => "cover-$id"; String get coverCacheKey => "cover-$id";
String get fullName => "$artist - $title";
@override
String toString() => "MusicEntry($id, $artist, $title)";
@override
int compareTo(MusicEntry other) {
return fullName.compareTo(other.fullName);
}
} }
typedef MusicsList = List<MusicEntry>; typedef MusicsList = List<MusicEntry>;
@ -27,11 +37,15 @@ class API {
throw Exception("Request failed with status ${response.statusCode} !"); throw Exception("Request failed with status ${response.statusCode} !");
} }
return response.data final MusicsList list = response.data
.map((r) => .map((r) =>
MusicEntry(id: r["id"], artist: r["artist"], title: r["title"])) MusicEntry(id: r["id"], artist: r["artist"], title: r["title"]))
.toList() .toList()
.cast<MusicEntry>(); .cast<MusicEntry>();
list.sort();
return list;
} }
} }

View File

@ -17,6 +17,8 @@ extension DurationExt on Duration {
} }
} }
const double playlistWidth = 300;
class MusicPlayer extends StatefulWidget { class MusicPlayer extends StatefulWidget {
final MusicsList musicsList; final MusicsList musicsList;
@ -38,19 +40,21 @@ class _MusicPlayerState extends State<MusicPlayer> {
Duration? get _position => _videoPlayerController?.value.position; Duration? get _position => _videoPlayerController?.value.position;
final List<MusicEntry> stack = []; final List<MusicEntry> _stack = [];
int currMusicPos = 0; int currMusicPos = 0;
var _showPlaylist = true;
MusicEntry get currMusic { MusicEntry get currMusic {
if (currMusicPos < 0) currMusicPos = 0; if (currMusicPos < 0) currMusicPos = 0;
// Automatically choose next music if required // Automatically choose next music if required
if (currMusicPos >= stack.length) { if (currMusicPos >= _stack.length) {
var nextId = rng.nextInt(widget.musicsList.length); var nextId = rng.nextInt(widget.musicsList.length);
stack.add(widget.musicsList[nextId]); _stack.add(widget.musicsList[nextId]);
} }
return stack[currMusicPos]; return _stack[currMusicPos];
} }
Future<void> _play() async { Future<void> _play() async {
@ -101,6 +105,12 @@ class _MusicPlayerState extends State<MusicPlayer> {
await _play(); await _play();
} }
void _playMusic(MusicEntry music) async {
_stack.insert(currMusicPos + 1, music);
_playNext();
print(_stack);
}
void _updatePosition(Duration d) => _videoPlayerController?.seekTo(d); void _updatePosition(Duration d) => _videoPlayerController?.seekTo(d);
@override @override
@ -112,16 +122,25 @@ class _MusicPlayerState extends State<MusicPlayer> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return LayoutBuilder( return LayoutBuilder(
builder: (context, constraints) => Stack( builder: (context, constraints) {
final mainAreaWidth =
constraints.maxWidth - (_showPlaylist ? playlistWidth : 0);
return fluent.Row(
children: [
SizedBox(
width: mainAreaWidth,
child: Stack(
children: [ children: [
// Background image // Background image
CoverImage( CoverImage(
music: currMusic, music: currMusic,
width: constraints.maxWidth, width: mainAreaWidth,
height: constraints.maxHeight, height: constraints.maxHeight,
fit: BoxFit.cover, fit: BoxFit.cover,
), ),
// Blur background image
ClipRRect( ClipRRect(
// Clip it cleanly. // Clip it cleanly.
child: BackdropFilter( child: BackdropFilter(
@ -130,20 +149,38 @@ class _MusicPlayerState extends State<MusicPlayer> {
color: Colors.black.withOpacity(0.8), color: Colors.black.withOpacity(0.8),
alignment: Alignment.center, alignment: Alignment.center,
child: SizedBox( child: SizedBox(
width: constraints.maxWidth, width: mainAreaWidth,
height: constraints.maxHeight, height: constraints.maxHeight,
), ),
), ),
)), ),
),
_buildCenter(), fluent.SizedBox(
width: mainAreaWidth,
child: _buildPlayerWidget(),
),
Positioned(
top: 10, right: 10, child: _buildToggleListButton()),
], ],
), ),
),
// Playlist
_showPlaylist
? SizedBox(
width: playlistWidth,
child: _buildPlaylistPane(),
)
: Container(),
],
);
},
); );
} }
Widget _buildCenter() { Widget _buildPlayerWidget() => fluent.Center(
return fluent.Center(
child: SizedBox( child: SizedBox(
width: 250, width: 250,
child: Column( child: Column(
@ -161,7 +198,8 @@ class _MusicPlayerState extends State<MusicPlayer> {
music: currMusic, music: currMusic,
fit: BoxFit.cover, fit: BoxFit.cover,
backgroundColor: Colors.black12, backgroundColor: Colors.black12,
icon: const Icon(FluentIcons.music_note_2_24_regular, size: 90), icon:
const Icon(FluentIcons.music_note_2_24_regular, size: 90),
), ),
), ),
const SizedBox(height: 40), const SizedBox(height: 40),
@ -215,6 +253,26 @@ class _MusicPlayerState extends State<MusicPlayer> {
), ),
), ),
); );
Widget _buildToggleListButton() => fluent.ToggleButton(
checked: _showPlaylist,
onChanged: (s) => setState(() => _showPlaylist = s),
child: const fluent.Icon(fluent.FluentIcons.playlist_music),
);
Widget _buildPlaylistPane() {
return ListView.builder(
itemBuilder: (c, i) {
final music = widget.musicsList[i];
return ListTile(
title: Text(music.title),
subtitle: Text(music.artist),
selected: currMusic == music,
onTap: () => _playMusic(music),
);
},
itemCount: widget.musicsList.length,
);
} }
} }