diff --git a/lib/components/Album/AlbumCard.dart b/lib/components/Album/AlbumCard.dart index bdc4d864..0588fd49 100644 --- a/lib/components/Album/AlbumCard.dart +++ b/lib/components/Album/AlbumCard.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Album/AlbumView.dart'; import 'package:spotube/components/Shared/PlaybuttonCard.dart'; @@ -7,10 +8,11 @@ import 'package:spotube/components/Shared/SpotubePageRoute.dart'; import 'package:spotube/helpers/artist-to-string.dart'; import 'package:spotube/helpers/image-to-url-string.dart'; import 'package:spotube/helpers/simple-track-to-track.dart'; +import 'package:spotube/hooks/useBreakpointValue.dart'; import 'package:spotube/provider/Playback.dart'; import 'package:spotube/provider/SpotifyDI.dart'; -class AlbumCard extends ConsumerWidget { +class AlbumCard extends HookConsumerWidget { final Album album; const AlbumCard(this.album, {Key? key}) : super(key: key); @@ -19,9 +21,11 @@ class AlbumCard extends ConsumerWidget { Playback playback = ref.watch(playbackProvider); bool isPlaylistPlaying = playback.currentPlaylist != null && playback.currentPlaylist!.id == album.id; - + final int marginH = + useBreakpointValue(sm: 10, md: 15, lg: 20, xl: 20, xxl: 20); return PlaybuttonCard( imageUrl: imageToUrlString(album.images), + margin: EdgeInsets.symmetric(horizontal: marginH.toDouble()), isPlaying: playback.currentPlaylist?.id != null && playback.currentPlaylist?.id == album.id, title: album.name!, diff --git a/lib/components/Artist/ArtistProfile.dart b/lib/components/Artist/ArtistProfile.dart index 8b2b0cdd..3b8c1f18 100644 --- a/lib/components/Artist/ArtistProfile.dart +++ b/lib/components/Artist/ArtistProfile.dart @@ -1,7 +1,8 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Album/AlbumCard.dart'; import 'package:spotube/components/Artist/ArtistAlbumView.dart'; @@ -12,16 +13,39 @@ import 'package:spotube/components/Shared/TracksTableView.dart'; import 'package:spotube/helpers/image-to-url-string.dart'; import 'package:spotube/helpers/readable-number.dart'; import 'package:spotube/helpers/zero-pad-num-str.dart'; +import 'package:spotube/hooks/useBreakpointValue.dart'; +import 'package:spotube/hooks/useBreakpoints.dart'; import 'package:spotube/provider/Playback.dart'; import 'package:spotube/provider/SpotifyDI.dart'; -class ArtistProfile extends ConsumerWidget { +class ArtistProfile extends HookConsumerWidget { final String artistId; const ArtistProfile(this.artistId, {Key? key}) : super(key: key); @override Widget build(BuildContext context, ref) { SpotifyApi spotify = ref.watch(spotifyProvider); + final scrollController = useScrollController(); + final parentScrollController = useScrollController(); + final textTheme = Theme.of(context).textTheme; + final chipTextVariant = useBreakpointValue( + sm: textTheme.bodySmall, + md: textTheme.bodyMedium, + lg: textTheme.headline6, + xl: textTheme.headline6, + xxl: textTheme.headline6, + ); + + final avatarWidth = useBreakpointValue( + sm: MediaQuery.of(context).size.width * 0.50, + md: MediaQuery.of(context).size.width * 0.40, + lg: MediaQuery.of(context).size.width * 0.18, + xl: MediaQuery.of(context).size.width * 0.18, + xxl: MediaQuery.of(context).size.width * 0.18, + ); + + final breakpoint = useBreakpoints(); + return Scaffold( appBar: const PageWindowTitleBar( leading: BackButton(), @@ -34,89 +58,93 @@ class ArtistProfile extends ConsumerWidget { } return SingleChildScrollView( + controller: parentScrollController, padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( + Wrap( + crossAxisAlignment: WrapCrossAlignment.center, + runAlignment: WrapAlignment.center, children: [ const SizedBox(width: 50), CircleAvatar( - radius: MediaQuery.of(context).size.width * 0.18, + radius: avatarWidth, backgroundImage: CachedNetworkImageProvider( imageToUrlString(snapshot.data!.images), ), ), - Flexible( - child: Padding( - padding: const EdgeInsets.all(20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - padding: const EdgeInsets.symmetric( - horizontal: 10, vertical: 5), - decoration: BoxDecoration( - color: Colors.blue, - borderRadius: BorderRadius.circular(50)), - child: Text(snapshot.data!.type!.toUpperCase(), - style: Theme.of(context) - .textTheme - .headline6 - ?.copyWith(color: Colors.white)), - ), - Text( - snapshot.data!.name!, - style: Theme.of(context).textTheme.headline2, - ), - Text( - "${toReadableNumber(snapshot.data!.followers!.total!.toDouble())} followers", - style: Theme.of(context).textTheme.headline5, - ), - const SizedBox(height: 20), - Row( - children: [ - // TODO: Implement check if user follows this artist - // LIMITATION: spotify-dart lib - FutureBuilder( - future: Future.value(true), - builder: (context, snapshot) { - return OutlinedButton( - onPressed: () async { - // TODO: make `follow/unfollow` artists button work - // LIMITATION: spotify-dart lib - }, - child: Text(snapshot.data == true - ? "Following" - : "Follow"), - ); - }), - IconButton( - icon: const Icon(Icons.share_rounded), - onPressed: () { - Clipboard.setData( - ClipboardData( - text: snapshot - .data?.externalUrls?.spotify), - ).then((val) { - ScaffoldMessenger.of(context) - .showSnackBar( - const SnackBar( - width: 300, - behavior: SnackBarBehavior.floating, - content: Text( - "Artist URL copied to clipboard", - textAlign: TextAlign.center, - ), + Padding( + padding: const EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: Colors.blue, + borderRadius: BorderRadius.circular(50)), + child: Text(snapshot.data!.type!.toUpperCase(), + style: chipTextVariant?.copyWith( + color: Colors.white)), + ), + Text( + snapshot.data!.name!, + style: breakpoint.isSm + ? textTheme.headline4 + : textTheme.headline2, + ), + Text( + "${toReadableNumber(snapshot.data!.followers!.total!.toDouble())} followers", + style: breakpoint.isSm + ? textTheme.bodyText1 + : textTheme.headline5, + ), + const SizedBox(height: 20), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + // TODO: Implement check if user follows this artist + // LIMITATION: spotify-dart lib + FutureBuilder( + future: Future.value(true), + builder: (context, snapshot) { + return OutlinedButton( + onPressed: () async { + // TODO: make `follow/unfollow` artists button work + // LIMITATION: spotify-dart lib + }, + child: Text(snapshot.data == true + ? "Following" + : "Follow"), + ); + }), + IconButton( + icon: const Icon(Icons.share_rounded), + onPressed: () { + Clipboard.setData( + ClipboardData( + text: snapshot + .data?.externalUrls?.spotify), + ).then((val) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + width: 300, + behavior: SnackBarBehavior.floating, + content: Text( + "Artist URL copied to clipboard", + textAlign: TextAlign.center, ), - ); - }); - }, - ) - ], - ) - ], - ), + ), + ); + }); + }, + ) + ], + ) + ], ), ), ], @@ -188,8 +216,7 @@ class ArtistProfile extends ConsumerWidget { index: (track.value.album?.images?.length ?? 1) - 1); - return TracksTableView.buildTrackTile( - context, + return TrackTile( playback, duration: duration, track: track, @@ -237,14 +264,18 @@ class ArtistProfile extends ConsumerWidget { return const Center( child: CircularProgressIndicator.adaptive()); } - return Center( - child: Wrap( - spacing: 20, - runSpacing: 20, - children: snapshot.data - ?.map((album) => AlbumCard(album)) - .toList() ?? - [], + return Scrollbar( + controller: scrollController, + child: SingleChildScrollView( + controller: scrollController, + scrollDirection: Axis.horizontal, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: snapshot.data + ?.map((album) => AlbumCard(album)) + .toList() ?? + [], + ), ), ); }, diff --git a/lib/components/Category/CategoryCard.dart b/lib/components/Category/CategoryCard.dart index 03b7440b..65225ef9 100644 --- a/lib/components/Category/CategoryCard.dart +++ b/lib/components/Category/CategoryCard.dart @@ -1,11 +1,10 @@ import 'package:flutter/material.dart' hide Page; import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Playlist/PlaylistCard.dart'; -import 'package:spotube/components/Playlist/PlaylistGenreView.dart'; -import 'package:spotube/components/Shared/SpotubePageRoute.dart'; +import 'package:spotube/hooks/usePagingController.dart'; import 'package:spotube/provider/SpotifyDI.dart'; class CategoryCard extends HookWidget { @@ -24,26 +23,11 @@ class CategoryCard extends HookWidget { Padding( padding: const EdgeInsets.all(8.0), child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( category.name ?? "Unknown", style: Theme.of(context).textTheme.headline5, ), - TextButton( - onPressed: () { - Navigator.of(context).push( - SpotubePageRoute( - child: PlaylistGenreView( - category.id!, - category.name!, - playlists: playlists, - ), - ), - ); - }, - child: const Text("See all"), - ) ], ), ), @@ -51,37 +35,63 @@ class CategoryCard extends HookWidget { builder: (context, ref, child) { SpotifyApi spotifyApi = ref.watch(spotifyProvider); final scrollController = useScrollController(); - return FutureBuilder>( - future: playlists == null - ? (category.id != "user-featured-playlists" - ? spotifyApi.playlists.getByCategoryId(category.id!) - : spotifyApi.playlists.featured) - .getPage(4, 0) - .then((value) => value.items ?? []) - : Future.value(playlists), - builder: (context, snapshot) { - if (snapshot.hasError) { - return const Center(child: Text("Error occurred")); + final pagingController = + usePagingController(firstPageKey: 0); + + final _error = useState(false); + + useEffect(() { + listener(pageKey) async { + try { + if (playlists != null && playlists?.isNotEmpty == true) { + return pagingController.appendLastPage(playlists!.toList()); } - if (!snapshot.hasData) { - return const Center( - child: CircularProgressIndicator.adaptive(), - ); + final Page page = await (category.id != + "user-featured-playlists" + ? spotifyApi.playlists.getByCategoryId(category.id!) + : spotifyApi.playlists.featured) + .getPage(3, pageKey); + + if (page.isLast && page.items != null) { + pagingController.appendLastPage(page.items!.toList()); + } else if (page.items != null) { + pagingController.appendPage( + page.items!.toList(), page.nextOffset); } - return Scrollbar( - controller: scrollController, - child: SingleChildScrollView( - controller: scrollController, - scrollDirection: Axis.horizontal, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: snapshot.data! - .map((playlist) => PlaylistCard(playlist)) - .toList(), - ), - ), - ); - }); + if (_error.value) _error.value = false; + } catch (e, stack) { + if (!_error.value) _error.value = true; + pagingController.error = e; + print( + "[CategoryCard.pagingController.addPageRequestListener] $e"); + print(stack); + } + } + + pagingController.addPageRequestListener(listener); + return () { + pagingController.removePageRequestListener(listener); + }; + }, [_error]); + + if (_error.value) return const Text("Something Went Wrong"); + return SizedBox( + height: 245, + child: Scrollbar( + controller: scrollController, + child: PagedListView( + shrinkWrap: true, + pagingController: pagingController, + scrollController: scrollController, + scrollDirection: Axis.horizontal, + builderDelegate: PagedChildBuilderDelegate( + itemBuilder: (context, playlist, index) { + return PlaylistCard(playlist); + }, + ), + ), + ), + ); }, ) ], diff --git a/lib/components/Home.dart b/lib/components/Home.dart index 1fc1f750..95d6e30b 100644 --- a/lib/components/Home.dart +++ b/lib/components/Home.dart @@ -123,16 +123,18 @@ class Home extends HookConsumerWidget { return null; }).then((_) { pagingController.addPageRequestListener(listener); + }).catchError((e, stack) { + if (e is AuthorizationException) { + oauthLogin( + auth, + clientId: clientId, + clientSecret: clientSecret, + ); + } + print("[Home.useEffect.spotify.getCredentials]: $e"); + print(stack); }); } - } on AuthorizationException catch (_) { - if (clientId != null && clientSecret != null) { - oauthLogin( - auth, - clientId: clientId, - clientSecret: clientSecret, - ); - } } catch (e, stack) { print("[Home.initState]: $e"); print(stack); diff --git a/lib/components/Home/SpotubeNavigationBar.dart b/lib/components/Home/SpotubeNavigationBar.dart index 54d78012..158aad5c 100644 --- a/lib/components/Home/SpotubeNavigationBar.dart +++ b/lib/components/Home/SpotubeNavigationBar.dart @@ -30,7 +30,13 @@ class SpotubeNavigationBar extends HookWidget { ) ], selectedIndex: selectedIndex, - onDestinationSelected: (i) => Sidebar.goToSettings(context), + onDestinationSelected: (i) { + if (i == 4) { + Sidebar.goToSettings(context); + } else { + onSelectedIndexChanged(i); + } + }, ); } } diff --git a/lib/components/Playlist/PlaylistCard.dart b/lib/components/Playlist/PlaylistCard.dart index 8c2949e1..82c52cc2 100644 --- a/lib/components/Playlist/PlaylistCard.dart +++ b/lib/components/Playlist/PlaylistCard.dart @@ -5,10 +5,11 @@ import 'package:spotube/components/Playlist/PlaylistView.dart'; import 'package:spotube/components/Shared/PlaybuttonCard.dart'; import 'package:spotube/components/Shared/SpotubePageRoute.dart'; import 'package:spotube/helpers/image-to-url-string.dart'; +import 'package:spotube/hooks/useBreakpointValue.dart'; import 'package:spotube/provider/Playback.dart'; import 'package:spotube/provider/SpotifyDI.dart'; -class PlaylistCard extends ConsumerWidget { +class PlaylistCard extends HookConsumerWidget { final PlaylistSimple playlist; const PlaylistCard(this.playlist, {Key? key}) : super(key: key); @override @@ -16,8 +17,11 @@ class PlaylistCard extends ConsumerWidget { Playback playback = ref.watch(playbackProvider); bool isPlaylistPlaying = playback.currentPlaylist != null && playback.currentPlaylist!.id == playlist.id; + + final int marginH = + useBreakpointValue(sm: 10, md: 15, lg: 20, xl: 20, xxl: 20); return PlaybuttonCard( - margin: const EdgeInsets.symmetric(horizontal: 20), + margin: EdgeInsets.symmetric(horizontal: marginH.toDouble()), title: playlist.name!, imageUrl: playlist.images![0].url!, isPlaying: isPlaylistPlaying, diff --git a/lib/components/Search/Search.dart b/lib/components/Search/Search.dart index a70efd92..07953903 100644 --- a/lib/components/Search/Search.dart +++ b/lib/components/Search/Search.dart @@ -99,8 +99,7 @@ class Search extends HookConsumerWidget { ...tracks.asMap().entries.map((track) { String duration = "${track.value.duration?.inMinutes.remainder(60)}:${zeroPadNumStr(track.value.duration?.inSeconds.remainder(60) ?? 0)}"; - return TracksTableView.buildTrackTile( - context, + return TrackTile( playback, track: track, duration: duration, diff --git a/lib/components/Shared/PlaybuttonCard.dart b/lib/components/Shared/PlaybuttonCard.dart index 4fcddd16..7af2f678 100644 --- a/lib/components/Shared/PlaybuttonCard.dart +++ b/lib/components/Shared/PlaybuttonCard.dart @@ -85,9 +85,13 @@ class PlaybuttonCard extends StatelessWidget { const EdgeInsets.symmetric(horizontal: 8, vertical: 10), child: Column( children: [ - Text( - title, - style: const TextStyle(fontWeight: FontWeight.bold), + Tooltip( + message: title, + child: Text( + title, + style: const TextStyle(fontWeight: FontWeight.bold), + overflow: TextOverflow.ellipsis, + ), ), if (description != null) ...[ const SizedBox(height: 10), diff --git a/lib/components/Shared/TracksTableView.dart b/lib/components/Shared/TracksTableView.dart index b3a6dffb..78ed7e24 100644 --- a/lib/components/Shared/TracksTableView.dart +++ b/lib/components/Shared/TracksTableView.dart @@ -1,6 +1,7 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Album/AlbumView.dart'; import 'package:spotube/components/Shared/LinkText.dart'; @@ -8,100 +9,23 @@ import 'package:spotube/components/Shared/SpotubePageRoute.dart'; import 'package:spotube/helpers/artists-to-clickable-artists.dart'; import 'package:spotube/helpers/image-to-url-string.dart'; import 'package:spotube/helpers/zero-pad-num-str.dart'; +import 'package:spotube/hooks/useBreakpoints.dart'; import 'package:spotube/provider/Playback.dart'; -class TracksTableView extends ConsumerWidget { +class TracksTableView extends HookConsumerWidget { final void Function(Track currentTrack)? onTrackPlayButtonPressed; final List tracks; const TracksTableView(this.tracks, {Key? key, this.onTrackPlayButtonPressed}) : super(key: key); - static Widget buildTrackTile( - BuildContext context, - Playback playback, { - required MapEntry track, - required String duration, - String? thumbnailUrl, - final void Function(Track currentTrack)? onTrackPlayButtonPressed, - }) { - return Row( - children: [ - SizedBox( - height: 20, - width: 25, - child: Text( - (track.key + 1).toString(), - textAlign: TextAlign.center, - ), - ), - if (thumbnailUrl != null) - Padding( - padding: const EdgeInsets.all(8.0), - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(5)), - child: CachedNetworkImage( - placeholder: (context, url) { - return Container( - height: 40, - width: 40, - color: Colors.green[300], - ); - }, - imageUrl: thumbnailUrl, - maxHeightDiskCache: 40, - maxWidthDiskCache: 40, - ), - ), - ), - IconButton( - icon: Icon( - playback.currentTrack?.id != null && - playback.currentTrack?.id == track.value.id - ? Icons.pause_circle_rounded - : Icons.play_circle_rounded, - color: Theme.of(context).primaryColor, - ), - onPressed: () => onTrackPlayButtonPressed?.call( - track.value, - ), - ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - track.value.name ?? "", - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 17, - ), - overflow: TextOverflow.ellipsis, - ), - artistsToClickableArtists(track.value.artists ?? []), - ], - ), - ), - Expanded( - child: LinkText( - track.value.album!.name!, - SpotubePageRoute( - child: AlbumView(track.value.album!), - ), - overflow: TextOverflow.ellipsis, - ), - ), - const SizedBox(width: 10), - Text(duration), - const SizedBox(width: 10), - ], - ); - } - @override Widget build(context, ref) { Playback playback = ref.watch(playbackProvider); TextStyle tableHeadStyle = const TextStyle(fontWeight: FontWeight.bold, fontSize: 16); + + final breakpoint = useBreakpoints(); + return Expanded( child: Scrollbar( child: ListView( @@ -128,21 +52,25 @@ class TracksTableView extends ConsumerWidget { ), ), // used alignment of this table-head - const SizedBox(width: 100), - Expanded( - child: Row( - children: [ - Text( - "Album", - overflow: TextOverflow.ellipsis, - style: tableHeadStyle, - ), - ], - ), - ), - const SizedBox(width: 10), - Text("Time", style: tableHeadStyle), - const SizedBox(width: 10), + if (breakpoint.isMoreThan(Breakpoints.md)) ...[ + const SizedBox(width: 100), + Expanded( + child: Row( + children: [ + Text( + "Album", + overflow: TextOverflow.ellipsis, + style: tableHeadStyle, + ), + ], + ), + ) + ], + if (!breakpoint.isSm) ...[ + const SizedBox(width: 10), + Text("Time", style: tableHeadStyle), + const SizedBox(width: 10), + ] ], ), ...tracks.asMap().entries.map((track) { @@ -152,11 +80,13 @@ class TracksTableView extends ConsumerWidget { ); String duration = "${track.value.duration?.inMinutes.remainder(60)}:${zeroPadNumStr(track.value.duration?.inSeconds.remainder(60) ?? 0)}"; - return buildTrackTile(context, playback, - track: track, - duration: duration, - thumbnailUrl: thumbnailUrl, - onTrackPlayButtonPressed: onTrackPlayButtonPressed); + return TrackTile( + playback, + track: track, + duration: duration, + thumbnailUrl: thumbnailUrl, + onTrackPlayButtonPressed: onTrackPlayButtonPressed, + ); }).toList() ], ), @@ -164,3 +94,104 @@ class TracksTableView extends ConsumerWidget { ); } } + +class TrackTile extends HookWidget { + final Playback playback; + final MapEntry track; + final String duration; + final String? thumbnailUrl; + final void Function(Track currentTrack)? onTrackPlayButtonPressed; + const TrackTile( + this.playback, { + required this.track, + required this.duration, + this.thumbnailUrl, + this.onTrackPlayButtonPressed, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final breakpoint = useBreakpoints(); + return Row( + children: [ + SizedBox( + height: 20, + width: 25, + child: Text( + (track.key + 1).toString(), + textAlign: TextAlign.center, + ), + ), + if (thumbnailUrl != null) + Padding( + padding: EdgeInsets.symmetric( + horizontal: breakpoint.isMoreThan(Breakpoints.md) ? 8.0 : 0, + vertical: 8.0, + ), + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(5)), + child: CachedNetworkImage( + placeholder: (context, url) { + return Container( + height: 40, + width: 40, + color: Colors.green[300], + ); + }, + imageUrl: thumbnailUrl!, + maxHeightDiskCache: 40, + maxWidthDiskCache: 40, + ), + ), + ), + IconButton( + icon: Icon( + playback.currentTrack?.id != null && + playback.currentTrack?.id == track.value.id + ? Icons.pause_circle_rounded + : Icons.play_circle_rounded, + color: Theme.of(context).primaryColor, + ), + onPressed: () => onTrackPlayButtonPressed?.call( + track.value, + ), + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + track.value.name ?? "", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: breakpoint.isSm ? 14 : 17, + ), + overflow: TextOverflow.ellipsis, + ), + artistsToClickableArtists(track.value.artists ?? [], + textStyle: TextStyle( + fontSize: + breakpoint.isLessThan(Breakpoints.lg) ? 12 : 14)), + ], + ), + ), + if (breakpoint.isMoreThan(Breakpoints.md)) + Expanded( + child: LinkText( + track.value.album!.name!, + SpotubePageRoute( + child: AlbumView(track.value.album!), + ), + overflow: TextOverflow.ellipsis, + ), + ), + if (!breakpoint.isSm) ...[ + const SizedBox(width: 10), + Text(duration), + const SizedBox(width: 10) + ], + ], + ); + } +} diff --git a/lib/helpers/artists-to-clickable-artists.dart b/lib/helpers/artists-to-clickable-artists.dart index 0cb5a7f6..030e8f3d 100644 --- a/lib/helpers/artists-to-clickable-artists.dart +++ b/lib/helpers/artists-to-clickable-artists.dart @@ -8,6 +8,7 @@ Widget artistsToClickableArtists( List artists, { CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, + TextStyle textStyle = const TextStyle(), }) { return Row( crossAxisAlignment: crossAxisAlignment, @@ -24,6 +25,7 @@ Widget artistsToClickableArtists( child: ArtistProfile(artist.value.id!), ), overflow: TextOverflow.ellipsis, + style: textStyle, ), ) .toList(), diff --git a/lib/hooks/useBreakpoints.dart b/lib/hooks/useBreakpoints.dart index 73fb0276..825e1217 100644 --- a/lib/hooks/useBreakpoints.dart +++ b/lib/hooks/useBreakpoints.dart @@ -12,11 +12,11 @@ class BreakpointUtils { ]; BreakpointUtils(this.breakpoint); - get isSm => breakpoint == Breakpoints.sm; - get isMd => breakpoint == Breakpoints.md; - get isLg => breakpoint == Breakpoints.lg; - get isXl => breakpoint == Breakpoints.xl; - get isXxl => breakpoint == Breakpoints.xxl; + bool get isSm => breakpoint == Breakpoints.sm; + bool get isMd => breakpoint == Breakpoints.md; + bool get isLg => breakpoint == Breakpoints.lg; + bool get isXl => breakpoint == Breakpoints.xl; + bool get isXxl => breakpoint == Breakpoints.xxl; bool isMoreThanOrEqualTo(Breakpoints b) { return breakpointList @@ -57,6 +57,11 @@ class BreakpointUtils { bool operator <=(other) { return isLessThanOrEqualTo(other); } + + @override + String toString() { + return "BreakpointUtils($breakpoint)"; + } } enum Breakpoints { sm, md, lg, xl, xxl }