fix: track_tile active and blacklist color, playbutton card action positioning

This commit is contained in:
Kingkor Roy Tirtho 2023-03-23 21:29:20 +06:00
parent 20c424c77f
commit 3f5a1b9587
8 changed files with 254 additions and 185 deletions

View File

@ -50,7 +50,11 @@ class UserAlbums extends HookConsumerWidget {
return const AnonymousFallback(); return const AnonymousFallback();
} }
if (albumsQuery.isLoading || !albumsQuery.hasData) { if (albumsQuery.isLoading || !albumsQuery.hasData) {
return const Center(child: ShimmerPlaybuttonCard(count: 7)); return Container(
alignment: Alignment.topLeft,
padding: const EdgeInsets.all(16.0),
child: const ShimmerPlaybuttonCard(count: 7),
);
} }
return RefreshIndicator( return RefreshIndicator(
@ -63,6 +67,7 @@ class UserAlbums extends HookConsumerWidget {
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: SafeArea( child: SafeArea(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
TextField( TextField(
onChanged: (value) => searchText.value = value, onChanged: (value) => searchText.value = value,

View File

@ -7,6 +7,7 @@ import 'package:spotube/collections/spotube_icons.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'; import 'package:spotube/hooks/use_breakpoint_value.dart';
import 'package:spotube/hooks/use_brightness_value.dart'; import 'package:spotube/hooks/use_brightness_value.dart';
import 'package:spotube/utils/platform.dart';
class PlaybuttonCard extends HookWidget { class PlaybuttonCard extends HookWidget {
final void Function()? onTap; final void Function()? onTap;
@ -34,6 +35,7 @@ class PlaybuttonCard extends HookWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final textsKey = useMemoized(() => GlobalKey(), []);
final theme = Theme.of(context); final theme = Theme.of(context);
final radius = BorderRadius.circular(15); final radius = BorderRadius.circular(15);
@ -41,15 +43,36 @@ class PlaybuttonCard extends HookWidget {
sm: 130, sm: 130,
md: 150, md: 150,
others: 170, others: 170,
); ) ??
170;
final end = useBreakpointValue<double>( final end = useBreakpointValue<double>(
sm: 5, sm: 15,
md: 7, others: 20,
others: 10, ) ??
20;
final textsHeight = useState(
(textsKey.currentContext?.findRenderObject() as RenderBox?)
?.size
.height ??
110.00,
); );
return Container( useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((_) {
textsHeight.value =
(textsKey.currentContext?.findRenderObject() as RenderBox?)
?.size
.height ??
textsHeight.value;
});
return null;
}, [textsKey]);
return Stack(
children: [
Container(
constraints: BoxConstraints(maxWidth: size), constraints: BoxConstraints(maxWidth: size),
margin: margin, margin: margin,
child: Material( child: Material(
@ -70,16 +93,12 @@ class PlaybuttonCard extends HookWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Stack( Padding(
clipBehavior: Clip.none, padding: const EdgeInsets.only(
children: [
Container(
margin: const EdgeInsets.only(
left: 8, left: 8,
right: 8, right: 8,
top: 8, top: 8,
), ),
constraints: BoxConstraints(maxHeight: size),
child: ClipRRect( child: ClipRRect(
borderRadius: radius, borderRadius: radius,
child: UniversalImage( child: UniversalImage(
@ -88,10 +107,45 @@ class PlaybuttonCard extends HookWidget {
), ),
), ),
), ),
Positioned.directional( Column(
textDirection: TextDirection.ltr, key: textsKey,
end: end, crossAxisAlignment: CrossAxisAlignment.start,
bottom: -size * .15, children: [
const SizedBox(height: 15),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: AutoSizeText(
title,
maxLines: 1,
minFontSize: theme.textTheme.bodyMedium!.fontSize!,
overflow: TextOverflow.ellipsis,
),
),
if (description != null)
Padding(
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),
],
),
],
),
),
),
),
AnimatedPositioned(
duration: const Duration(milliseconds: 300),
right: end,
bottom: textsHeight.value - (kIsMobile ? 5 : 10),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@ -115,8 +169,7 @@ class PlaybuttonCard extends HookWidget {
icon: isLoading icon: isLoading
? SizedBox.fromSize( ? SizedBox.fromSize(
size: const Size.square(15), size: const Size.square(15),
child: const CircularProgressIndicator( child: const CircularProgressIndicator(strokeWidth: 2),
strokeWidth: 2),
) )
: isPlaying : isPlaying
? const Icon(SpotubeIcons.pause) ? const Icon(SpotubeIcons.pause)
@ -127,38 +180,6 @@ class PlaybuttonCard extends HookWidget {
), ),
), ),
], ],
),
const SizedBox(height: 15),
Flexible(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: AutoSizeText(
title,
maxLines: 1,
minFontSize: theme.textTheme.bodyMedium!.fontSize!,
overflow: TextOverflow.ellipsis,
),
),
),
if (description != null)
Flexible(
child: Padding(
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),
],
),
),
),
); );
} }
} }

View File

@ -26,7 +26,8 @@ class ShimmerArtistProfile extends HookWidget {
lg: MediaQuery.of(context).size.width * 0.30, lg: MediaQuery.of(context).size.width * 0.30,
xl: MediaQuery.of(context).size.width * 0.30, xl: MediaQuery.of(context).size.width * 0.30,
xxl: MediaQuery.of(context).size.width * 0.30, xxl: MediaQuery.of(context).size.width * 0.30,
); ) ??
0;
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,

View File

@ -51,7 +51,7 @@ class SortTracksDropdown extends StatelessWidget {
}, },
onSelected: onChanged, onSelected: onChanged,
tooltip: "Sort tracks", tooltip: "Sort tracks",
child: const Icon(SpotubeIcons.sort), icon: const Icon(SpotubeIcons.sort),
); );
} }
} }

View File

@ -100,12 +100,13 @@ class TrackCollectionView<T> extends HookConsumerWidget {
), ),
), ),
// play playlist // play playlist
IconButton( ElevatedButton(
style: IconButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: theme.colorScheme.primary, shape: const CircleBorder(),
backgroundColor: theme.colorScheme.inversePrimary,
), ),
onPressed: tracksSnapshot.data != null ? onPlay : null, onPressed: tracksSnapshot.data != null ? onPlay : null,
icon: Icon(isPlaying ? SpotubeIcons.stop : SpotubeIcons.play), child: Icon(isPlaying ? SpotubeIcons.stop : SpotubeIcons.play),
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
]; ];

View File

@ -179,18 +179,16 @@ class TrackTile extends HookConsumerWidget {
return AnimatedContainer( return AnimatedContainer(
duration: const Duration(milliseconds: 500), duration: const Duration(milliseconds: 500),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isBlackListed color: isActive
? Colors.red[100] ? theme.colorScheme.surfaceVariant.withOpacity(0.5)
: isActive
? theme.popupMenuTheme.color
: Colors.transparent, : Colors.transparent,
borderRadius: BorderRadius.circular(isActive ? 10 : 0), borderRadius: BorderRadius.circular(10),
), ),
child: Material( child: Material(
type: MaterialType.transparency, type: MaterialType.transparency,
child: Row( child: Row(
children: [ children: [
if (showCheck) if (showCheck && !isBlackListed)
Checkbox( Checkbox(
value: isChecked, value: isChecked,
onChanged: (s) => onCheckChange?.call(s), onChanged: (s) => onCheckChange?.call(s),
@ -222,22 +220,21 @@ class TrackTile extends HookConsumerWidget {
), ),
Padding( Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: IconButton( child: ElevatedButton(
icon: Icon( style: ElevatedButton.styleFrom(
playlist?.activeTrack.id == track.value.id backgroundColor: theme.colorScheme.inversePrimary,
? SpotubeIcons.pause shape: const CircleBorder(),
: SpotubeIcons.play,
color: Colors.white,
),
style: IconButton.styleFrom(
backgroundColor: theme.colorScheme.primary,
hoverColor: theme.colorScheme.primary.withOpacity(0.5),
), ),
onPressed: !isBlackListed onPressed: !isBlackListed
? () => onTrackPlayButtonPressed?.call( ? () => onTrackPlayButtonPressed?.call(
track.value, track.value,
) )
: null, : null,
child: Icon(
playlist?.activeTrack.id == track.value.id
? SpotubeIcons.pause
: SpotubeIcons.play,
),
), ),
), ),
Expanded( Expanded(

View File

@ -197,7 +197,7 @@ class TracksTableView extends HookConsumerWidget {
default: default:
} }
}, },
child: const Icon(SpotubeIcons.moreVertical), icon: const Icon(SpotubeIcons.moreVertical),
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
], ],
@ -205,12 +205,31 @@ class TracksTableView extends HookConsumerWidget {
...sortedTracks.asMap().entries.map((track) { ...sortedTracks.asMap().entries.map((track) {
String duration = String duration =
"${track.value.duration?.inMinutes.remainder(60)}:${PrimitiveUtils.zeroPadNumStr(track.value.duration?.inSeconds.remainder(60) ?? 0)}"; "${track.value.duration?.inMinutes.remainder(60)}:${PrimitiveUtils.zeroPadNumStr(track.value.duration?.inSeconds.remainder(60) ?? 0)}";
return InkWell( return Consumer(builder: (context, ref, _) {
onLongPress: () { final isBlackListed = ref.watch(
BlackListNotifier.provider.select(
(blacklist) => blacklist.contains(
BlacklistedElement.track(
track.value.id!, track.value.name!),
),
),
);
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: InkWell(
borderRadius: BorderRadius.circular(10),
onLongPress: isBlackListed
? null
: () {
showCheck.value = true; showCheck.value = true;
selected.value = [...selected.value, track.value.id!]; selected.value = [
...selected.value,
track.value.id!
];
}, },
onTap: () { onTap: isBlackListed
? null
: () {
if (showCheck.value) { if (showCheck.value) {
final alreadyChecked = final alreadyChecked =
selected.value.contains(track.value.id); selected.value.contains(track.value.id);
@ -219,7 +238,10 @@ class TracksTableView extends HookConsumerWidget {
.where((id) => id != track.value.id) .where((id) => id != track.value.id)
.toList(); .toList();
} else { } else {
selected.value = [...selected.value, track.value.id!]; selected.value = [
...selected.value,
track.value.id!
];
} }
} else { } else {
final isBlackListed = ref.read( final isBlackListed = ref.read(
@ -255,7 +277,9 @@ class TracksTableView extends HookConsumerWidget {
} }
}, },
), ),
),
); );
});
}).toList(), }).toList(),
]; ];

View File

@ -1,6 +1,6 @@
import 'package:spotube/hooks/use_breakpoints.dart'; import 'package:spotube/hooks/use_breakpoints.dart';
useBreakpointValue<T>({ T useBreakpointValue<T>({
T? sm, T? sm,
T? md, T? md,
T? lg, T? lg,
@ -8,17 +8,37 @@ useBreakpointValue<T>({
T? xxl, T? xxl,
T? others, T? others,
}) { }) {
final isSomeNull =
sm == null || md == null || lg == null || xl == null || xxl == null;
assert(
(isSomeNull && others != null) || (!isSomeNull && others == null),
'You must provide a value for all breakpoints or a default value for others',
);
final breakpoint = useBreakpoints(); final breakpoint = useBreakpoints();
if (isSomeNull) {
if (breakpoint.isSm) { if (breakpoint.isSm) {
return sm ?? others; return sm ?? others!;
} else if (breakpoint.isMd) { } else if (breakpoint.isMd) {
return md ?? others; return md ?? others!;
} else if (breakpoint.isXl) { } else if (breakpoint.isXl) {
return xl ?? others; return xl ?? others!;
} else if (breakpoint.isXxl) { } else if (breakpoint.isXxl) {
return xxl ?? others; return xxl ?? others!;
} else { } else {
return lg ?? others; return lg ?? others!;
}
} else {
if (breakpoint.isSm) {
return sm;
} else if (breakpoint.isMd) {
return md;
} else if (breakpoint.isXl) {
return xl;
} else if (breakpoint.isXxl) {
return xxl;
} else {
return lg;
}
} }
} }