mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
feat: compact and adaptive playbutton card design
This commit is contained in:
parent
1bdce9fe96
commit
eeb8cabf49
@ -33,11 +33,9 @@ enum AlbumType {
|
|||||||
|
|
||||||
class AlbumCard extends HookConsumerWidget {
|
class AlbumCard extends HookConsumerWidget {
|
||||||
final Album album;
|
final Album album;
|
||||||
final PlaybuttonCardViewType viewType;
|
|
||||||
const AlbumCard(
|
const AlbumCard(
|
||||||
this.album, {
|
this.album, {
|
||||||
Key? key,
|
Key? key,
|
||||||
this.viewType = PlaybuttonCardViewType.square,
|
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -65,7 +63,6 @@ class AlbumCard extends HookConsumerWidget {
|
|||||||
album.images,
|
album.images,
|
||||||
placeholder: ImagePlaceholder.collection,
|
placeholder: ImagePlaceholder.collection,
|
||||||
),
|
),
|
||||||
viewType: viewType,
|
|
||||||
margin: EdgeInsets.symmetric(horizontal: marginH.toDouble()),
|
margin: EdgeInsets.symmetric(horizontal: marginH.toDouble()),
|
||||||
isPlaying: isPlaylistPlaying,
|
isPlaying: isPlaylistPlaying,
|
||||||
isLoading: isPlaylistPlaying && playlist?.isLoading == true,
|
isLoading: isPlaylistPlaying && playlist?.isLoading == true,
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import 'package:flutter/gestures.dart';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/material.dart' hide Page;
|
import 'package:flutter/material.dart' hide Page;
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
|
import 'package:spotube/components/playlist/playlist_card.dart';
|
||||||
import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart';
|
import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart';
|
||||||
import 'package:spotube/components/shared/waypoint.dart';
|
import 'package:spotube/components/shared/waypoint.dart';
|
||||||
import 'package:spotube/components/playlist/playlist_card.dart';
|
|
||||||
import 'package:spotube/models/logger.dart';
|
import 'package:spotube/models/logger.dart';
|
||||||
import 'package:spotube/services/queries/queries.dart';
|
import 'package:spotube/services/queries/queries.dart';
|
||||||
|
|
||||||
@ -27,51 +28,42 @@ class CategoryCard extends HookConsumerWidget {
|
|||||||
category.id!,
|
category.id!,
|
||||||
);
|
);
|
||||||
|
|
||||||
final playlists = playlistQuery.pages
|
if (playlistQuery.hasErrors && !playlistQuery.hasPageData) {
|
||||||
.expand(
|
return const SizedBox.shrink();
|
||||||
(page) => page.items ?? const Iterable.empty(),
|
}
|
||||||
)
|
final playlists = playlistQuery.pages.expand(
|
||||||
.toList();
|
(page) {
|
||||||
|
return page.items?.where((i) => i != null) ?? const Iterable.empty();
|
||||||
return Column(
|
},
|
||||||
children: [
|
).toList();
|
||||||
Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Row(
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
category.name ?? "Unknown",
|
category.name!,
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
),
|
),
|
||||||
],
|
ScrollConfiguration(
|
||||||
),
|
|
||||||
),
|
|
||||||
playlistQuery.hasPageError && !playlistQuery.hasPageData
|
|
||||||
? Text("Something Went Wrong\n${playlistQuery.errors.first}")
|
|
||||||
: SizedBox(
|
|
||||||
height: 245,
|
|
||||||
child: ScrollConfiguration(
|
|
||||||
behavior: ScrollConfiguration.of(context).copyWith(
|
behavior: ScrollConfiguration.of(context).copyWith(
|
||||||
dragDevices: {
|
dragDevices: {
|
||||||
PointerDeviceKind.touch,
|
PointerDeviceKind.touch,
|
||||||
PointerDeviceKind.mouse,
|
PointerDeviceKind.mouse,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
child: Scrollbar(
|
|
||||||
controller: scrollController,
|
|
||||||
interactive: false,
|
|
||||||
child: Waypoint(
|
child: Waypoint(
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
onTouchEdge: () {
|
onTouchEdge: playlistQuery.fetchNext,
|
||||||
playlistQuery.fetchNext();
|
child: SingleChildScrollView(
|
||||||
},
|
|
||||||
child: ListView(
|
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
shrinkWrap: true,
|
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
...playlists
|
...playlists.map((playlist) => PlaylistCard(playlist)),
|
||||||
.map((playlist) => PlaylistCard(playlist)),
|
|
||||||
if (playlistQuery.hasNextPage)
|
if (playlistQuery.hasNextPage)
|
||||||
const ShimmerPlaybuttonCard(count: 1),
|
const ShimmerPlaybuttonCard(count: 1),
|
||||||
],
|
],
|
||||||
@ -79,8 +71,8 @@ class CategoryCard extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
|
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/album/album_card.dart';
|
import 'package:spotube/components/album/album_card.dart';
|
||||||
import 'package:spotube/components/shared/playbutton_card.dart';
|
|
||||||
import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart';
|
import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart';
|
||||||
import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
|
import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
|
||||||
import 'package:spotube/hooks/use_breakpoint_value.dart';
|
import 'package:spotube/hooks/use_breakpoint_value.dart';
|
||||||
@ -28,9 +27,6 @@ class UserAlbums extends HookConsumerWidget {
|
|||||||
sm: 0,
|
sm: 0,
|
||||||
others: 20,
|
others: 20,
|
||||||
);
|
);
|
||||||
final viewType = MediaQuery.of(context).size.width < 480
|
|
||||||
? PlaybuttonCardViewType.list
|
|
||||||
: PlaybuttonCardViewType.square;
|
|
||||||
|
|
||||||
final searchText = useState('');
|
final searchText = useState('');
|
||||||
|
|
||||||
@ -82,7 +78,6 @@ class UserAlbums extends HookConsumerWidget {
|
|||||||
alignment: WrapAlignment.center,
|
alignment: WrapAlignment.center,
|
||||||
children: albums
|
children: albums
|
||||||
.map((album) => AlbumCard(
|
.map((album) => AlbumCard(
|
||||||
viewType: viewType,
|
|
||||||
TypeConversionUtils.simpleAlbum_X_Album(album),
|
TypeConversionUtils.simpleAlbum_X_Album(album),
|
||||||
))
|
))
|
||||||
.toList(),
|
.toList(),
|
||||||
|
@ -7,7 +7,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/playlist/playlist_create_dialog.dart';
|
import 'package:spotube/components/playlist/playlist_create_dialog.dart';
|
||||||
import 'package:spotube/components/shared/playbutton_card.dart';
|
|
||||||
import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart';
|
import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart';
|
||||||
import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
|
import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
|
||||||
import 'package:spotube/components/playlist/playlist_card.dart';
|
import 'package:spotube/components/playlist/playlist_card.dart';
|
||||||
@ -28,9 +27,6 @@ class UserPlaylists extends HookConsumerWidget {
|
|||||||
sm: 0,
|
sm: 0,
|
||||||
others: 20,
|
others: 20,
|
||||||
);
|
);
|
||||||
final viewType = MediaQuery.of(context).size.width < 480
|
|
||||||
? PlaybuttonCardViewType.list
|
|
||||||
: PlaybuttonCardViewType.square;
|
|
||||||
final auth = ref.watch(AuthenticationNotifier.provider);
|
final auth = ref.watch(AuthenticationNotifier.provider);
|
||||||
|
|
||||||
final playlistsQuery = useQueries.playlist.ofMine(ref);
|
final playlistsQuery = useQueries.playlist.ofMine(ref);
|
||||||
@ -81,12 +77,7 @@ class UserPlaylists extends HookConsumerWidget {
|
|||||||
|
|
||||||
final children = [
|
final children = [
|
||||||
const PlaylistCreateDialog(),
|
const PlaylistCreateDialog(),
|
||||||
...playlists
|
...playlists.map((playlist) => PlaylistCard(playlist)).toList(),
|
||||||
.map((playlist) => PlaylistCard(
|
|
||||||
playlist,
|
|
||||||
viewType: viewType,
|
|
||||||
))
|
|
||||||
.toList(),
|
|
||||||
];
|
];
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: playlistsQuery.refresh,
|
onRefresh: playlistsQuery.refresh,
|
||||||
|
@ -13,11 +13,9 @@ import 'package:spotube/utils/type_conversion_utils.dart';
|
|||||||
|
|
||||||
class PlaylistCard extends HookConsumerWidget {
|
class PlaylistCard extends HookConsumerWidget {
|
||||||
final PlaylistSimple playlist;
|
final PlaylistSimple playlist;
|
||||||
final PlaybuttonCardViewType viewType;
|
|
||||||
const PlaylistCard(
|
const PlaylistCard(
|
||||||
this.playlist, {
|
this.playlist, {
|
||||||
Key? key,
|
Key? key,
|
||||||
this.viewType = PlaybuttonCardViewType.square,
|
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
@ -43,9 +41,9 @@ class PlaylistCard extends HookConsumerWidget {
|
|||||||
final spotify = ref.watch(spotifyProvider);
|
final spotify = ref.watch(spotifyProvider);
|
||||||
|
|
||||||
return PlaybuttonCard(
|
return PlaybuttonCard(
|
||||||
viewType: viewType,
|
|
||||||
margin: EdgeInsets.symmetric(horizontal: marginH.toDouble()),
|
margin: EdgeInsets.symmetric(horizontal: marginH.toDouble()),
|
||||||
title: playlist.name!,
|
title: playlist.name!,
|
||||||
|
description: playlist.description,
|
||||||
imageUrl: TypeConversionUtils.image_X_UrlString(
|
imageUrl: TypeConversionUtils.image_X_UrlString(
|
||||||
playlist.images,
|
playlist.images,
|
||||||
placeholder: ImagePlaceholder.collection,
|
placeholder: ImagePlaceholder.collection,
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
|
import 'package:auto_size_text/auto_size_text.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
|
||||||
import 'package:spotube/collections/assets.gen.dart';
|
import 'package:spotube/collections/assets.gen.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/shared/hover_builder.dart';
|
|
||||||
import 'package:spotube/components/shared/spotube_marquee_text.dart';
|
|
||||||
import 'package:spotube/components/shared/image/universal_image.dart';
|
import 'package:spotube/components/shared/image/universal_image.dart';
|
||||||
|
import 'package:spotube/hooks/use_breakpoint_value.dart';
|
||||||
enum PlaybuttonCardViewType { square, list }
|
import 'package:spotube/hooks/use_brightness_value.dart';
|
||||||
|
|
||||||
class PlaybuttonCard extends HookWidget {
|
class PlaybuttonCard extends HookWidget {
|
||||||
final void Function()? onTap;
|
final void Function()? onTap;
|
||||||
@ -19,7 +18,6 @@ class PlaybuttonCard extends HookWidget {
|
|||||||
final bool isPlaying;
|
final bool isPlaying;
|
||||||
final bool isLoading;
|
final bool isLoading;
|
||||||
final String title;
|
final String title;
|
||||||
final PlaybuttonCardViewType viewType;
|
|
||||||
|
|
||||||
const PlaybuttonCard({
|
const PlaybuttonCard({
|
||||||
required this.imageUrl,
|
required this.imageUrl,
|
||||||
@ -31,190 +29,144 @@ class PlaybuttonCard extends HookWidget {
|
|||||||
this.onPlaybuttonPressed,
|
this.onPlaybuttonPressed,
|
||||||
this.onAddToQueuePressed,
|
this.onAddToQueuePressed,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
this.viewType = PlaybuttonCardViewType.square,
|
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final backgroundColor = Theme.of(context).cardColor;
|
final theme = Theme.of(context);
|
||||||
|
final radius = BorderRadius.circular(15);
|
||||||
|
|
||||||
final isSquare = viewType == PlaybuttonCardViewType.square;
|
final shadowColor = useBrightnessValue(
|
||||||
|
theme.colorScheme.background,
|
||||||
|
theme.colorScheme.background,
|
||||||
|
);
|
||||||
|
|
||||||
|
final double size = useBreakpointValue<double>(
|
||||||
|
sm: 130,
|
||||||
|
md: 150,
|
||||||
|
others: 170,
|
||||||
|
);
|
||||||
|
|
||||||
|
final end = useBreakpointValue<double>(
|
||||||
|
sm: 5,
|
||||||
|
md: 7,
|
||||||
|
others: 10,
|
||||||
|
);
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
|
constraints: BoxConstraints(maxWidth: size),
|
||||||
margin: margin,
|
margin: margin,
|
||||||
|
child: Material(
|
||||||
|
color: Color.lerp(
|
||||||
|
theme.colorScheme.surfaceVariant,
|
||||||
|
theme.colorScheme.surface,
|
||||||
|
useBrightnessValue(.9, .7),
|
||||||
|
),
|
||||||
|
borderRadius: radius,
|
||||||
|
shadowColor: shadowColor,
|
||||||
|
elevation: 3,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
|
mouseCursor: SystemMouseCursors.click,
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: radius,
|
||||||
highlightColor: Colors.black12,
|
splashFactory: theme.splashFactory,
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: BoxConstraints(
|
|
||||||
maxWidth: isSquare ? 200 : double.infinity,
|
|
||||||
maxHeight: !isSquare ? 60 : double.infinity,
|
|
||||||
),
|
|
||||||
child: HoverBuilder(builder: (context, isHovering) {
|
|
||||||
final playButton = IconButton(
|
|
||||||
onPressed: onPlaybuttonPressed,
|
|
||||||
style: IconButton.styleFrom(
|
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
|
||||||
hoverColor: Theme.of(context).primaryColor.withOpacity(0.5),
|
|
||||||
),
|
|
||||||
icon: isLoading
|
|
||||||
? SizedBox(
|
|
||||||
height: 23,
|
|
||||||
width: 23,
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
color: ThemeData.estimateBrightnessForColor(
|
|
||||||
Theme.of(context).primaryColor,
|
|
||||||
) ==
|
|
||||||
Brightness.dark
|
|
||||||
? Colors.white
|
|
||||||
: Colors.grey[900],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Icon(
|
|
||||||
isPlaying ? SpotubeIcons.pause : SpotubeIcons.play,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
final addToQueueButton = IconButton(
|
|
||||||
onPressed: isLoading ? null : onAddToQueuePressed,
|
|
||||||
style: IconButton.styleFrom(
|
|
||||||
backgroundColor: Theme.of(context).cardColor,
|
|
||||||
hoverColor: Theme.of(context)
|
|
||||||
.cardColor
|
|
||||||
.withOpacity(isLoading ? 1 : 0.5),
|
|
||||||
),
|
|
||||||
icon: const Icon(SpotubeIcons.queueAdd),
|
|
||||||
);
|
|
||||||
final image = ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
child: UniversalImage(
|
|
||||||
path: imageUrl,
|
|
||||||
width: isSquare ? 200 : 60,
|
|
||||||
placeholder: (context, url) => Assets.placeholder.image(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final square = Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
// thumbnail of the playlist
|
|
||||||
Stack(
|
|
||||||
children: [
|
|
||||||
image,
|
|
||||||
Positioned.directional(
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
bottom: 10,
|
|
||||||
end: 5,
|
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (!isPlaying) addToQueueButton,
|
Stack(
|
||||||
const SizedBox(height: 5),
|
clipBehavior: Clip.none,
|
||||||
playButton,
|
children: [
|
||||||
],
|
Container(
|
||||||
|
margin: const EdgeInsets.only(
|
||||||
|
left: 8,
|
||||||
|
right: 8,
|
||||||
|
top: 8,
|
||||||
),
|
),
|
||||||
)
|
constraints: BoxConstraints(maxHeight: size),
|
||||||
],
|
child: ClipRRect(
|
||||||
|
borderRadius: radius,
|
||||||
|
child: UniversalImage(
|
||||||
|
path: imageUrl,
|
||||||
|
placeholder: (context, url) {
|
||||||
|
return Assets.albumPlaceholder
|
||||||
|
.image(fit: BoxFit.cover);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 5),
|
),
|
||||||
Padding(
|
),
|
||||||
padding:
|
Positioned.directional(
|
||||||
const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
textDirection: TextDirection.ltr,
|
||||||
|
end: end,
|
||||||
|
bottom: -5,
|
||||||
child: Column(
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Tooltip(
|
if (!isPlaying)
|
||||||
message: title,
|
IconButton(
|
||||||
child: SizedBox(
|
style: IconButton.styleFrom(
|
||||||
height: 20,
|
backgroundColor: theme.colorScheme.background,
|
||||||
child: SpotubeMarqueeText(
|
foregroundColor: theme.colorScheme.primary,
|
||||||
text: title,
|
minimumSize: const Size.square(10),
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
isHovering: isHovering,
|
|
||||||
),
|
),
|
||||||
|
icon: const Icon(SpotubeIcons.queueAdd),
|
||||||
|
onPressed: isLoading ? null : onAddToQueuePressed,
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
IconButton(
|
||||||
|
style: IconButton.styleFrom(
|
||||||
|
backgroundColor: theme.colorScheme.primaryContainer,
|
||||||
|
foregroundColor: theme.colorScheme.primary,
|
||||||
|
minimumSize: const Size.square(10),
|
||||||
),
|
),
|
||||||
if (description != null) ...[
|
icon: isLoading
|
||||||
const SizedBox(height: 10),
|
? SizedBox.fromSize(
|
||||||
SizedBox(
|
size: const Size.square(15),
|
||||||
height: 30,
|
child: const CircularProgressIndicator(
|
||||||
child: SpotubeMarqueeText(
|
strokeWidth: 2),
|
||||||
text: description!,
|
)
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
: isPlaying
|
||||||
isHovering: isHovering,
|
? const Icon(SpotubeIcons.pause)
|
||||||
|
: const Icon(SpotubeIcons.play),
|
||||||
|
onPressed: isLoading ? null : onPlaybuttonPressed,
|
||||||
),
|
),
|
||||||
),
|
|
||||||
]
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
),
|
||||||
|
const SizedBox(height: 15),
|
||||||
final list = Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
// thumbnail of the playlist
|
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Row(
|
child: Padding(
|
||||||
children: [
|
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||||
image,
|
child: AutoSizeText(
|
||||||
const SizedBox(width: 10),
|
title,
|
||||||
Flexible(
|
maxLines: 1,
|
||||||
child: RichText(
|
minFontSize: theme.textTheme.bodyMedium!.fontSize!,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
text: TextSpan(
|
),
|
||||||
children: [
|
),
|
||||||
TextSpan(
|
|
||||||
text: title,
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyMedium
|
|
||||||
?.copyWith(fontWeight: FontWeight.bold),
|
|
||||||
),
|
),
|
||||||
if (description != null)
|
if (description != null)
|
||||||
TextSpan(
|
Flexible(
|
||||||
text: '\n$description',
|
child: Padding(
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||||
|
child: AutoSizeText(
|
||||||
|
description!,
|
||||||
|
maxLines: 2,
|
||||||
|
style: theme.textTheme.bodySmall?.copyWith(
|
||||||
|
color: theme.colorScheme.onSurface.withOpacity(.5),
|
||||||
),
|
),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
addToQueueButton,
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
playButton,
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
return Ink(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: backgroundColor,
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
blurRadius: 10,
|
|
||||||
offset: const Offset(0, 3),
|
|
||||||
spreadRadius: 5,
|
|
||||||
color: Theme.of(context).colorScheme.shadow,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: isSquare ? square : list,
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
|
||||||
import 'package:spotube/extensions/theme.dart';
|
import 'package:spotube/hooks/use_breakpoint_value.dart';
|
||||||
|
|
||||||
class ShimmerPlaybuttonCardPainter extends CustomPainter {
|
class ShimmerPlaybuttonCardPainter extends CustomPainter {
|
||||||
final Color background;
|
final Color background;
|
||||||
@ -12,29 +13,59 @@ class ShimmerPlaybuttonCardPainter extends CustomPainter {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(Canvas canvas, Size size) {
|
void paint(Canvas canvas, Size size) {
|
||||||
|
const radius = Radius.circular(15);
|
||||||
|
|
||||||
canvas.drawRRect(
|
canvas.drawRRect(
|
||||||
RRect.fromRectAndRadius(
|
RRect.fromRectAndRadius(
|
||||||
Rect.fromLTWH(0, 0, size.width, size.height),
|
Rect.fromLTWH(0, 0, size.width, size.height),
|
||||||
const Radius.circular(10),
|
radius,
|
||||||
),
|
),
|
||||||
Paint()..color = background,
|
Paint()..color = background,
|
||||||
);
|
);
|
||||||
|
|
||||||
canvas.drawRRect(
|
canvas.drawRRect(
|
||||||
RRect.fromRectAndRadius(
|
RRect.fromRectAndRadius(
|
||||||
Rect.fromLTWH(0, 0, size.width, size.height - 45),
|
Rect.fromLTWH(8, 8, size.width - 16, size.height - 90),
|
||||||
const Radius.circular(10),
|
radius,
|
||||||
),
|
),
|
||||||
Paint()..color = foreground,
|
Paint()..color = foreground,
|
||||||
);
|
);
|
||||||
|
|
||||||
canvas.drawRRect(
|
canvas.drawRRect(
|
||||||
RRect.fromRectAndRadius(
|
RRect.fromRectAndRadius(
|
||||||
Rect.fromLTWH(size.width / 4, size.height - 27, size.width / 2, 10),
|
Rect.fromLTWH(12, size.height - 67, size.width / 2, 10),
|
||||||
const Radius.circular(10),
|
radius,
|
||||||
),
|
),
|
||||||
Paint()..color = foreground,
|
Paint()..color = foreground,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
canvas.drawRRect(
|
||||||
|
RRect.fromRectAndRadius(
|
||||||
|
Rect.fromLTWH(12, size.height - 45, size.width - 24, 8),
|
||||||
|
radius,
|
||||||
|
),
|
||||||
|
Paint()..color = foreground,
|
||||||
|
);
|
||||||
|
|
||||||
|
canvas.drawRRect(
|
||||||
|
RRect.fromRectAndRadius(
|
||||||
|
Rect.fromLTWH(12, size.height - 30, size.width * .4, 8),
|
||||||
|
radius,
|
||||||
|
),
|
||||||
|
Paint()..color = foreground,
|
||||||
|
);
|
||||||
|
|
||||||
|
canvas.drawCircle(
|
||||||
|
Offset(size.width * .85, size.height * .50),
|
||||||
|
17,
|
||||||
|
Paint()..color = background,
|
||||||
|
);
|
||||||
|
|
||||||
|
canvas.drawCircle(
|
||||||
|
Offset(size.width * .85, size.height * .67),
|
||||||
|
17,
|
||||||
|
Paint()..color = background,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -43,7 +74,7 @@ class ShimmerPlaybuttonCardPainter extends CustomPainter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ShimmerPlaybuttonCard extends StatelessWidget {
|
class ShimmerPlaybuttonCard extends HookWidget {
|
||||||
final int count;
|
final int count;
|
||||||
|
|
||||||
const ShimmerPlaybuttonCard({
|
const ShimmerPlaybuttonCard({
|
||||||
@ -53,10 +84,19 @@ class ShimmerPlaybuttonCard extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
final Size size = useBreakpointValue<Size>(
|
||||||
|
sm: const Size(130, 200),
|
||||||
|
md: const Size(150, 220),
|
||||||
|
others: const Size(170, 240),
|
||||||
|
);
|
||||||
|
|
||||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
final shimmerTheme = ShimmerColorTheme(
|
final bgColor = theme.colorScheme.surfaceVariant.withOpacity(.2);
|
||||||
shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200],
|
final fgColor = Color.lerp(
|
||||||
shimmerColor: isDark ? Colors.grey[800] : Colors.grey[300],
|
theme.colorScheme.surfaceVariant,
|
||||||
|
isDark ? Colors.black : Colors.white,
|
||||||
|
.4,
|
||||||
);
|
);
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
@ -64,12 +104,10 @@ class ShimmerPlaybuttonCard extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
for (var i = 0; i < count; i++) ...[
|
for (var i = 0; i < count; i++) ...[
|
||||||
CustomPaint(
|
CustomPaint(
|
||||||
size: const Size(200, 250),
|
size: size,
|
||||||
painter: ShimmerPlaybuttonCardPainter(
|
painter: ShimmerPlaybuttonCardPainter(
|
||||||
background: shimmerTheme.shimmerBackgroundColor ??
|
background: bgColor,
|
||||||
Theme.of(context).scaffoldBackgroundColor,
|
foreground: fgColor!,
|
||||||
foreground:
|
|
||||||
shimmerTheme.shimmerColor ?? Theme.of(context).cardColor,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
@ -78,89 +116,3 @@ class ShimmerPlaybuttonCard extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// class ShimmerPlaybuttonCard extends StatelessWidget {
|
|
||||||
// final int count;
|
|
||||||
// const ShimmerPlaybuttonCard({Key? key, this.count = 4}) : super(key: key);
|
|
||||||
|
|
||||||
// @override
|
|
||||||
// Widget build(BuildContext context) {
|
|
||||||
// final shimmerColor =
|
|
||||||
// Theme.of(context).extension<ShimmerColorTheme>()?.shimmerColor ??
|
|
||||||
// Colors.white;
|
|
||||||
// final shimmerBackgroundColor = Theme.of(context)
|
|
||||||
// .extension<ShimmerColorTheme>()
|
|
||||||
// ?.shimmerBackgroundColor ??
|
|
||||||
// Colors.grey;
|
|
||||||
|
|
||||||
// final card = Stack(
|
|
||||||
// children: [
|
|
||||||
// SkeletonAnimation(
|
|
||||||
// shimmerColor: shimmerColor,
|
|
||||||
// borderRadius: BorderRadius.circular(20),
|
|
||||||
// shimmerDuration: 1000,
|
|
||||||
// child: Container(
|
|
||||||
// width: 200,
|
|
||||||
// height: 220,
|
|
||||||
// decoration: BoxDecoration(
|
|
||||||
// color: shimmerBackgroundColor,
|
|
||||||
// borderRadius: BorderRadius.circular(10),
|
|
||||||
// ),
|
|
||||||
// margin: const EdgeInsets.only(top: 10),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// Column(
|
|
||||||
// children: [
|
|
||||||
// SkeletonAnimation(
|
|
||||||
// shimmerColor: shimmerBackgroundColor,
|
|
||||||
// borderRadius: BorderRadius.circular(20),
|
|
||||||
// shimmerDuration: 1000,
|
|
||||||
// child: Container(
|
|
||||||
// width: 200,
|
|
||||||
// height: 180,
|
|
||||||
// decoration: BoxDecoration(
|
|
||||||
// color: shimmerColor,
|
|
||||||
// borderRadius: BorderRadius.circular(10),
|
|
||||||
// ),
|
|
||||||
// margin: const EdgeInsets.only(top: 10),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// const SizedBox(height: 5),
|
|
||||||
// SkeletonAnimation(
|
|
||||||
// shimmerColor: shimmerBackgroundColor,
|
|
||||||
// borderRadius: BorderRadius.circular(20),
|
|
||||||
// shimmerDuration: 1000,
|
|
||||||
// child: Container(
|
|
||||||
// width: 150,
|
|
||||||
// height: 10,
|
|
||||||
// decoration: BoxDecoration(
|
|
||||||
// color: shimmerColor,
|
|
||||||
// borderRadius: BorderRadius.circular(10),
|
|
||||||
// ),
|
|
||||||
// margin: const EdgeInsets.only(top: 10),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// );
|
|
||||||
|
|
||||||
// return SingleChildScrollView(
|
|
||||||
// physics: const NeverScrollableScrollPhysics(),
|
|
||||||
// scrollDirection: Axis.horizontal,
|
|
||||||
// child: Row(
|
|
||||||
// children: [
|
|
||||||
// Row(
|
|
||||||
// children: List.generate(
|
|
||||||
// count,
|
|
||||||
// (_) => Padding(
|
|
||||||
// padding: const EdgeInsets.symmetric(horizontal: 15),
|
|
||||||
// child: card,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
import 'package:auto_size_text/auto_size_text.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
||||||
import 'package:marquee/marquee.dart';
|
|
||||||
|
|
||||||
class SpotubeMarqueeText extends HookWidget {
|
|
||||||
final bool? isHovering;
|
|
||||||
const SpotubeMarqueeText({
|
|
||||||
Key? key,
|
|
||||||
required this.text,
|
|
||||||
this.style,
|
|
||||||
this.isHovering,
|
|
||||||
}) : super(key: key);
|
|
||||||
final TextStyle? style;
|
|
||||||
final String text;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final uKey = useState(UniqueKey());
|
|
||||||
|
|
||||||
useEffect(() {
|
|
||||||
uKey.value = UniqueKey();
|
|
||||||
return;
|
|
||||||
}, [isHovering]);
|
|
||||||
|
|
||||||
return AutoSizeText(
|
|
||||||
text,
|
|
||||||
minFontSize: 13,
|
|
||||||
style: DefaultTextStyle.of(context).style.merge(style),
|
|
||||||
maxLines: 1,
|
|
||||||
overflowReplacement: Marquee(
|
|
||||||
key: uKey.value,
|
|
||||||
text: text,
|
|
||||||
style: DefaultTextStyle.of(context).style.merge(style),
|
|
||||||
scrollAxis: Axis.horizontal,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
blankSpace: 40.0,
|
|
||||||
velocity: 30.0,
|
|
||||||
accelerationDuration: const Duration(seconds: 1),
|
|
||||||
accelerationCurve: Curves.linear,
|
|
||||||
decelerationDuration: const Duration(milliseconds: 500),
|
|
||||||
decelerationCurve: Curves.easeOut,
|
|
||||||
showFadingOnlyWhenScrolling: true,
|
|
||||||
numberOfRounds: isHovering == true ? null : 1,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -68,17 +68,21 @@ class GenrePage extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
child: ListView.builder(
|
child: SingleChildScrollView(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
child: SafeArea(
|
||||||
controller: scrollController,
|
child: Column(
|
||||||
itemCount: categories.length,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
itemBuilder: (context, index) {
|
children: [
|
||||||
final category = categories[index];
|
...categories.mapIndexed((index, category) {
|
||||||
if (searchText.value.isEmpty && index == categories.length - 1) {
|
if (searchText.value.isEmpty &&
|
||||||
|
index == categories.length - 1) {
|
||||||
return const ShimmerCategories();
|
return const ShimmerCategories();
|
||||||
}
|
}
|
||||||
return SafeArea(child: CategoryCard(category));
|
return CategoryCard(category);
|
||||||
},
|
})
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -47,22 +47,20 @@ class PersonalizedItemCard extends HookWidget {
|
|||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
return Column(
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Row(
|
child: Text(
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
title,
|
title,
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
ScrollConfiguration(
|
||||||
SizedBox(
|
|
||||||
height: playlists != null ? 245 : 285,
|
|
||||||
child: ScrollConfiguration(
|
|
||||||
behavior: ScrollConfiguration.of(context).copyWith(
|
behavior: ScrollConfiguration.of(context).copyWith(
|
||||||
dragDevices: {
|
dragDevices: {
|
||||||
PointerDeviceKind.touch,
|
PointerDeviceKind.touch,
|
||||||
@ -75,12 +73,13 @@ class PersonalizedItemCard extends HookWidget {
|
|||||||
child: Waypoint(
|
child: Waypoint(
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
onTouchEdge: hasNextPage ? onFetchMore : null,
|
onTouchEdge: hasNextPage ? onFetchMore : null,
|
||||||
child: SafeArea(
|
child: SingleChildScrollView(
|
||||||
child: ListView(
|
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
shrinkWrap: true,
|
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
...?playlistItems
|
...?playlistItems
|
||||||
?.map((playlist) => PlaylistCard(playlist)),
|
?.map((playlist) => PlaylistCard(playlist)),
|
||||||
@ -96,8 +95,8 @@ class PersonalizedItemCard extends HookWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,11 +110,14 @@ class PersonalizedPage extends HookConsumerWidget {
|
|||||||
|
|
||||||
final newReleases = useQueries.album.newReleases(ref);
|
final newReleases = useQueries.album.newReleases(ref);
|
||||||
|
|
||||||
return ListView(
|
return SingleChildScrollView(
|
||||||
|
child: SafeArea(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
PersonalizedItemCard(
|
PersonalizedItemCard(
|
||||||
playlists:
|
playlists: featuredPlaylistsQuery.pages
|
||||||
featuredPlaylistsQuery.pages.whereType<Page<PlaylistSimple>>(),
|
.whereType<Page<PlaylistSimple>>(),
|
||||||
title: 'Featured',
|
title: 'Featured',
|
||||||
hasNextPage: featuredPlaylistsQuery.hasNextPage,
|
hasNextPage: featuredPlaylistsQuery.hasNextPage,
|
||||||
onFetchMore: featuredPlaylistsQuery.fetchNext,
|
onFetchMore: featuredPlaylistsQuery.fetchNext,
|
||||||
@ -127,6 +129,8 @@ class PersonalizedPage extends HookConsumerWidget {
|
|||||||
onFetchMore: newReleases.fetchNext,
|
onFetchMore: newReleases.fetchNext,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import 'package:spotify/spotify.dart';
|
|||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/lyrics/zoom_controls.dart';
|
import 'package:spotube/components/lyrics/zoom_controls.dart';
|
||||||
import 'package:spotube/components/shared/shimmers/shimmer_lyrics.dart';
|
import 'package:spotube/components/shared/shimmers/shimmer_lyrics.dart';
|
||||||
import 'package:spotube/components/shared/spotube_marquee_text.dart';
|
|
||||||
import 'package:spotube/hooks/use_auto_scroll_controller.dart';
|
import 'package:spotube/hooks/use_auto_scroll_controller.dart';
|
||||||
import 'package:spotube/hooks/use_breakpoints.dart';
|
import 'package:spotube/hooks/use_breakpoints.dart';
|
||||||
import 'package:spotube/hooks/use_synced_lyrics.dart';
|
import 'package:spotube/hooks/use_synced_lyrics.dart';
|
||||||
@ -69,17 +68,15 @@ class SyncedLyrics extends HookConsumerWidget {
|
|||||||
: textTheme.headlineMedium?.copyWith(fontSize: 25))
|
: textTheme.headlineMedium?.copyWith(fontSize: 25))
|
||||||
?.copyWith(color: palette.titleTextColor);
|
?.copyWith(color: palette.titleTextColor);
|
||||||
|
|
||||||
return HookBuilder(builder: (context) {
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
if (isModal != true)
|
if (isModal != true)
|
||||||
Center(
|
Center(
|
||||||
child: SpotubeMarqueeText(
|
child: Text(
|
||||||
text: playlist?.activeTrack.name ?? "Not Playing",
|
playlist?.activeTrack.name ?? "Not Playing",
|
||||||
style: headlineTextStyle,
|
style: headlineTextStyle,
|
||||||
isHovering: true,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (isModal != true)
|
if (isModal != true)
|
||||||
@ -181,6 +178,5 @@ class SyncedLyrics extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import 'package:spotube/collections/spotube_icons.dart';
|
|||||||
import 'package:spotube/components/player/player_actions.dart';
|
import 'package:spotube/components/player/player_actions.dart';
|
||||||
import 'package:spotube/components/player/player_controls.dart';
|
import 'package:spotube/components/player/player_controls.dart';
|
||||||
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||||
import 'package:spotube/components/shared/spotube_marquee_text.dart';
|
|
||||||
import 'package:spotube/components/shared/image/universal_image.dart';
|
import 'package:spotube/components/shared/image/universal_image.dart';
|
||||||
import 'package:spotube/hooks/use_breakpoints.dart';
|
import 'package:spotube/hooks/use_breakpoints.dart';
|
||||||
import 'package:spotube/hooks/use_custom_status_bar_color.dart';
|
import 'package:spotube/hooks/use_custom_status_bar_color.dart';
|
||||||
@ -93,8 +92,8 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 30,
|
height: 30,
|
||||||
child: SpotubeMarqueeText(
|
child: Text(
|
||||||
text: currentTrack?.name ?? "Not playing",
|
currentTrack?.name ?? "Not playing",
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.headlineSmall
|
.headlineSmall
|
||||||
@ -102,7 +101,6 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: paletteColor.titleTextColor,
|
color: paletteColor.titleTextColor,
|
||||||
),
|
),
|
||||||
isHovering: true,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (isLocalTrack)
|
if (isLocalTrack)
|
||||||
|
@ -35,16 +35,20 @@ class CategoryQueries {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
InfiniteQuery<Page<PlaylistSimple>, dynamic, int> playlistsOf(
|
InfiniteQuery<Page<PlaylistSimple?>, dynamic, int> playlistsOf(
|
||||||
WidgetRef ref,
|
WidgetRef ref,
|
||||||
String category,
|
String category,
|
||||||
) {
|
) {
|
||||||
return useSpotifyInfiniteQuery<Page<PlaylistSimple>, dynamic, int>(
|
return useSpotifyInfiniteQuery<Page<PlaylistSimple?>, dynamic, int>(
|
||||||
"category-playlists/$category",
|
"category-playlists/$category",
|
||||||
(pageParam, spotify) async {
|
(pageParam, spotify) async {
|
||||||
final playlists = await spotify.playlists
|
final playlists = await Pages<PlaylistSimple?>(
|
||||||
.getByCategoryId(category)
|
spotify,
|
||||||
.getPage(5, pageParam);
|
"v1/browse/categories/$category/playlists",
|
||||||
|
(json) => json == null ? null : PlaylistSimple.fromJson(json),
|
||||||
|
'playlists',
|
||||||
|
(json) => PlaylistsFeatured.fromJson(json),
|
||||||
|
).getPage(5, pageParam);
|
||||||
|
|
||||||
return playlists;
|
return playlists;
|
||||||
},
|
},
|
||||||
|
16
pubspec.lock
16
pubspec.lock
@ -490,14 +490,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.0"
|
||||||
fading_edge_scrollview:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: fading_edge_scrollview
|
|
||||||
sha256: c25c2231652ce774cc31824d0112f11f653881f43d7f5302c05af11942052031
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.0.0"
|
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -929,14 +921,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.0"
|
version: "6.0.0"
|
||||||
marquee:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: marquee
|
|
||||||
sha256: "4b5243d2804373bdc25fc93d42c3b402d6ec1f4ee8d0bb72276edd04ae7addb8"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.2.3"
|
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -51,7 +51,6 @@ dependencies:
|
|||||||
json_annotation: ^4.8.0
|
json_annotation: ^4.8.0
|
||||||
json_serializable: ^6.6.0
|
json_serializable: ^6.6.0
|
||||||
logger: ^1.1.0
|
logger: ^1.1.0
|
||||||
marquee: ^2.2.3
|
|
||||||
metadata_god: ^0.3.2
|
metadata_god: ^0.3.2
|
||||||
mime: ^1.0.2
|
mime: ^1.0.2
|
||||||
package_info_plus: ^3.0.2
|
package_info_plus: ^3.0.2
|
||||||
|
Loading…
Reference in New Issue
Block a user