feat(playlist,album page): play and shuffle take full width on smaller screens, add new xs breakpoint

This commit is contained in:
Kingkor Roy Tirtho 2023-06-18 12:07:26 +06:00
parent 7a8bd92104
commit dce1b88694
25 changed files with 276 additions and 177 deletions

View File

@ -53,7 +53,7 @@ class AlbumCard extends HookConsumerWidget {
[playlistNotifier, query?.data, album.tracks], [playlistNotifier, query?.data, album.tracks],
); );
final int marginH = final int marginH =
useBreakpointValue(sm: 10, md: 15, lg: 20, xl: 20, xxl: 20); useBreakpointValue(xs: 10, sm: 10, md: 15, lg: 20, xl: 20, xxl: 20);
final updating = useState(false); final updating = useState(false);
final spotify = ref.watch(spotifyProvider); final spotify = ref.watch(spotifyProvider);

View File

@ -35,6 +35,7 @@ class ArtistCard extends HookConsumerWidget {
final radius = BorderRadius.circular(15); final radius = BorderRadius.circular(15);
final double size = useBreakpointValue<double>( final double size = useBreakpointValue<double>(
xs: 130,
sm: 130, sm: 130,
md: 150, md: 150,
others: 170, others: 170,

View File

@ -188,7 +188,7 @@ class _MultiSelectDialog<T> extends HookWidget {
return AlertDialog( return AlertDialog(
scrollable: true, scrollable: true,
title: dialogTitle ?? const Text('Select'), title: dialogTitle ?? const Text('Select'),
contentPadding: mediaQuery.isSm ? const EdgeInsets.all(16) : null, contentPadding: mediaQuery.mdAndUp ? null : const EdgeInsets.all(16),
insetPadding: const EdgeInsets.all(16), insetPadding: const EdgeInsets.all(16),
actions: [ actions: [
OutlinedButton( OutlinedButton(

View File

@ -24,6 +24,7 @@ class UserAlbums extends HookConsumerWidget {
final albumsQuery = useQueries.album.ofMine(ref); final albumsQuery = useQueries.album.ofMine(ref);
final spacing = useBreakpointValue<double>( final spacing = useBreakpointValue<double>(
xs: 0,
sm: 0, sm: 0,
others: 20, others: 20,
); );

View File

@ -39,7 +39,7 @@ class PlayerTrackDetails extends HookConsumerWidget {
), ),
), ),
), ),
if (mediaQuery.isSm || mediaQuery.isMd) if (mediaQuery.mdAndDown)
Flexible( Flexible(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,

View File

@ -121,7 +121,7 @@ class PlaylistCreateDialogButton extends HookConsumerWidget {
final mediaQuery = MediaQuery.of(context); final mediaQuery = MediaQuery.of(context);
final spotify = ref.watch(spotifyProvider); final spotify = ref.watch(spotifyProvider);
if (mediaQuery.isSm) { if (mediaQuery.smAndDown) {
return ElevatedButton( return ElevatedButton(
style: FilledButton.styleFrom( style: FilledButton.styleFrom(
foregroundColor: Theme.of(context).colorScheme.primary, foregroundColor: Theme.of(context).colorScheme.primary,

View File

@ -58,8 +58,7 @@ class BottomPlayer extends HookConsumerWidget {
// returning an empty non spacious Container as the overlay will take // returning an empty non spacious Container as the overlay will take
// place in the global overlay stack aka [_entries] // place in the global overlay stack aka [_entries]
if (layoutMode == LayoutMode.compact || if (layoutMode == LayoutMode.compact ||
((mediaQuery.isSm || mediaQuery.isMd) && ((mediaQuery.mdAndDown) && layoutMode == LayoutMode.adaptive)) {
layoutMode == LayoutMode.adaptive)) {
return PlayerOverlay(albumArt: albumArt); return PlayerOverlay(albumArt: albumArt);
} }

View File

@ -82,16 +82,17 @@ class Sidebar extends HookConsumerWidget {
}, [controller]); }, [controller]);
useEffect(() { useEffect(() {
if (!context.mounted) return;
if (mediaQuery.lgAndUp && !controller.extended) { if (mediaQuery.lgAndUp && !controller.extended) {
controller.setExtended(true); controller.setExtended(true);
} else if ((mediaQuery.isSm || mediaQuery.isMd) && controller.extended) { } else if (mediaQuery.mdAndDown && controller.extended) {
controller.setExtended(false); controller.setExtended(false);
} }
return null; return null;
}, [mediaQuery, controller]); }, [mediaQuery, controller]);
if (layoutMode == LayoutMode.compact || if (layoutMode == LayoutMode.compact ||
(mediaQuery.isSm && layoutMode == LayoutMode.adaptive)) { (mediaQuery.smAndDown && layoutMode == LayoutMode.adaptive)) {
return Scaffold(body: child); return Scaffold(body: child);
} }
@ -186,7 +187,7 @@ class SidebarHeader extends HookWidget {
final mediaQuery = MediaQuery.of(context); final mediaQuery = MediaQuery.of(context);
final theme = Theme.of(context); final theme = Theme.of(context);
if (mediaQuery.isSm || mediaQuery.isMd) { if (mediaQuery.mdAndDown) {
return Container( return Container(
height: 40, height: 40,
width: 40, width: 40,
@ -236,7 +237,7 @@ class SidebarFooter extends HookConsumerWidget {
final auth = ref.watch(AuthenticationNotifier.provider); final auth = ref.watch(AuthenticationNotifier.provider);
if (mediaQuery.isSm || mediaQuery.isMd) { if (mediaQuery.mdAndDown) {
return IconButton( return IconButton(
icon: const Icon(SpotubeIcons.settings), icon: const Icon(SpotubeIcons.settings),
onPressed: () => Sidebar.goToSettings(context), onPressed: () => Sidebar.goToSettings(context),

View File

@ -27,10 +27,11 @@ class AdaptiveListTile extends HookWidget {
return ListTile( return ListTile(
title: title, title: title,
subtitle: subtitle, subtitle: subtitle,
trailing: trailing: breakOn ?? mediaQuery.smAndDown
breakOn ?? mediaQuery.isSm ? null : trailing?.call(context, null), ? null
: trailing?.call(context, null),
leading: leading, leading: leading,
onTap: breakOn ?? mediaQuery.isSm onTap: breakOn ?? mediaQuery.smAndDown
? () { ? () {
onTap?.call(); onTap?.call();
showDialog( showDialog(

View File

@ -40,6 +40,7 @@ class PlaybuttonCard extends HookWidget {
final radius = BorderRadius.circular(15); final radius = BorderRadius.circular(15);
final double size = useBreakpointValue<double>( final double size = useBreakpointValue<double>(
xs: 130,
sm: 130, sm: 130,
md: 150, md: 150,
others: 170, others: 170,
@ -47,6 +48,7 @@ class PlaybuttonCard extends HookWidget {
170; 170;
final end = useBreakpointValue<double>( final end = useBreakpointValue<double>(
xs: 15,
sm: 15, sm: 15,
others: 20, others: 20,
) ?? ) ??

View File

@ -21,6 +21,7 @@ class ShimmerArtistProfile extends HookWidget {
shimmerTheme.shimmerBackgroundColor ?? Colors.grey; shimmerTheme.shimmerBackgroundColor ?? Colors.grey;
final avatarWidth = useBreakpointValue( final avatarWidth = useBreakpointValue(
xs: MediaQuery.of(context).size.width * 0.80,
sm: MediaQuery.of(context).size.width * 0.80, sm: MediaQuery.of(context).size.width * 0.80,
md: MediaQuery.of(context).size.width * 0.50, md: MediaQuery.of(context).size.width * 0.50,
lg: MediaQuery.of(context).size.width * 0.30, lg: MediaQuery.of(context).size.width * 0.30,

View File

@ -18,6 +18,7 @@ class ShimmerCategories extends HookWidget {
shimmerTheme.shimmerBackgroundColor ?? Colors.grey; shimmerTheme.shimmerBackgroundColor ?? Colors.grey;
final shimmerCount = useBreakpointValue( final shimmerCount = useBreakpointValue(
xs: 2,
sm: 2, sm: 2,
md: 3, md: 3,
lg: 3, lg: 3,

View File

@ -32,7 +32,7 @@ class ShimmerLyrics extends HookWidget {
if (mediaQuery.isMd) { if (mediaQuery.isMd) {
widthsCp.removeLast(); widthsCp.removeLast();
} }
if (mediaQuery.isSm) { if (mediaQuery.smAndDown) {
widthsCp.removeLast(); widthsCp.removeLast();
widthsCp.removeLast(); widthsCp.removeLast();
} }

View File

@ -86,6 +86,7 @@ class ShimmerPlaybuttonCard extends HookWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
final Size size = useBreakpointValue<Size>( final Size size = useBreakpointValue<Size>(
xs: const Size(130, 200),
sm: const Size(130, 200), sm: const Size(130, 200),
md: const Size(150, 220), md: const Size(150, 220),
others: const Size(170, 240), others: const Size(170, 240),

View File

@ -18,6 +18,7 @@ class ThemedButtonsTabBar extends HookWidget implements PreferredSizeWidget {
); );
final breakpoint = useBreakpointValue( final breakpoint = useBreakpointValue(
xs: 85.0,
sm: 85.0, sm: 85.0,
md: 35.0, md: 35.0,
others: 0.0, others: 0.0,

View File

@ -0,0 +1,195 @@
import 'dart:ui';
import 'package:fl_query/fl_query.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:palette_generator/palette_generator.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/album/album_card.dart';
import 'package:spotube/components/shared/image/universal_image.dart';
import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/extensions/context.dart';
class TrackCollectionHeading<T> extends HookConsumerWidget {
final String title;
final String? description;
final String titleImage;
final List<Widget> buttons;
final AlbumSimple? album;
final Query<List<TrackSimple>, T> tracksSnapshot;
final bool isPlaying;
final void Function([Track? currentTrack]) onPlay;
final void Function([Track? currentTrack]) onShuffledPlay;
final PaletteColor? color;
const TrackCollectionHeading({
Key? key,
required this.title,
required this.titleImage,
required this.buttons,
required this.tracksSnapshot,
required this.isPlaying,
required this.onPlay,
required this.onShuffledPlay,
required this.color,
this.description,
this.album,
}) : super(key: key);
@override
Widget build(BuildContext context, ref) {
final theme = Theme.of(context);
return LayoutBuilder(
builder: (context, constrains) {
return DecoratedBox(
decoration: BoxDecoration(
image: DecorationImage(
image: UniversalImage.imageProvider(titleImage),
fit: BoxFit.cover,
),
),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.black45,
theme.colorScheme.surface,
],
begin: const FractionalOffset(0, 0),
end: const FractionalOffset(0, 1),
tileMode: TileMode.clamp,
),
),
child: Material(
type: MaterialType.transparency,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20,
),
child: Flex(
direction:
constrains.mdAndDown ? Axis.vertical : Axis.horizontal,
mainAxisAlignment: MainAxisAlignment.center,
children: [
ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 200),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: UniversalImage(
path: titleImage,
placeholder: Assets.albumPlaceholder.path,
),
),
),
const SizedBox(width: 10, height: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
Text(
title,
style: theme.textTheme.titleLarge!.copyWith(
color: Colors.white,
fontWeight: FontWeight.w600,
),
),
if (album != null)
Text(
"${AlbumType.from(album?.albumType).formatted}${context.l10n.released}${DateTime.tryParse(
album?.releaseDate ?? "",
)?.year}",
style: theme.textTheme.titleMedium!.copyWith(
color: Colors.white,
fontWeight: FontWeight.normal,
),
),
if (description != null)
ConstrainedBox(
constraints: BoxConstraints(
maxWidth: constrains.mdAndDown ? 400 : 300,
),
child: Text(
description!,
style: const TextStyle(color: Colors.white),
maxLines: 2,
overflow: TextOverflow.fade,
),
),
const SizedBox(height: 10),
IconTheme(
data: theme.iconTheme.copyWith(
color: Colors.white,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: buttons,
),
),
const SizedBox(height: 10),
ConstrainedBox(
constraints: BoxConstraints(
maxWidth: constrains.mdAndDown ? 400 : 300,
),
child: Row(
mainAxisSize: constrains.smAndUp
? MainAxisSize.min
: MainAxisSize.min,
children: [
Expanded(
child: FilledButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: color?.color,
),
label: Text(context.l10n.shuffle),
icon: const Icon(SpotubeIcons.shuffle),
onPressed:
tracksSnapshot.data == null || isPlaying
? null
: onShuffledPlay,
),
),
const SizedBox(width: 10),
Expanded(
child: FilledButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: color?.color,
foregroundColor: color?.bodyTextColor,
),
onPressed: tracksSnapshot.data != null
? onPlay
: null,
icon: Icon(
isPlaying
? SpotubeIcons.stop
: SpotubeIcons.play,
),
label: Text(
isPlaying
? context.l10n.stop
: context.l10n.play,
),
),
),
],
),
),
],
)
],
),
),
),
),
),
);
},
);
}
}

View File

@ -1,16 +1,12 @@
import 'dart:ui';
import 'package:fl_query/fl_query.dart'; import 'package:fl_query/fl_query.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.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/album/album_card.dart';
import 'package:spotube/components/shared/shimmers/shimmer_track_tile.dart'; import 'package:spotube/components/shared/shimmers/shimmer_track_tile.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/image/universal_image.dart'; import 'package:spotube/components/shared/track_table/track_collection_view/track_collection_heading.dart';
import 'package:spotube/components/shared/track_table/tracks_table_view.dart'; import 'package:spotube/components/shared/track_table/tracks_table_view.dart';
import 'package:spotube/extensions/context.dart'; import 'package:spotube/extensions/context.dart';
import 'package:spotube/hooks/use_custom_status_bar_color.dart'; import 'package:spotube/hooks/use_custom_status_bar_color.dart';
@ -31,8 +27,8 @@ class TrackCollectionView<T> extends HookConsumerWidget {
final String titleImage; final String titleImage;
final bool isPlaying; final bool isPlaying;
final void Function([Track? currentTrack]) onPlay; final void Function([Track? currentTrack]) onPlay;
final void Function() onAddToQueue;
final void Function([Track? currentTrack]) onShuffledPlay; final void Function([Track? currentTrack]) onShuffledPlay;
final void Function() onAddToQueue;
final void Function() onShare; final void Function() onShare;
final Widget? heartBtn; final Widget? heartBtn;
final AlbumSimple? album; final AlbumSimple? album;
@ -187,145 +183,17 @@ class TrackCollectionView<T> extends HookConsumerWidget {
: null, : null,
centerTitle: true, centerTitle: true,
flexibleSpace: FlexibleSpaceBar( flexibleSpace: FlexibleSpaceBar(
background: DecoratedBox( background: TrackCollectionHeading<T>(
decoration: BoxDecoration( color: color,
image: DecorationImage( title: title,
image: UniversalImage.imageProvider(titleImage), description: description,
fit: BoxFit.cover, titleImage: titleImage,
), isPlaying: isPlaying,
), onPlay: onPlay,
child: BackdropFilter( onShuffledPlay: onShuffledPlay,
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), tracksSnapshot: tracksSnapshot,
child: DecoratedBox( buttons: buttons,
decoration: BoxDecoration( album: album,
gradient: LinearGradient(
colors: [
Colors.black45,
theme.colorScheme.surface,
],
begin: const FractionalOffset(0, 0),
end: const FractionalOffset(0, 1),
tileMode: TileMode.clamp,
),
),
child: Material(
type: MaterialType.transparency,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20,
),
child: Wrap(
spacing: 20,
runSpacing: 20,
crossAxisAlignment: WrapCrossAlignment.center,
alignment: WrapAlignment.center,
runAlignment: WrapAlignment.center,
children: [
Container(
constraints:
const BoxConstraints(maxHeight: 200),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: UniversalImage(
path: titleImage,
placeholder:
Assets.albumPlaceholder.path,
),
),
),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: theme.textTheme.titleLarge!
.copyWith(
color: Colors.white,
fontWeight: FontWeight.w600,
),
),
if (album != null)
Text(
"${AlbumType.from(album?.albumType).formatted}${context.l10n.released}${DateTime.tryParse(
album?.releaseDate ?? "",
)?.year}",
style: theme.textTheme.titleMedium!
.copyWith(
color: Colors.white,
fontWeight: FontWeight.normal,
),
),
if (description != null)
Text(
description!,
style: const TextStyle(
color: Colors.white),
maxLines: 2,
overflow: TextOverflow.fade,
),
const SizedBox(height: 10),
IconTheme(
data: theme.iconTheme.copyWith(
color: Colors.white,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: buttons,
),
),
const SizedBox(height: 10),
Row(
mainAxisSize: MainAxisSize.min,
children: [
FilledButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: color?.color,
),
label: Text(context.l10n.shuffle),
icon: const Icon(
SpotubeIcons.shuffle),
onPressed:
tracksSnapshot.data == null ||
isPlaying
? null
: onShuffledPlay,
),
const SizedBox(width: 10),
FilledButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: color?.color,
foregroundColor:
color?.bodyTextColor,
),
onPressed:
tracksSnapshot.data != null
? onPlay
: null,
icon: Icon(
isPlaying
? SpotubeIcons.stop
: SpotubeIcons.play,
),
label: Text(
isPlaying
? context.l10n.stop
: context.l10n.play,
),
),
],
),
],
)
],
),
),
),
),
),
), ),
), ),
), ),
@ -361,7 +229,7 @@ class TrackCollectionView<T> extends HookConsumerWidget {
// scroll the flexible space // scroll the flexible space
// to allow more space for search results // to allow more space for search results
controller.animateTo( controller.animateTo(
390, 330,
duration: const Duration(milliseconds: 200), duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut, curve: Curves.easeInOut,
); );

View File

@ -61,7 +61,7 @@ class TrackTile extends HookConsumerWidget {
return LayoutBuilder(builder: (context, constrains) { return LayoutBuilder(builder: (context, constrains) {
return HoverBuilder( return HoverBuilder(
permanentState: isPlaying || constrains.isSm ? true : null, permanentState: isPlaying || constrains.smAndDown ? true : null,
builder: (context, isHovering) { builder: (context, isHovering) {
return ListTile( return ListTile(
selected: isPlaying, selected: isPlaying,
@ -89,7 +89,7 @@ class TrackTile extends HookConsumerWidget {
), ),
), ),
) )
else if (constrains.isSm) else if (constrains.smAndDown)
const SizedBox(width: 16), const SizedBox(width: 16),
if (onChanged != null) if (onChanged != null)
Checkbox.adaptive( Checkbox.adaptive(

View File

@ -390,6 +390,7 @@ class TracksTableView extends HookConsumerWidget {
if (isSliver) { if (isSliver) {
return SliverSafeArea( return SliverSafeArea(
top: false,
sliver: SliverList(delegate: SliverChildListDelegate(children)), sliver: SliverList(delegate: SliverChildListDelegate(children)),
); );
} }

View File

@ -1,25 +1,39 @@
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
extension ContainerBreakpoints on BoxConstraints { extension ContainerBreakpoints on BoxConstraints {
bool get isSm => biggest.width <= 640; bool get isXs => biggest.width <= 480;
bool get isSm => biggest.width > 480 && biggest.width <= 640;
bool get isMd => biggest.width > 640 && biggest.width <= 768; bool get isMd => biggest.width > 640 && biggest.width <= 768;
bool get isLg => biggest.width > 768 && biggest.width <= 1024; bool get isLg => biggest.width > 768 && biggest.width <= 1024;
bool get isXl => biggest.width > 1024 && biggest.width <= 1280; bool get isXl => biggest.width > 1024 && biggest.width <= 1280;
bool get is2Xl => biggest.width > 1280; bool get is2Xl => biggest.width > 1280;
bool get smAndUp => isSm || isMd || isLg || isXl || is2Xl;
bool get mdAndUp => isMd || isLg || isXl || is2Xl; bool get mdAndUp => isMd || isLg || isXl || is2Xl;
bool get lgAndUp => isLg || isXl || is2Xl; bool get lgAndUp => isLg || isXl || is2Xl;
bool get xlAndUp => isXl || is2Xl; bool get xlAndUp => isXl || is2Xl;
bool get smAndDown => isXs || isSm;
bool get mdAndDown => isXs || isSm || isMd;
bool get lgAndDown => isXs || isSm || isMd || isLg;
bool get xlAndDown => isXs || isSm || isMd || isLg || isXl;
} }
extension ScreenBreakpoints on MediaQueryData { extension ScreenBreakpoints on MediaQueryData {
bool get isSm => size.width <= 640; bool get isXs => size.width <= 480;
bool get isSm => size.width > 480 && size.width <= 640;
bool get isMd => size.width > 640 && size.width <= 768; bool get isMd => size.width > 640 && size.width <= 768;
bool get isLg => size.width > 768 && size.width <= 1024; bool get isLg => size.width > 768 && size.width <= 1024;
bool get isXl => size.width > 1024 && size.width <= 1280; bool get isXl => size.width > 1024 && size.width <= 1280;
bool get is2Xl => size.width > 1280; bool get is2Xl => size.width > 1280;
bool get smAndUp => isSm || isMd || isLg || isXl || is2Xl;
bool get mdAndUp => isMd || isLg || isXl || is2Xl; bool get mdAndUp => isMd || isLg || isXl || is2Xl;
bool get lgAndUp => isLg || isXl || is2Xl; bool get lgAndUp => isLg || isXl || is2Xl;
bool get xlAndUp => isXl || is2Xl; bool get xlAndUp => isXl || is2Xl;
bool get smAndDown => isXs || isSm;
bool get mdAndDown => isXs || isSm || isMd;
bool get lgAndDown => isXs || isSm || isMd || isLg;
bool get xlAndDown => isXs || isSm || isMd || isLg || isXl;
} }

View File

@ -3,6 +3,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:spotube/extensions/constrains.dart'; import 'package:spotube/extensions/constrains.dart';
T useBreakpointValue<T>({ T useBreakpointValue<T>({
T? xs,
T? sm, T? sm,
T? md, T? md,
T? lg, T? lg,
@ -10,8 +11,12 @@ T useBreakpointValue<T>({
T? xxl, T? xxl,
T? others, T? others,
}) { }) {
final isSomeNull = final isSomeNull = xs == null ||
sm == null || md == null || lg == null || xl == null || xxl == null; sm == null ||
md == null ||
lg == null ||
xl == null ||
xxl == null;
assert( assert(
(isSomeNull && others != null) || (!isSomeNull && others == null), (isSomeNull && others != null) || (!isSomeNull && others == null),
'You must provide a value for all breakpoints or a default value for others', 'You must provide a value for all breakpoints or a default value for others',
@ -20,7 +25,9 @@ T useBreakpointValue<T>({
final mediaQuery = MediaQuery.of(context); final mediaQuery = MediaQuery.of(context);
if (isSomeNull) { if (isSomeNull) {
if (mediaQuery.isSm) { if (mediaQuery.isXs) {
return xs ?? others!;
} else if (mediaQuery.isSm) {
return sm ?? others!; return sm ?? others!;
} else if (mediaQuery.isMd) { } else if (mediaQuery.isMd) {
return md ?? others!; return md ?? others!;
@ -32,7 +39,9 @@ T useBreakpointValue<T>({
return lg ?? others!; return lg ?? others!;
} }
} else { } else {
if (mediaQuery.isSm) { if (mediaQuery.isXs) {
return xs;
} else if (mediaQuery.isSm) {
return sm; return sm;
} else if (mediaQuery.isMd) { } else if (mediaQuery.isMd) {
return md; return md;

View File

@ -4,7 +4,7 @@ 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/shared/heart_button.dart'; import 'package:spotube/components/shared/heart_button.dart';
import 'package:spotube/components/shared/track_table/track_collection_view.dart'; import 'package:spotube/components/shared/track_table/track_collection_view/track_collection_view.dart';
import 'package:spotube/components/shared/track_table/tracks_table_view.dart'; import 'package:spotube/components/shared/track_table/tracks_table_view.dart';
import 'package:spotube/extensions/constrains.dart'; import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart'; import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
@ -67,7 +67,7 @@ class AlbumPage extends HookConsumerWidget {
tracksSnapshot: tracksSnapshot, tracksSnapshot: tracksSnapshot,
album: album, album: album,
routePath: "/album/${album.id}", routePath: "/album/${album.id}",
bottomSpace: mediaQuery.isSm || mediaQuery.isMd, bottomSpace: mediaQuery.mdAndDown,
onPlay: ([track]) { onPlay: ([track]) {
if (tracksSnapshot.hasData) { if (tracksSnapshot.hasData) {
if (!isAlbumPlaying) { if (!isAlbumPlaying) {

View File

@ -39,6 +39,7 @@ class ArtistPage extends HookConsumerWidget {
final scaffoldMessenger = ScaffoldMessenger.of(context); final scaffoldMessenger = ScaffoldMessenger.of(context);
final textTheme = theme.textTheme; final textTheme = theme.textTheme;
final chipTextVariant = useBreakpointValue( final chipTextVariant = useBreakpointValue(
xs: textTheme.bodySmall,
sm: textTheme.bodySmall, sm: textTheme.bodySmall,
md: textTheme.bodyMedium, md: textTheme.bodyMedium,
lg: textTheme.bodyLarge, lg: textTheme.bodyLarge,
@ -49,6 +50,7 @@ class ArtistPage extends HookConsumerWidget {
final mediaQuery = MediaQuery.of(context); final mediaQuery = MediaQuery.of(context);
final avatarWidth = useBreakpointValue( final avatarWidth = useBreakpointValue(
xs: mediaQuery.size.width * 0.50,
sm: mediaQuery.size.width * 0.50, sm: mediaQuery.size.width * 0.50,
md: mediaQuery.size.width * 0.40, md: mediaQuery.size.width * 0.40,
lg: mediaQuery.size.width * 0.18, lg: mediaQuery.size.width * 0.18,
@ -155,7 +157,7 @@ class ArtistPage extends HookConsumerWidget {
), ),
Text( Text(
data.name!, data.name!,
style: mediaQuery.isSm style: mediaQuery.smAndDown
? textTheme.headlineSmall ? textTheme.headlineSmall
: textTheme.headlineMedium, : textTheme.headlineMedium,
), ),
@ -166,8 +168,9 @@ class ArtistPage extends HookConsumerWidget {
), ),
), ),
style: textTheme.bodyMedium?.copyWith( style: textTheme.bodyMedium?.copyWith(
fontWeight: fontWeight: mediaQuery.mdAndUp
mediaQuery.isSm ? null : FontWeight.bold, ? FontWeight.bold
: null,
), ),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),

View File

@ -35,7 +35,7 @@ class DesktopLoginPage extends HookConsumerWidget {
children: [ children: [
Assets.spotubeLogoPng.image( Assets.spotubeLogoPng.image(
width: MediaQuery.of(context).size.width * width: MediaQuery.of(context).size.width *
(mediaQuery.isSm || mediaQuery.isMd ? .5 : .3), (mediaQuery.mdAndDown ? .5 : .3),
), ),
Text( Text(
context.l10n.add_spotify_credentials, context.l10n.add_spotify_credentials,

View File

@ -2,7 +2,7 @@ import 'package:flutter/services.dart';
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:spotube/components/shared/heart_button.dart'; import 'package:spotube/components/shared/heart_button.dart';
import 'package:spotube/components/shared/track_table/track_collection_view.dart'; import 'package:spotube/components/shared/track_table/track_collection_view/track_collection_view.dart';
import 'package:spotube/components/shared/track_table/tracks_table_view.dart'; import 'package:spotube/components/shared/track_table/tracks_table_view.dart';
import 'package:spotube/extensions/constrains.dart'; import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/models/logger.dart'; import 'package:spotube/models/logger.dart';
@ -99,7 +99,7 @@ class PlaylistView extends HookConsumerWidget {
playlistNotifier.addTracks(tracksSnapshot.data!); playlistNotifier.addTracks(tracksSnapshot.data!);
} }
}, },
bottomSpace: mediaQuery.isSm || mediaQuery.isMd, bottomSpace: mediaQuery.mdAndDown,
showShare: playlist.id != "user-liked-tracks", showShare: playlist.id != "user-liked-tracks",
routePath: "/playlist/${playlist.id}", routePath: "/playlist/${playlist.id}",
onShare: () { onShare: () {