mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 16:05:18 +00:00
feat(playlist,album page): play and shuffle take full width on smaller screens, add new xs breakpoint
This commit is contained in:
parent
7a8bd92104
commit
dce1b88694
@ -53,7 +53,7 @@ class AlbumCard extends HookConsumerWidget {
|
||||
[playlistNotifier, query?.data, album.tracks],
|
||||
);
|
||||
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 spotify = ref.watch(spotifyProvider);
|
||||
|
@ -35,6 +35,7 @@ class ArtistCard extends HookConsumerWidget {
|
||||
final radius = BorderRadius.circular(15);
|
||||
|
||||
final double size = useBreakpointValue<double>(
|
||||
xs: 130,
|
||||
sm: 130,
|
||||
md: 150,
|
||||
others: 170,
|
||||
|
@ -188,7 +188,7 @@ class _MultiSelectDialog<T> extends HookWidget {
|
||||
return AlertDialog(
|
||||
scrollable: true,
|
||||
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),
|
||||
actions: [
|
||||
OutlinedButton(
|
||||
|
@ -24,6 +24,7 @@ class UserAlbums extends HookConsumerWidget {
|
||||
final albumsQuery = useQueries.album.ofMine(ref);
|
||||
|
||||
final spacing = useBreakpointValue<double>(
|
||||
xs: 0,
|
||||
sm: 0,
|
||||
others: 20,
|
||||
);
|
||||
|
@ -39,7 +39,7 @@ class PlayerTrackDetails extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (mediaQuery.isSm || mediaQuery.isMd)
|
||||
if (mediaQuery.mdAndDown)
|
||||
Flexible(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
@ -121,7 +121,7 @@ class PlaylistCreateDialogButton extends HookConsumerWidget {
|
||||
final mediaQuery = MediaQuery.of(context);
|
||||
final spotify = ref.watch(spotifyProvider);
|
||||
|
||||
if (mediaQuery.isSm) {
|
||||
if (mediaQuery.smAndDown) {
|
||||
return ElevatedButton(
|
||||
style: FilledButton.styleFrom(
|
||||
foregroundColor: Theme.of(context).colorScheme.primary,
|
||||
|
@ -58,8 +58,7 @@ class BottomPlayer extends HookConsumerWidget {
|
||||
// returning an empty non spacious Container as the overlay will take
|
||||
// place in the global overlay stack aka [_entries]
|
||||
if (layoutMode == LayoutMode.compact ||
|
||||
((mediaQuery.isSm || mediaQuery.isMd) &&
|
||||
layoutMode == LayoutMode.adaptive)) {
|
||||
((mediaQuery.mdAndDown) && layoutMode == LayoutMode.adaptive)) {
|
||||
return PlayerOverlay(albumArt: albumArt);
|
||||
}
|
||||
|
||||
|
@ -82,16 +82,17 @@ class Sidebar extends HookConsumerWidget {
|
||||
}, [controller]);
|
||||
|
||||
useEffect(() {
|
||||
if (!context.mounted) return;
|
||||
if (mediaQuery.lgAndUp && !controller.extended) {
|
||||
controller.setExtended(true);
|
||||
} else if ((mediaQuery.isSm || mediaQuery.isMd) && controller.extended) {
|
||||
} else if (mediaQuery.mdAndDown && controller.extended) {
|
||||
controller.setExtended(false);
|
||||
}
|
||||
return null;
|
||||
}, [mediaQuery, controller]);
|
||||
|
||||
if (layoutMode == LayoutMode.compact ||
|
||||
(mediaQuery.isSm && layoutMode == LayoutMode.adaptive)) {
|
||||
(mediaQuery.smAndDown && layoutMode == LayoutMode.adaptive)) {
|
||||
return Scaffold(body: child);
|
||||
}
|
||||
|
||||
@ -186,7 +187,7 @@ class SidebarHeader extends HookWidget {
|
||||
final mediaQuery = MediaQuery.of(context);
|
||||
final theme = Theme.of(context);
|
||||
|
||||
if (mediaQuery.isSm || mediaQuery.isMd) {
|
||||
if (mediaQuery.mdAndDown) {
|
||||
return Container(
|
||||
height: 40,
|
||||
width: 40,
|
||||
@ -236,7 +237,7 @@ class SidebarFooter extends HookConsumerWidget {
|
||||
|
||||
final auth = ref.watch(AuthenticationNotifier.provider);
|
||||
|
||||
if (mediaQuery.isSm || mediaQuery.isMd) {
|
||||
if (mediaQuery.mdAndDown) {
|
||||
return IconButton(
|
||||
icon: const Icon(SpotubeIcons.settings),
|
||||
onPressed: () => Sidebar.goToSettings(context),
|
||||
|
@ -27,10 +27,11 @@ class AdaptiveListTile extends HookWidget {
|
||||
return ListTile(
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
trailing:
|
||||
breakOn ?? mediaQuery.isSm ? null : trailing?.call(context, null),
|
||||
trailing: breakOn ?? mediaQuery.smAndDown
|
||||
? null
|
||||
: trailing?.call(context, null),
|
||||
leading: leading,
|
||||
onTap: breakOn ?? mediaQuery.isSm
|
||||
onTap: breakOn ?? mediaQuery.smAndDown
|
||||
? () {
|
||||
onTap?.call();
|
||||
showDialog(
|
||||
|
@ -40,6 +40,7 @@ class PlaybuttonCard extends HookWidget {
|
||||
final radius = BorderRadius.circular(15);
|
||||
|
||||
final double size = useBreakpointValue<double>(
|
||||
xs: 130,
|
||||
sm: 130,
|
||||
md: 150,
|
||||
others: 170,
|
||||
@ -47,6 +48,7 @@ class PlaybuttonCard extends HookWidget {
|
||||
170;
|
||||
|
||||
final end = useBreakpointValue<double>(
|
||||
xs: 15,
|
||||
sm: 15,
|
||||
others: 20,
|
||||
) ??
|
||||
|
@ -21,6 +21,7 @@ class ShimmerArtistProfile extends HookWidget {
|
||||
shimmerTheme.shimmerBackgroundColor ?? Colors.grey;
|
||||
|
||||
final avatarWidth = useBreakpointValue(
|
||||
xs: MediaQuery.of(context).size.width * 0.80,
|
||||
sm: MediaQuery.of(context).size.width * 0.80,
|
||||
md: MediaQuery.of(context).size.width * 0.50,
|
||||
lg: MediaQuery.of(context).size.width * 0.30,
|
||||
|
@ -18,6 +18,7 @@ class ShimmerCategories extends HookWidget {
|
||||
shimmerTheme.shimmerBackgroundColor ?? Colors.grey;
|
||||
|
||||
final shimmerCount = useBreakpointValue(
|
||||
xs: 2,
|
||||
sm: 2,
|
||||
md: 3,
|
||||
lg: 3,
|
||||
|
@ -32,7 +32,7 @@ class ShimmerLyrics extends HookWidget {
|
||||
if (mediaQuery.isMd) {
|
||||
widthsCp.removeLast();
|
||||
}
|
||||
if (mediaQuery.isSm) {
|
||||
if (mediaQuery.smAndDown) {
|
||||
widthsCp.removeLast();
|
||||
widthsCp.removeLast();
|
||||
}
|
||||
|
@ -86,6 +86,7 @@ class ShimmerPlaybuttonCard extends HookWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final Size size = useBreakpointValue<Size>(
|
||||
xs: const Size(130, 200),
|
||||
sm: const Size(130, 200),
|
||||
md: const Size(150, 220),
|
||||
others: const Size(170, 240),
|
||||
|
@ -18,6 +18,7 @@ class ThemedButtonsTabBar extends HookWidget implements PreferredSizeWidget {
|
||||
);
|
||||
|
||||
final breakpoint = useBreakpointValue(
|
||||
xs: 85.0,
|
||||
sm: 85.0,
|
||||
md: 35.0,
|
||||
others: 0.0,
|
||||
|
@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -1,16 +1,12 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:fl_query/fl_query.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:go_router/go_router.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/components/album/album_card.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/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/extensions/context.dart';
|
||||
import 'package:spotube/hooks/use_custom_status_bar_color.dart';
|
||||
@ -31,8 +27,8 @@ class TrackCollectionView<T> extends HookConsumerWidget {
|
||||
final String titleImage;
|
||||
final bool isPlaying;
|
||||
final void Function([Track? currentTrack]) onPlay;
|
||||
final void Function() onAddToQueue;
|
||||
final void Function([Track? currentTrack]) onShuffledPlay;
|
||||
final void Function() onAddToQueue;
|
||||
final void Function() onShare;
|
||||
final Widget? heartBtn;
|
||||
final AlbumSimple? album;
|
||||
@ -187,145 +183,17 @@ class TrackCollectionView<T> extends HookConsumerWidget {
|
||||
: null,
|
||||
centerTitle: true,
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
background: 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: 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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
background: TrackCollectionHeading<T>(
|
||||
color: color,
|
||||
title: title,
|
||||
description: description,
|
||||
titleImage: titleImage,
|
||||
isPlaying: isPlaying,
|
||||
onPlay: onPlay,
|
||||
onShuffledPlay: onShuffledPlay,
|
||||
tracksSnapshot: tracksSnapshot,
|
||||
buttons: buttons,
|
||||
album: album,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -361,7 +229,7 @@ class TrackCollectionView<T> extends HookConsumerWidget {
|
||||
// scroll the flexible space
|
||||
// to allow more space for search results
|
||||
controller.animateTo(
|
||||
390,
|
||||
330,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
@ -61,7 +61,7 @@ class TrackTile extends HookConsumerWidget {
|
||||
|
||||
return LayoutBuilder(builder: (context, constrains) {
|
||||
return HoverBuilder(
|
||||
permanentState: isPlaying || constrains.isSm ? true : null,
|
||||
permanentState: isPlaying || constrains.smAndDown ? true : null,
|
||||
builder: (context, isHovering) {
|
||||
return ListTile(
|
||||
selected: isPlaying,
|
||||
@ -89,7 +89,7 @@ class TrackTile extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
)
|
||||
else if (constrains.isSm)
|
||||
else if (constrains.smAndDown)
|
||||
const SizedBox(width: 16),
|
||||
if (onChanged != null)
|
||||
Checkbox.adaptive(
|
||||
|
@ -390,6 +390,7 @@ class TracksTableView extends HookConsumerWidget {
|
||||
|
||||
if (isSliver) {
|
||||
return SliverSafeArea(
|
||||
top: false,
|
||||
sliver: SliverList(delegate: SliverChildListDelegate(children)),
|
||||
);
|
||||
}
|
||||
|
@ -1,25 +1,39 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
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 isLg => biggest.width > 768 && biggest.width <= 1024;
|
||||
bool get isXl => biggest.width > 1024 && 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 lgAndUp => isLg || 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 {
|
||||
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 isLg => size.width > 768 && size.width <= 1024;
|
||||
bool get isXl => size.width > 1024 && 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 lgAndUp => isLg || 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;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:spotube/extensions/constrains.dart';
|
||||
|
||||
T useBreakpointValue<T>({
|
||||
T? xs,
|
||||
T? sm,
|
||||
T? md,
|
||||
T? lg,
|
||||
@ -10,8 +11,12 @@ T useBreakpointValue<T>({
|
||||
T? xxl,
|
||||
T? others,
|
||||
}) {
|
||||
final isSomeNull =
|
||||
sm == null || md == null || lg == null || xl == null || xxl == null;
|
||||
final isSomeNull = xs == null ||
|
||||
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',
|
||||
@ -20,7 +25,9 @@ T useBreakpointValue<T>({
|
||||
final mediaQuery = MediaQuery.of(context);
|
||||
|
||||
if (isSomeNull) {
|
||||
if (mediaQuery.isSm) {
|
||||
if (mediaQuery.isXs) {
|
||||
return xs ?? others!;
|
||||
} else if (mediaQuery.isSm) {
|
||||
return sm ?? others!;
|
||||
} else if (mediaQuery.isMd) {
|
||||
return md ?? others!;
|
||||
@ -32,7 +39,9 @@ T useBreakpointValue<T>({
|
||||
return lg ?? others!;
|
||||
}
|
||||
} else {
|
||||
if (mediaQuery.isSm) {
|
||||
if (mediaQuery.isXs) {
|
||||
return xs;
|
||||
} else if (mediaQuery.isSm) {
|
||||
return sm;
|
||||
} else if (mediaQuery.isMd) {
|
||||
return md;
|
||||
|
@ -4,7 +4,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:spotify/spotify.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/extensions/constrains.dart';
|
||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
||||
@ -67,7 +67,7 @@ class AlbumPage extends HookConsumerWidget {
|
||||
tracksSnapshot: tracksSnapshot,
|
||||
album: album,
|
||||
routePath: "/album/${album.id}",
|
||||
bottomSpace: mediaQuery.isSm || mediaQuery.isMd,
|
||||
bottomSpace: mediaQuery.mdAndDown,
|
||||
onPlay: ([track]) {
|
||||
if (tracksSnapshot.hasData) {
|
||||
if (!isAlbumPlaying) {
|
||||
|
@ -39,6 +39,7 @@ class ArtistPage extends HookConsumerWidget {
|
||||
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||
final textTheme = theme.textTheme;
|
||||
final chipTextVariant = useBreakpointValue(
|
||||
xs: textTheme.bodySmall,
|
||||
sm: textTheme.bodySmall,
|
||||
md: textTheme.bodyMedium,
|
||||
lg: textTheme.bodyLarge,
|
||||
@ -49,6 +50,7 @@ class ArtistPage extends HookConsumerWidget {
|
||||
final mediaQuery = MediaQuery.of(context);
|
||||
|
||||
final avatarWidth = useBreakpointValue(
|
||||
xs: mediaQuery.size.width * 0.50,
|
||||
sm: mediaQuery.size.width * 0.50,
|
||||
md: mediaQuery.size.width * 0.40,
|
||||
lg: mediaQuery.size.width * 0.18,
|
||||
@ -155,7 +157,7 @@ class ArtistPage extends HookConsumerWidget {
|
||||
),
|
||||
Text(
|
||||
data.name!,
|
||||
style: mediaQuery.isSm
|
||||
style: mediaQuery.smAndDown
|
||||
? textTheme.headlineSmall
|
||||
: textTheme.headlineMedium,
|
||||
),
|
||||
@ -166,8 +168,9 @@ class ArtistPage extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
style: textTheme.bodyMedium?.copyWith(
|
||||
fontWeight:
|
||||
mediaQuery.isSm ? null : FontWeight.bold,
|
||||
fontWeight: mediaQuery.mdAndUp
|
||||
? FontWeight.bold
|
||||
: null,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
@ -35,7 +35,7 @@ class DesktopLoginPage extends HookConsumerWidget {
|
||||
children: [
|
||||
Assets.spotubeLogoPng.image(
|
||||
width: MediaQuery.of(context).size.width *
|
||||
(mediaQuery.isSm || mediaQuery.isMd ? .5 : .3),
|
||||
(mediaQuery.mdAndDown ? .5 : .3),
|
||||
),
|
||||
Text(
|
||||
context.l10n.add_spotify_credentials,
|
||||
|
@ -2,7 +2,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.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/extensions/constrains.dart';
|
||||
import 'package:spotube/models/logger.dart';
|
||||
@ -99,7 +99,7 @@ class PlaylistView extends HookConsumerWidget {
|
||||
playlistNotifier.addTracks(tracksSnapshot.data!);
|
||||
}
|
||||
},
|
||||
bottomSpace: mediaQuery.isSm || mediaQuery.isMd,
|
||||
bottomSpace: mediaQuery.mdAndDown,
|
||||
showShare: playlist.id != "user-liked-tracks",
|
||||
routePath: "/playlist/${playlist.id}",
|
||||
onShare: () {
|
||||
|
Loading…
Reference in New Issue
Block a user