diff --git a/lib/components/links/anchor_button.dart b/lib/components/links/anchor_button.dart index c6f0b889..a0b3fa73 100644 --- a/lib/components/links/anchor_button.dart +++ b/lib/components/links/anchor_button.dart @@ -1,5 +1,5 @@ -import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; class AnchorButton extends HookWidget { final String text; diff --git a/lib/modules/player/player_controls.dart b/lib/modules/player/player_controls.dart index acc3064e..964ff54f 100644 --- a/lib/modules/player/player_controls.dart +++ b/lib/modules/player/player_controls.dart @@ -148,7 +148,10 @@ class PlayerControls extends HookConsumerWidget { ), ), child: IconButton( - icon: const Icon(SpotubeIcons.shuffle), + icon: Icon( + SpotubeIcons.shuffle, + color: shuffled ? theme.colorScheme.primary : null, + ), variance: shuffled ? ButtonVariance.secondary : ButtonVariance.ghost, @@ -228,6 +231,9 @@ class PlayerControls extends HookConsumerWidget { loopMode == PlaylistMode.single ? SpotubeIcons.repeatOne : SpotubeIcons.repeat, + color: loopMode != PlaylistMode.none + ? theme.colorScheme.primary + : null, ), variance: loopMode == PlaylistMode.single || loopMode == PlaylistMode.loop diff --git a/lib/modules/root/update_dialog.dart b/lib/modules/root/update_dialog.dart index 27b857df..4aa2fd13 100644 --- a/lib/modules/root/update_dialog.dart +++ b/lib/modules/root/update_dialog.dart @@ -1,4 +1,4 @@ -import 'package:flutter/material.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:spotube/components/links/anchor_button.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:spotube/extensions/context.dart'; @@ -19,7 +19,7 @@ class RootAppUpdateDialog extends StatelessWidget { return AlertDialog( title: Text(context.l10n.spotube_has_an_update), actions: [ - FilledButton( + Button.primary( child: Text(context.l10n.download_now), onPressed: () => launchUrlString( nightlyBuildNum != null ? nightlyUrl : url, diff --git a/lib/modules/stats/common/album_item.dart b/lib/modules/stats/common/album_item.dart index eec68717..0920baae 100644 --- a/lib/modules/stats/common/album_item.dart +++ b/lib/modules/stats/common/album_item.dart @@ -1,5 +1,6 @@ -import 'package:flutter/material.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:spotify/spotify.dart'; +import 'package:spotube/components/ui/button_tile.dart'; import 'package:spotube/modules/album/album_card.dart'; import 'package:spotube/components/image/universal_image.dart'; import 'package:spotube/components/links/artist_link.dart'; @@ -14,8 +15,8 @@ class StatsAlbumItem extends StatelessWidget { @override Widget build(BuildContext context) { - return ListTile( - horizontalTitleGap: 8, + return ButtonTile( + style: ButtonVariance.ghost, leading: ClipRRect( borderRadius: BorderRadius.circular(4), child: UniversalImage( @@ -47,7 +48,7 @@ class StatsAlbumItem extends StatelessWidget { ], ), trailing: info, - onTap: () { + onPressed: () { ServiceUtils.pushNamed( context, AlbumPage.name, diff --git a/lib/modules/stats/common/artist_item.dart b/lib/modules/stats/common/artist_item.dart index 7e7281da..26691ba4 100644 --- a/lib/modules/stats/common/artist_item.dart +++ b/lib/modules/stats/common/artist_item.dart @@ -1,6 +1,7 @@ -import 'package:flutter/material.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/image/universal_image.dart'; +import 'package:spotube/components/ui/button_tile.dart'; import 'package:spotube/extensions/image.dart'; import 'package:spotube/pages/artist/artist.dart'; import 'package:spotube/utils/service_utils.dart'; @@ -16,18 +17,19 @@ class StatsArtistItem extends StatelessWidget { @override Widget build(BuildContext context) { - return ListTile( + return ButtonTile( + style: ButtonVariance.ghost, title: Text(artist.name!), - horizontalTitleGap: 8, - leading: CircleAvatar( - backgroundImage: UniversalImage.imageProvider( + leading: Avatar( + initials: artist.name!.substring(0, 1), + provider: UniversalImage.imageProvider( (artist.images).asUrlString( placeholder: ImagePlaceholder.artist, ), ), ), trailing: info, - onTap: () { + onPressed: () { ServiceUtils.pushNamed( context, ArtistPage.name, diff --git a/lib/modules/stats/common/playlist_item.dart b/lib/modules/stats/common/playlist_item.dart index 515c97b3..3859db6b 100644 --- a/lib/modules/stats/common/playlist_item.dart +++ b/lib/modules/stats/common/playlist_item.dart @@ -1,6 +1,7 @@ -import 'package:flutter/material.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/image/universal_image.dart'; +import 'package:spotube/components/ui/button_tile.dart'; import 'package:spotube/extensions/image.dart'; import 'package:spotube/extensions/string.dart'; import 'package:spotube/pages/playlist/playlist.dart'; @@ -14,8 +15,8 @@ class StatsPlaylistItem extends StatelessWidget { @override Widget build(BuildContext context) { - return ListTile( - horizontalTitleGap: 8, + return ButtonTile( + style: ButtonVariance.ghost, leading: ClipRRect( borderRadius: BorderRadius.circular(4), child: UniversalImage( @@ -33,7 +34,7 @@ class StatsPlaylistItem extends StatelessWidget { overflow: TextOverflow.ellipsis, ), trailing: info, - onTap: () { + onPressed: () { ServiceUtils.pushNamed( context, PlaylistPage.name, diff --git a/lib/modules/stats/common/track_item.dart b/lib/modules/stats/common/track_item.dart index 44e81340..8f0f5b8d 100644 --- a/lib/modules/stats/common/track_item.dart +++ b/lib/modules/stats/common/track_item.dart @@ -1,7 +1,8 @@ -import 'package:flutter/material.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/image/universal_image.dart'; import 'package:spotube/components/links/artist_link.dart'; +import 'package:spotube/components/ui/button_tile.dart'; import 'package:spotube/extensions/image.dart'; import 'package:spotube/pages/track/track.dart'; import 'package:spotube/utils/service_utils.dart'; @@ -17,8 +18,8 @@ class StatsTrackItem extends StatelessWidget { @override Widget build(BuildContext context) { - return ListTile( - horizontalTitleGap: 8, + return ButtonTile( + style: ButtonVariance.ghost, leading: ClipRRect( borderRadius: BorderRadius.circular(4), child: UniversalImage( @@ -42,7 +43,7 @@ class StatsTrackItem extends StatelessWidget { ), ), trailing: info, - onTap: () { + onPressed: () { ServiceUtils.pushNamed( context, TrackPage.name, diff --git a/lib/modules/stats/summary/summary.dart b/lib/modules/stats/summary/summary.dart index 46068fec..351b0264 100644 --- a/lib/modules/stats/summary/summary.dart +++ b/lib/modules/stats/summary/summary.dart @@ -1,5 +1,5 @@ -import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:skeletonizer/skeletonizer.dart'; import 'package:spotube/collections/fake.dart'; import 'package:spotube/collections/formatters.dart'; @@ -48,7 +48,7 @@ class StatsPageSummarySection extends HookConsumerWidget { title: summaryData.duration.inMinutes.toDouble(), unit: context.l10n.summary_minutes, description: context.l10n.summary_listened_to_music, - color: Colors.purple, + color: Colors.indigo, onTap: () { ServiceUtils.pushNamed(context, StatsMinutesPage.name); }, @@ -57,7 +57,7 @@ class StatsPageSummarySection extends HookConsumerWidget { title: summaryData.tracks.toDouble(), unit: context.l10n.summary_songs, description: context.l10n.summary_streamed_overall, - color: Colors.lightBlue, + color: Colors.blue, onTap: () { ServiceUtils.pushNamed(context, StatsStreamsPage.name); }, diff --git a/lib/modules/stats/summary/summary_card.dart b/lib/modules/stats/summary/summary_card.dart index 243c50e8..e78dd080 100644 --- a/lib/modules/stats/summary/summary_card.dart +++ b/lib/modules/stats/summary/summary_card.dart @@ -1,6 +1,7 @@ import 'package:auto_size_text/auto_size_text.dart'; -import 'package:flutter/material.dart'; -import 'package:gap/gap.dart'; +import 'package:flutter/foundation.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; +import 'package:shadcn_flutter/shadcn_flutter_extension.dart'; import 'package:spotube/collections/formatters.dart'; class SummaryCard extends StatelessWidget { @@ -9,7 +10,7 @@ class SummaryCard extends StatelessWidget { final String description; final VoidCallback? onTap; - final MaterialColor color; + final ColorShades color; SummaryCard({ super.key, @@ -31,15 +32,18 @@ class SummaryCard extends StatelessWidget { @override Widget build(BuildContext context) { - final ThemeData(:textTheme, :brightness) = Theme.of(context); + final ThemeData(:typography, :brightness) = Theme.of(context); final descriptionNewLines = description.split("").where((s) => s == "\n"); return Card( - color: brightness == Brightness.dark ? color.shade100 : color.shade50, - child: InkWell( - borderRadius: BorderRadius.circular(16), - onTap: onTap, + fillColor: brightness == Brightness.dark ? color.shade100 : color.shade50, + filled: true, + borderColor: color, + padding: EdgeInsets.zero, + borderRadius: context.theme.borderRadiusLg, + child: Button.ghost( + onPressed: onTap, child: Padding( padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 15), child: Column( @@ -52,13 +56,13 @@ class SummaryCard extends StatelessWidget { children: [ TextSpan( text: title, - style: textTheme.headlineLarge?.copyWith( + style: typography.h2.copyWith( color: color.shade900, ), ), TextSpan( text: " $unit", - style: textTheme.titleMedium?.copyWith( + style: typography.semiBold.copyWith( color: color.shade900, ), ), @@ -73,7 +77,7 @@ class SummaryCard extends StatelessWidget { ? descriptionNewLines.length + 1 : 1, minFontSize: 9, - style: textTheme.labelMedium!.copyWith( + style: typography.small.copyWith( color: color.shade900, ), ), diff --git a/lib/modules/stats/top/top.dart b/lib/modules/stats/top/top.dart index 5c2cfbf9..8e9134c7 100644 --- a/lib/modules/stats/top/top.dart +++ b/lib/modules/stats/top/top.dart @@ -1,6 +1,8 @@ -import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; +import 'package:shadcn_flutter/shadcn_flutter_extension.dart'; +import 'package:spotube/extensions/constrains.dart'; import 'package:spotube/modules/stats/top/albums.dart'; import 'package:spotube/modules/stats/top/artists.dart'; import 'package:spotube/modules/stats/top/tracks.dart'; @@ -13,94 +15,90 @@ class StatsPageTopSection extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { - final tabController = useTabController(initialLength: 3); + final selectedIndex = useState(0); final historyDuration = ref.watch(playbackHistoryTopDurationProvider); final historyDurationNotifier = ref.watch(playbackHistoryTopDurationProvider.notifier); - return SliverMainAxisGroup( - slivers: [ - SliverAppBar( - floating: true, - flexibleSpace: TabBar( - controller: tabController, - tabs: [ - Tab( - child: Padding( - padding: const EdgeInsets.all(5), - child: Text(context.l10n.top_tracks), - ), - ), - Tab( - child: Padding( - padding: const EdgeInsets.all(5), - child: Text(context.l10n.top_artists), - ), - ), - Tab( - child: Padding( - padding: const EdgeInsets.all(5), - child: Text(context.l10n.top_albums), - ), - ), - ], + final translations = { + HistoryDuration.days7: context.l10n.this_week, + HistoryDuration.days30: context.l10n.this_month, + HistoryDuration.months6: context.l10n.last_6_months, + HistoryDuration.year: context.l10n.this_year, + HistoryDuration.years2: context.l10n.last_2_years, + HistoryDuration.allTime: context.l10n.all_time, + }; + + final dropdown = Select( + popupConstraints: const BoxConstraints(maxWidth: 150), + popupWidthConstraint: PopoverConstraint.flexible, + padding: const EdgeInsets.all(4), + borderRadius: BorderRadius.circular(4), + value: historyDuration, + onChanged: (value) { + if (value == null) return; + historyDurationNotifier.update((_) => value); + }, + itemBuilder: (context, item) => Text(translations[item]!), + children: [ + for (final item in HistoryDuration.values) + SelectItemButton( + value: item, + child: Text(translations[item]!), ), - ), - SliverToBoxAdapter( - child: Align( - alignment: Alignment.centerRight, - child: DropdownButton( - style: Theme.of(context).textTheme.bodySmall!, - isDense: true, - padding: const EdgeInsets.all(4), - borderRadius: BorderRadius.circular(4), - underline: const SizedBox(), - value: historyDuration, - onChanged: (value) { - if (value == null) return; - historyDurationNotifier.update((_) => value); - }, - icon: const Icon(Icons.arrow_drop_down), - items: [ - DropdownMenuItem( - value: HistoryDuration.days7, - child: Text(context.l10n.this_week), - ), - DropdownMenuItem( - value: HistoryDuration.days30, - child: Text(context.l10n.this_month), - ), - DropdownMenuItem( - value: HistoryDuration.months6, - child: Text(context.l10n.last_6_months), - ), - DropdownMenuItem( - value: HistoryDuration.year, - child: Text(context.l10n.this_year), - ), - DropdownMenuItem( - value: HistoryDuration.years2, - child: Text(context.l10n.last_2_years), - ), - DropdownMenuItem( - value: HistoryDuration.allTime, - child: Text(context.l10n.all_time), - ), - ], - ), - ), - ), - ListenableBuilder( - listenable: tabController, - builder: (context, _) { - return switch (tabController.index) { - 1 => const TopArtists(), - 2 => const TopAlbums(), - _ => const TopTracks(), - }; - }, - ), ], ); + + return SliverLayoutBuilder(builder: (context, constraints) { + return SliverMainAxisGroup( + slivers: [ + SliverAppBar( + floating: true, + elevation: 0, + backgroundColor: context.theme.colorScheme.background, + flexibleSpace: Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + TabList( + index: selectedIndex.value, + children: [ + TabButton( + child: Text(context.l10n.top_tracks), + onPressed: () => selectedIndex.value = 0, + ), + TabButton( + child: Text(context.l10n.top_artists), + onPressed: () => selectedIndex.value = 1, + ), + TabButton( + child: Text(context.l10n.top_albums), + onPressed: () => selectedIndex.value = 2, + ), + ], + ), + if (constraints.mdAndUp) ...[ + const Spacer(), + dropdown, + ] + ], + ), + ), + ), + if (constraints.smAndDown) + SliverToBoxAdapter( + child: Align( + alignment: Alignment.centerRight, + child: dropdown, + ), + ), + switch (selectedIndex.value) { + 1 => const TopArtists(), + 2 => const TopAlbums(), + _ => const TopTracks(), + }, + ], + ); + }); } } diff --git a/lib/pages/stats/albums/albums.dart b/lib/pages/stats/albums/albums.dart index b424cd70..eee6694b 100644 --- a/lib/pages/stats/albums/albums.dart +++ b/lib/pages/stats/albums/albums.dart @@ -1,5 +1,5 @@ -import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:skeletonizer/skeletonizer.dart'; import 'package:spotube/collections/formatters.dart'; import 'package:spotube/components/titlebar/titlebar.dart'; @@ -25,11 +25,13 @@ class StatsAlbumsPage extends HookConsumerWidget { final albumsData = topAlbums.asData?.value.items ?? []; return Scaffold( - appBar: TitleBar( - automaticallyImplyLeading: true, - title: Text(context.l10n.albums), - ), - body: Skeletonizer( + headers: [ + TitleBar( + automaticallyImplyLeading: true, + title: Text(context.l10n.albums), + ) + ], + child: Skeletonizer( enabled: topAlbums.isLoading && !topAlbums.isLoadingNextPage, child: InfiniteList( onFetchData: () async { diff --git a/lib/pages/stats/artists/artists.dart b/lib/pages/stats/artists/artists.dart index 468ddabb..3a719725 100644 --- a/lib/pages/stats/artists/artists.dart +++ b/lib/pages/stats/artists/artists.dart @@ -1,6 +1,6 @@ -import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:skeletonizer/skeletonizer.dart'; import 'package:spotube/collections/formatters.dart'; import 'package:spotube/components/titlebar/titlebar.dart'; @@ -28,11 +28,13 @@ class StatsArtistsPage extends HookConsumerWidget { () => topTracks.asData?.value.artists ?? [], [topTracks.asData?.value]); return Scaffold( - appBar: TitleBar( - automaticallyImplyLeading: true, - title: Text(context.l10n.artists), - ), - body: Skeletonizer( + headers: [ + TitleBar( + automaticallyImplyLeading: true, + title: Text(context.l10n.artists), + ) + ], + child: Skeletonizer( enabled: topTracks.isLoading && !topTracks.isLoadingNextPage, child: InfiniteList( onFetchData: () async { diff --git a/lib/pages/stats/fees/fees.dart b/lib/pages/stats/fees/fees.dart index 1bbcb103..14f432d6 100644 --- a/lib/pages/stats/fees/fees.dart +++ b/lib/pages/stats/fees/fees.dart @@ -1,6 +1,6 @@ -import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:skeletonizer/skeletonizer.dart'; import 'package:sliver_tools/sliver_tools.dart'; import 'package:spotube/collections/formatters.dart'; @@ -20,7 +20,6 @@ class StatsStreamFeesPage extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { - final ThemeData(:textTheme, :hintColor) = Theme.of(context); final duration = useState(HistoryDuration.days30); final topTracks = ref.watch( @@ -40,12 +39,23 @@ class StatsStreamFeesPage extends HookConsumerWidget { [artistsData], ); + final translations = { + HistoryDuration.days7: context.l10n.this_week, + HistoryDuration.days30: context.l10n.this_month, + HistoryDuration.months6: context.l10n.last_6_months, + HistoryDuration.year: context.l10n.this_year, + HistoryDuration.years2: context.l10n.last_2_years, + HistoryDuration.allTime: context.l10n.all_time, + }; + return Scaffold( - appBar: TitleBar( - automaticallyImplyLeading: true, - title: Text(context.l10n.streaming_fees_hypothetical), - ), - body: CustomScrollView( + headers: [ + TitleBar( + automaticallyImplyLeading: true, + title: Text(context.l10n.streaming_fees_hypothetical), + ) + ], + child: CustomScrollView( slivers: [ SliverCrossAxisConstrained( maxCrossAxisExtent: 600, @@ -55,10 +65,7 @@ class StatsStreamFeesPage extends HookConsumerWidget { sliver: SliverToBoxAdapter( child: Text( context.l10n.spotify_hipotetical_calculation, - style: textTheme.bodySmall?.copyWith( - color: hintColor, - ), - ), + ).small().muted(), ), ), ), @@ -70,39 +77,22 @@ class StatsStreamFeesPage extends HookConsumerWidget { children: [ Text( context.l10n.total_money(usdFormatter.format(total)), - style: textTheme.titleLarge, - ), - DropdownButton( + ).semiBold().large(), + Select( value: duration.value, onChanged: (value) { if (value == null) return; duration.value = value; }, - items: [ - DropdownMenuItem( - value: HistoryDuration.days7, - child: Text(context.l10n.this_week), - ), - DropdownMenuItem( - value: HistoryDuration.days30, - child: Text(context.l10n.this_month), - ), - DropdownMenuItem( - value: HistoryDuration.months6, - child: Text(context.l10n.last_6_months), - ), - DropdownMenuItem( - value: HistoryDuration.year, - child: Text(context.l10n.this_year), - ), - DropdownMenuItem( - value: HistoryDuration.years2, - child: Text(context.l10n.last_2_years), - ), - DropdownMenuItem( - value: HistoryDuration.allTime, - child: Text(context.l10n.all_time), - ), + itemBuilder: (context, value) => Text(translations[value]!), + constraints: const BoxConstraints(maxWidth: 150), + popupWidthConstraint: PopoverConstraint.anchorMaxSize, + children: [ + for (final entry in translations.entries) + SelectItemButton( + value: entry.key, + child: Text(entry.value), + ), ], ), ], diff --git a/lib/pages/stats/minutes/minutes.dart b/lib/pages/stats/minutes/minutes.dart index f6cfafca..39438b47 100644 --- a/lib/pages/stats/minutes/minutes.dart +++ b/lib/pages/stats/minutes/minutes.dart @@ -1,6 +1,5 @@ -import 'package:flutter/material.dart'; -import 'package:gap/gap.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:skeletonizer/skeletonizer.dart'; import 'package:spotube/collections/formatters.dart'; import 'package:spotube/components/titlebar/titlebar.dart'; @@ -28,11 +27,13 @@ class StatsMinutesPage extends HookConsumerWidget { final tracksData = topTracks.asData?.value.items ?? []; return Scaffold( - appBar: TitleBar( - title: Text(context.l10n.minutes_listened), - automaticallyImplyLeading: true, - ), - body: Skeletonizer( + headers: [ + TitleBar( + title: Text(context.l10n.minutes_listened), + automaticallyImplyLeading: true, + ) + ], + child: Skeletonizer( enabled: topTracks.isLoading && !topTracks.isLoadingNextPage, child: InfiniteList( separatorBuilder: (context, index) => const Gap(8), diff --git a/lib/pages/stats/playlists/playlists.dart b/lib/pages/stats/playlists/playlists.dart index 53885570..f5d7a285 100644 --- a/lib/pages/stats/playlists/playlists.dart +++ b/lib/pages/stats/playlists/playlists.dart @@ -1,5 +1,5 @@ -import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:skeletonizer/skeletonizer.dart'; import 'package:spotube/collections/formatters.dart'; import 'package:spotube/components/titlebar/titlebar.dart'; @@ -26,11 +26,13 @@ class StatsPlaylistsPage extends HookConsumerWidget { final playlistsData = topPlaylists.asData?.value.items ?? []; return Scaffold( - appBar: TitleBar( - automaticallyImplyLeading: true, - title: Text(context.l10n.playlists), - ), - body: Skeletonizer( + headers: [ + TitleBar( + automaticallyImplyLeading: true, + title: Text(context.l10n.playlists), + ) + ], + child: Skeletonizer( enabled: topPlaylists.isLoading && !topPlaylists.isLoadingNextPage, child: InfiniteList( onFetchData: () async { diff --git a/lib/pages/stats/stats.dart b/lib/pages/stats/stats.dart index 9c35df63..a17e7562 100644 --- a/lib/pages/stats/stats.dart +++ b/lib/pages/stats/stats.dart @@ -1,6 +1,5 @@ -import 'package:flutter/material.dart'; -import 'package:gap/gap.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:spotube/components/titlebar/titlebar.dart'; import 'package:spotube/modules/stats/summary/summary.dart'; import 'package:spotube/modules/stats/top/top.dart'; @@ -16,8 +15,10 @@ class StatsPage extends HookConsumerWidget { return SafeArea( bottom: false, child: Scaffold( - appBar: kIsMacOS || kIsMobile ? null : const TitleBar(), - body: CustomScrollView( + headers: [ + if (kIsWindows || kIsLinux) const TitleBar(), + ], + child: CustomScrollView( slivers: [ if (kIsMacOS) const SliverGap(20), const StatsPageSummarySection(), diff --git a/lib/pages/stats/streams/streams.dart b/lib/pages/stats/streams/streams.dart index b835b3a6..2c2e0c9b 100644 --- a/lib/pages/stats/streams/streams.dart +++ b/lib/pages/stats/streams/streams.dart @@ -1,6 +1,5 @@ -import 'package:flutter/material.dart'; -import 'package:gap/gap.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:skeletonizer/skeletonizer.dart'; import 'package:spotube/collections/formatters.dart'; import 'package:spotube/components/titlebar/titlebar.dart'; @@ -28,11 +27,13 @@ class StatsStreamsPage extends HookConsumerWidget { final tracksData = topTracks.asData?.value.items ?? []; return Scaffold( - appBar: TitleBar( - title: Text(context.l10n.streamed_songs), - automaticallyImplyLeading: true, - ), - body: Skeletonizer( + headers: [ + TitleBar( + title: Text(context.l10n.streamed_songs), + automaticallyImplyLeading: true, + ) + ], + child: Skeletonizer( enabled: topTracks.isLoading && !topTracks.isLoadingNextPage, child: InfiniteList( separatorBuilder: (context, index) => const Gap(8),