mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-12 23:45:18 +00:00
Queue support added for both Desktop & Mobile
This commit is contained in:
parent
a985c19ad8
commit
7e24059900
@ -240,6 +240,7 @@ class ArtistProfile extends HookConsumerWidget {
|
||||
duration: duration,
|
||||
track: track,
|
||||
thumbnailUrl: thumbnailUrl,
|
||||
isActive: playback.track?.id == track.value.id,
|
||||
onTrackPlayButtonPressed: (currentTrack) =>
|
||||
playPlaylist(
|
||||
topTracks.toList(),
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/components/Player/PlayerQueue.dart';
|
||||
import 'package:spotube/components/Shared/DownloadTrackButton.dart';
|
||||
import 'package:spotube/components/Shared/HeartButton.dart';
|
||||
import 'package:spotube/hooks/useForceUpdate.dart';
|
||||
@ -27,6 +28,29 @@ class PlayerActions extends HookConsumerWidget {
|
||||
return Row(
|
||||
mainAxisAlignment: mainAxisAlignment,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.queue_music_rounded),
|
||||
onPressed: playback.playlist != null
|
||||
? () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isDismissible: true,
|
||||
enableDrag: true,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: MediaQuery.of(context).size.height * .7,
|
||||
),
|
||||
builder: (context) {
|
||||
return const PlayerQueue();
|
||||
},
|
||||
);
|
||||
}
|
||||
: null,
|
||||
),
|
||||
DownloadTrackButton(
|
||||
track: playback.track,
|
||||
),
|
||||
|
77
lib/components/Player/PlayerQueue.dart
Normal file
77
lib/components/Player/PlayerQueue.dart
Normal file
@ -0,0 +1,77 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:spotube/components/Shared/NotFound.dart';
|
||||
import 'package:spotube/components/Shared/TrackTile.dart';
|
||||
import 'package:spotube/helpers/image-to-url-string.dart';
|
||||
import 'package:spotube/helpers/zero-pad-num-str.dart';
|
||||
import 'package:spotube/provider/Playback.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
class PlayerQueue extends HookConsumerWidget {
|
||||
const PlayerQueue({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final playback = ref.watch(playbackProvider);
|
||||
final tracks = playback.playlist?.tracks ?? [];
|
||||
|
||||
if (tracks.isEmpty) {
|
||||
return const NotFound(vertical: true);
|
||||
}
|
||||
|
||||
return BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.navigationRailTheme
|
||||
.backgroundColor
|
||||
?.withOpacity(0.5),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
margin: const EdgeInsets.all(8.0),
|
||||
padding: const EdgeInsets.only(
|
||||
top: 5.0,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
"Queue (${playback.playlist?.name})",
|
||||
style: Theme.of(context).textTheme.headline4,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
Flexible(
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
...tracks.asMap().entries.mapIndexed((i, track) {
|
||||
String duration =
|
||||
"${track.value.duration?.inMinutes.remainder(60)}:${zeroPadNumStr(track.value.duration?.inSeconds.remainder(60) ?? 0)}";
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: TrackTile(
|
||||
playback,
|
||||
track: track,
|
||||
duration: duration,
|
||||
thumbnailUrl:
|
||||
imageToUrlString(track.value.album?.images),
|
||||
isActive: playback.track?.id == track.value.id,
|
||||
onTrackPlayButtonPressed: (currentTrack) async {
|
||||
if (playback.track?.id == track.value.id) return;
|
||||
await playback.setPlaylistPosition(i);
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -114,6 +114,7 @@ class Search extends HookConsumerWidget {
|
||||
duration: duration,
|
||||
thumbnailUrl:
|
||||
imageToUrlString(track.value.album?.images),
|
||||
isActive: playback.track?.id == track.value.id,
|
||||
onTrackPlayButtonPressed: (currentTrack) async {
|
||||
var isPlaylistPlaying = playback.playlist?.id !=
|
||||
null &&
|
||||
|
@ -1,13 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class NotFound extends StatelessWidget {
|
||||
const NotFound({Key? key}) : super(key: key);
|
||||
final bool vertical;
|
||||
const NotFound({Key? key, this.vertical = false}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
final widgets = [
|
||||
SizedBox(
|
||||
height: 150,
|
||||
width: 150,
|
||||
@ -24,7 +23,7 @@ class NotFound extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
];
|
||||
return vertical ? Column(children: widgets) : Row(children: widgets);
|
||||
}
|
||||
}
|
||||
|
@ -24,14 +24,21 @@ class TrackTile extends HookConsumerWidget {
|
||||
final bool userPlaylist;
|
||||
// null playlistId indicates its not inside a playlist
|
||||
final String? playlistId;
|
||||
|
||||
final bool showAlbum;
|
||||
|
||||
final bool isActive;
|
||||
|
||||
TrackTile(
|
||||
this.playback, {
|
||||
required this.track,
|
||||
required this.duration,
|
||||
required this.isActive,
|
||||
this.playlistId,
|
||||
this.userPlaylist = false,
|
||||
this.thumbnailUrl,
|
||||
this.onTrackPlayButtonPressed,
|
||||
this.showAlbum = true,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@ -162,7 +169,15 @@ class TrackTile extends HookConsumerWidget {
|
||||
});
|
||||
}
|
||||
|
||||
return Row(
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 500),
|
||||
decoration: BoxDecoration(
|
||||
color: isActive
|
||||
? Theme.of(context).popupMenuTheme.color
|
||||
: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(isActive ? 10 : 0),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 20,
|
||||
@ -224,7 +239,7 @@ class TrackTile extends HookConsumerWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
if (breakpoint.isMoreThan(Breakpoints.md))
|
||||
if (breakpoint.isMoreThan(Breakpoints.md) && showAlbum)
|
||||
Expanded(
|
||||
child: LinkText(
|
||||
track.value.album!.name!,
|
||||
@ -307,6 +322,7 @@ class TrackTile extends HookConsumerWidget {
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -92,6 +92,7 @@ class TracksTableView extends HookConsumerWidget {
|
||||
duration: duration,
|
||||
thumbnailUrl: thumbnailUrl,
|
||||
userPlaylist: userPlaylist,
|
||||
isActive: playback.track?.id == track.value.id,
|
||||
onTrackPlayButtonPressed: onTrackPlayButtonPressed,
|
||||
);
|
||||
}).toList()
|
||||
|
@ -3,7 +3,7 @@ import 'package:spotube/extensions/ShimmerColorTheme.dart';
|
||||
|
||||
final materialWhite = MaterialColor(Colors.white.value, {
|
||||
50: Colors.white,
|
||||
100: Colors.blueGrey[50]!,
|
||||
100: Colors.blueGrey[100]!,
|
||||
200: Colors.white,
|
||||
300: Colors.white,
|
||||
400: Colors.blueGrey[300]!,
|
||||
|
Loading…
Reference in New Issue
Block a user