From 92a418c8a8a9df99e27407b628e5e3cc9ccb4115 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Sun, 12 Mar 2023 10:06:37 +0600 Subject: [PATCH] feat: artist card redesign chore: add license as asset for about --- LICENSE | 2 +- lib/components/artist/artist_card.dart | 95 ++++++++++--------- lib/components/library/user_artists.dart | 56 +++++------ lib/components/library/user_playlists.dart | 1 - lib/components/player/player_controls.dart | 5 +- lib/components/player/player_overlay.dart | 10 +- lib/components/player/player_queue.dart | 13 ++- .../player/player_track_details.dart | 12 +-- .../player/sibling_tracks_sheet.dart | 7 +- lib/components/root/bottom_player.dart | 5 +- lib/components/root/sidebar.dart | 30 +++--- .../dialogs/replace_downloaded_dialog.dart | 5 +- .../shared/fallbacks/not_found.dart | 5 +- lib/components/shared/playbutton_card.dart | 7 +- .../shimmers/shimmer_playbutton_card.dart | 2 +- .../shared/shimmers/shimmer_track_tile.dart | 13 ++- .../shared/themed_button_tab_bar.dart | 21 ++-- .../track_table/track_collection_view.dart | 37 ++++---- .../shared/track_table/track_tile.dart | 7 +- lib/pages/artist/artist.dart | 25 ++--- lib/pages/desktop_login/desktop_login.dart | 8 +- lib/pages/desktop_login/login_tutorial.dart | 9 +- lib/pages/player/player.dart | 34 +++---- lib/pages/search/search.dart | 19 ++-- lib/pages/settings/about.dart | 50 +++++----- lib/pages/settings/settings.dart | 29 ++---- pubspec.yaml | 1 + 27 files changed, 247 insertions(+), 261 deletions(-) diff --git a/LICENSE b/LICENSE index 5119c222..f965af85 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD-4-Clause License -Copyright (c) 2022 Kingkor Roy Tirtho. All rights reserved. +Copyright (c) 2023 Kingkor Roy Tirtho. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/lib/components/artist/artist_card.dart b/lib/components/artist/artist_card.dart index 49b75150..9f103637 100644 --- a/lib/components/artist/artist_card.dart +++ b/lib/components/artist/artist_card.dart @@ -3,8 +3,9 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotify/spotify.dart'; -import 'package:spotube/components/shared/hover_builder.dart'; import 'package:spotube/components/shared/image/universal_image.dart'; +import 'package:spotube/hooks/use_breakpoint_value.dart'; +import 'package:spotube/hooks/use_brightness_value.dart'; import 'package:spotube/provider/blacklist_provider.dart'; import 'package:spotube/utils/service_utils.dart'; import 'package:spotube/utils/type_conversion_utils.dart'; @@ -15,6 +16,7 @@ class ArtistCard extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { + final theme = Theme.of(context); final backgroundImage = UniversalImage.imageProvider( TypeConversionUtils.image_X_UrlString( artist.images, @@ -29,46 +31,53 @@ class ArtistCard extends HookConsumerWidget { ), ); - return SizedBox( - height: 240, - width: 200, - child: InkWell( - onTap: () { - ServiceUtils.navigate(context, "/artist/${artist.id}"); - }, - borderRadius: BorderRadius.circular(8), - child: HoverBuilder(builder: (context, isHovering) { - return Ink( - width: 200, - decoration: BoxDecoration( - color: Theme.of(context).cardColor, - borderRadius: BorderRadius.circular(8), - boxShadow: [ - BoxShadow( - blurRadius: 10, - offset: const Offset(0, 3), - spreadRadius: 5, - color: Theme.of(context).colorScheme.shadow, - ), - ], - border: isBlackListed - ? Border.all( - color: Colors.red[400]!, - width: 2, - ) - : null, - ), + final radius = BorderRadius.circular(15); + + final double size = useBreakpointValue( + sm: 130, + md: 150, + others: 170, + ); + + return Container( + width: size, + margin: const EdgeInsets.symmetric(vertical: 5), + child: Material( + shadowColor: theme.colorScheme.background, + color: Color.lerp( + theme.colorScheme.surfaceVariant, + theme.colorScheme.surface, + useBrightnessValue(.9, .7), + ), + elevation: 3, + shape: RoundedRectangleBorder( + borderRadius: radius, + side: isBlackListed + ? const BorderSide( + color: Colors.red, + width: 2, + ) + : BorderSide.none, + ), + child: InkWell( + onTap: () { + ServiceUtils.navigate(context, "/artist/${artist.id}"); + }, + borderRadius: radius, child: Padding( - padding: const EdgeInsets.all(15), + padding: const EdgeInsets.all(12), child: Column( - mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Stack( children: [ - CircleAvatar( - maxRadius: 80, - minRadius: 20, - backgroundImage: backgroundImage, + ConstrainedBox( + constraints: BoxConstraints( + maxHeight: size, + ), + child: CircleAvatar( + backgroundImage: backgroundImage, + radius: size / 2, + ), ), Positioned( right: 0, @@ -92,19 +101,19 @@ class ArtistCard extends HookConsumerWidget { ), ], ), + const SizedBox(height: 10), AutoSizeText( artist.name!, - maxLines: 2, + maxLines: 1, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.bold, - ), + overflow: TextOverflow.ellipsis, + style: theme.textTheme.bodyMedium?.copyWith( + fontWeight: FontWeight.bold, + ), ), ], ), - ), - ); - }), + )), ), ); } diff --git a/lib/components/library/user_artists.dart b/lib/components/library/user_artists.dart index 76e2a443..dd5dc687 100644 --- a/lib/components/library/user_artists.dart +++ b/lib/components/library/user_artists.dart @@ -18,6 +18,7 @@ class UserArtists extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { + final theme = Theme.of(context); final auth = ref.watch(AuthenticationNotifier.provider); final artistQuery = useQueries.artist.followedByMe(ref); @@ -46,6 +47,8 @@ class UserArtists extends HookConsumerWidget { .toList(); }, [artistQuery.pages, searchText.value]); + final controller = useScrollController(); + if (auth == null) { return const AnonymousFallback(); } @@ -56,7 +59,7 @@ class UserArtists extends HookConsumerWidget { child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), child: ColoredBox( - color: Theme.of(context).scaffoldBackgroundColor, + color: theme.scaffoldBackgroundColor, child: TextField( onChanged: (value) => searchText.value = value, decoration: const InputDecoration( @@ -67,7 +70,7 @@ class UserArtists extends HookConsumerWidget { ), ), ), - backgroundColor: Theme.of(context).scaffoldBackgroundColor, + backgroundColor: theme.scaffoldBackgroundColor, body: artistQuery.pages.isEmpty ? Padding( padding: const EdgeInsets.all(20), @@ -84,33 +87,30 @@ class UserArtists extends HookConsumerWidget { onRefresh: () async { await artistQuery.refreshAll(); }, - child: SafeArea( - child: GridView.builder( - itemCount: filteredArtists.length, - physics: const AlwaysScrollableScrollPhysics(), - gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 200, - mainAxisExtent: 250, - crossAxisSpacing: 20, - mainAxisSpacing: 20, + child: SingleChildScrollView( + controller: controller, + child: SizedBox( + width: double.infinity, + child: SafeArea( + child: Center( + child: Wrap( + spacing: 15, + runSpacing: 5, + children: filteredArtists.mapIndexed((index, artist) { + if (index == artistQuery.pages.length - 1 && + hasNextPage) { + return Waypoint( + controller: controller, + isGrid: true, + onTouchEdge: artistQuery.fetchNext, + child: ArtistCard(artist), + ); + } + return ArtistCard(artist); + }).toList(), + ), + ), ), - padding: const EdgeInsets.all(10), - itemBuilder: (context, index) { - return HookBuilder(builder: (context) { - if (index == artistQuery.pages.length - 1 && - hasNextPage) { - return Waypoint( - controller: useScrollController(), - isGrid: true, - onTouchEdge: () { - artistQuery.fetchNext(); - }, - child: ArtistCard(filteredArtists[index]), - ); - } - return ArtistCard(filteredArtists[index]); - }); - }, ), ), ), diff --git a/lib/components/library/user_playlists.dart b/lib/components/library/user_playlists.dart index 840dee3d..02a0fe87 100644 --- a/lib/components/library/user_playlists.dart +++ b/lib/components/library/user_playlists.dart @@ -79,7 +79,6 @@ class UserPlaylists extends HookConsumerWidget { onRefresh: playlistsQuery.refresh, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), - padding: const EdgeInsets.all(8.0), child: SafeArea( child: Column( children: [ diff --git a/lib/components/player/player_controls.dart b/lib/components/player/player_controls.dart index ebb42325..5a0b0bcb 100644 --- a/lib/components/player/player_controls.dart +++ b/lib/components/player/player_controls.dart @@ -41,6 +41,7 @@ class PlayerControls extends HookConsumerWidget { final playlistNotifier = ref.watch(PlaylistQueueNotifier.notifier); final playing = useStream(PlaylistQueueNotifier.playing).data ?? PlaylistQueueNotifier.isPlaying; + final theme = Theme.of(context); return GestureDetector( behavior: HitTestBehavior.translucent, @@ -139,7 +140,7 @@ class PlayerControls extends HookConsumerWidget { icon: Icon( SpotubeIcons.shuffle, color: playlist?.isShuffled == true - ? Theme.of(context).primaryColor + ? theme.colorScheme.primary : null, ), onPressed: playlist == null @@ -194,7 +195,7 @@ class PlayerControls extends HookConsumerWidget { ? SpotubeIcons.repeatOne : SpotubeIcons.repeat, color: playlist?.isLooping == true - ? Theme.of(context).primaryColor + ? theme.colorScheme.primary : null, ), onPressed: playlist == null || playlist.isLoading diff --git a/lib/components/player/player_overlay.dart b/lib/components/player/player_overlay.dart index 8a4cbb76..4d00bbf5 100644 --- a/lib/components/player/player_overlay.dart +++ b/lib/components/player/player_overlay.dart @@ -30,7 +30,8 @@ class PlayerOverlay extends HookConsumerWidget { final playing = useStream(PlaylistQueueNotifier.playing).data ?? PlaylistQueueNotifier.isPlaying; - final textColor = Theme.of(context).colorScheme.primary; + final theme = Theme.of(context); + final textColor = theme.colorScheme.primary; const radius = BorderRadius.only( topLeft: Radius.circular(10), @@ -54,10 +55,7 @@ class PlayerOverlay extends HookConsumerWidget { width: MediaQuery.of(context).size.width, height: canShow ? 53 : 0, decoration: BoxDecoration( - color: Theme.of(context) - .colorScheme - .secondaryContainer - .withOpacity(.8), + color: theme.colorScheme.secondaryContainer.withOpacity(.8), borderRadius: radius, ), child: AnimatedOpacity( @@ -81,7 +79,7 @@ class PlayerOverlay extends HookConsumerWidget { minHeight: 2, backgroundColor: Colors.transparent, valueColor: AlwaysStoppedAnimation( - Theme.of(context).colorScheme.primary, + theme.colorScheme.primary, ), ); }, diff --git a/lib/components/player/player_queue.dart b/lib/components/player/player_queue.dart index c0a9b6ac..a1fe5d76 100644 --- a/lib/components/player/player_queue.dart +++ b/lib/components/player/player_queue.dart @@ -36,7 +36,8 @@ class PlayerQueue extends HookConsumerWidget { topLeft: Radius.circular(10), topRight: Radius.circular(10), ); - final headlineColor = Theme.of(context).textTheme.headlineSmall?.color; + final theme = Theme.of(context); + final headlineColor = theme.textTheme.headlineSmall?.color; useEffect(() { if (playlist == null) return null; @@ -60,7 +61,7 @@ class PlayerQueue extends HookConsumerWidget { top: 5.0, ), decoration: BoxDecoration( - color: Theme.of(context).scaffoldBackgroundColor.withOpacity(0.5), + color: theme.scaffoldBackgroundColor.withOpacity(0.5), borderRadius: borderRadius, ), child: Column( @@ -88,11 +89,9 @@ class PlayerQueue extends HookConsumerWidget { const Spacer(), FilledButton( style: FilledButton.styleFrom( - backgroundColor: Theme.of(context) - .scaffoldBackgroundColor - .withOpacity(0.5), - foregroundColor: - Theme.of(context).textTheme.headlineSmall?.color, + backgroundColor: + theme.scaffoldBackgroundColor.withOpacity(0.5), + foregroundColor: theme.textTheme.headlineSmall?.color, ), child: Row( children: const [ diff --git a/lib/components/player/player_track_details.dart b/lib/components/player/player_track_details.dart index 5ecc11f0..4dee246f 100644 --- a/lib/components/player/player_track_details.dart +++ b/lib/components/player/player_track_details.dart @@ -16,6 +16,7 @@ class PlayerTrackDetails extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { + final theme = Theme.of(context); final breakpoint = useBreakpoints(); final playback = ref.watch(PlaylistQueueNotifier.provider); @@ -50,19 +51,16 @@ class PlayerTrackDetails extends HookConsumerWidget { Text( playback?.activeTrack.name ?? "", overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: color, - ), + style: theme.textTheme.bodyMedium?.copyWith( + color: color, + ), ), Text( TypeConversionUtils.artists_X_String( playback?.activeTrack.artists ?? [], ), overflow: TextOverflow.ellipsis, - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(color: color), + style: theme.textTheme.bodySmall!.copyWith(color: color), ) ], ), diff --git a/lib/components/player/sibling_tracks_sheet.dart b/lib/components/player/sibling_tracks_sheet.dart index 96f4502d..528b8a15 100644 --- a/lib/components/player/sibling_tracks_sheet.dart +++ b/lib/components/player/sibling_tracks_sheet.dart @@ -19,6 +19,7 @@ class SiblingTracksSheet extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { + final theme = Theme.of(context); final playlist = ref.watch(PlaylistQueueNotifier.provider); final playlistNotifier = ref.watch(PlaylistQueueNotifier.notifier); @@ -50,7 +51,7 @@ class SiblingTracksSheet extends HookConsumerWidget { margin: const EdgeInsets.all(8.0), decoration: BoxDecoration( borderRadius: borderRadius, - color: Theme.of(context).scaffoldBackgroundColor.withOpacity(.3), + color: theme.scaffoldBackgroundColor.withOpacity(.3), ), child: Scaffold( backgroundColor: Colors.transparent, @@ -58,7 +59,7 @@ class SiblingTracksSheet extends HookConsumerWidget { centerTitle: true, title: Text( 'Alternative Tracks Sources', - style: Theme.of(context).textTheme.headlineSmall, + style: theme.textTheme.headlineSmall, ), automaticallyImplyLeading: false, backgroundColor: Colors.transparent, @@ -96,7 +97,7 @@ class SiblingTracksSheet extends HookConsumerWidget { .ytTrack .id .value, - selectedTileColor: Theme.of(context).popupMenuTheme.color, + selectedTileColor: theme.popupMenuTheme.color, onTap: () async { if (playlist?.isLoading == false && video.id.value != diff --git a/lib/components/root/bottom_player.dart b/lib/components/root/bottom_player.dart index 701767c3..41670170 100644 --- a/lib/components/root/bottom_player.dart +++ b/lib/components/root/bottom_player.dart @@ -40,7 +40,8 @@ class BottomPlayer extends HookConsumerWidget { [playlist?.activeTrack.album?.images], ); - final bg = Theme.of(context).colorScheme.surfaceVariant; + final theme = Theme.of(context); + final bg = theme.colorScheme.surfaceVariant; final bgColor = useBrightnessValue( Color.lerp(bg, Colors.white, 0.7), @@ -62,7 +63,7 @@ class BottomPlayer extends HookConsumerWidget { decoration: BoxDecoration(color: bgColor?.withOpacity(0.8)), child: Material( type: MaterialType.transparency, - textStyle: Theme.of(context).textTheme.bodyMedium!, + textStyle: theme.textTheme.bodyMedium!, child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ diff --git a/lib/components/root/sidebar.dart b/lib/components/root/sidebar.dart index 6002ac0e..609e93da 100644 --- a/lib/components/root/sidebar.dart +++ b/lib/components/root/sidebar.dart @@ -61,7 +61,8 @@ class Sidebar extends HookConsumerWidget { extended: breakpoints > Breakpoints.md, ); - final bg = Theme.of(context).colorScheme.surfaceVariant; + final theme = Theme.of(context); + final bg = theme.colorScheme.surfaceVariant; final bgColor = useBrightnessValue( Color.lerp(bg, Colors.white, 0.7), @@ -98,7 +99,7 @@ class Sidebar extends HookConsumerWidget { (e) { return SidebarXItem( // iconWidget: Badge( - // backgroundColor: Theme.of(context).primaryColor, + // backgroundColor: theme.colorScheme.primary, // isLabelVisible: e.title == "Library" && downloadCount > 0, // label: Text( // downloadCount.toString(), @@ -125,10 +126,10 @@ class Sidebar extends HookConsumerWidget { margin: EdgeInsets.only(bottom: 10, top: kIsMacOS ? 35 : 5), selectedItemDecoration: BoxDecoration( borderRadius: BorderRadius.circular(10), - color: Theme.of(context).colorScheme.primary.withOpacity(0.1), + color: theme.colorScheme.primary.withOpacity(0.1), ), selectedIconTheme: IconThemeData( - color: Theme.of(context).colorScheme.primary, + color: theme.colorScheme.primary, ), ), extendedTheme: SidebarXTheme( @@ -148,16 +149,15 @@ class Sidebar extends HookConsumerWidget { ), selectedItemDecoration: BoxDecoration( borderRadius: BorderRadius.circular(10), - color: Theme.of(context).colorScheme.primary.withOpacity(0.1), + color: theme.colorScheme.primary.withOpacity(0.1), ), selectedIconTheme: IconThemeData( - color: Theme.of(context).colorScheme.primary, + color: theme.colorScheme.primary, + ), + selectedTextStyle: theme.textTheme.bodyMedium?.copyWith( + color: theme.colorScheme.primary, + fontWeight: FontWeight.w600, ), - selectedTextStyle: - Theme.of(context).textTheme.bodyMedium?.copyWith( - color: Theme.of(context).colorScheme.primary, - fontWeight: FontWeight.w600, - ), itemTextPadding: const EdgeInsets.only(left: 10), selectedItemTextPadding: const EdgeInsets.only(left: 10), ), @@ -175,6 +175,7 @@ class SidebarHeader extends HookWidget { @override Widget build(BuildContext context) { final breakpoint = useBreakpoints(); + final theme = Theme.of(context); if (breakpoint <= Breakpoints.md) { return Container( @@ -196,7 +197,7 @@ class SidebarHeader extends HookWidget { const SizedBox(width: 10), Text( "Spotube", - style: Theme.of(context).textTheme.titleLarge, + style: theme.textTheme.titleLarge, ), ], ), @@ -213,6 +214,7 @@ class SidebarFooter extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { + final theme = Theme.of(context); final breakpoint = useBreakpoints(); final me = useQueries.user.me(ref); final data = me.data; @@ -260,9 +262,7 @@ class SidebarFooter extends HookConsumerWidget { maxLines: 1, softWrap: false, overflow: TextOverflow.fade, - style: Theme.of(context) - .textTheme - .bodyMedium + style: theme.textTheme.bodyMedium ?.copyWith(fontWeight: FontWeight.bold), ), ), diff --git a/lib/components/shared/dialogs/replace_downloaded_dialog.dart b/lib/components/shared/dialogs/replace_downloaded_dialog.dart index 3587bc8a..a0d01986 100644 --- a/lib/components/shared/dialogs/replace_downloaded_dialog.dart +++ b/lib/components/shared/dialogs/replace_downloaded_dialog.dart @@ -13,6 +13,7 @@ class ReplaceDownloadedDialog extends ConsumerWidget { @override Widget build(BuildContext context, ref) { final groupValue = ref.watch(replaceDownloadedFileState); + final theme = Theme.of(context); return AlertDialog( title: Text("Track ${track.name} Already Exists"), @@ -23,7 +24,7 @@ class ReplaceDownloadedDialog extends ConsumerWidget { RadioListTile( dense: true, contentPadding: EdgeInsets.zero, - activeColor: Theme.of(context).primaryColor, + activeColor: theme.colorScheme.primary, value: true, groupValue: groupValue, onChanged: (value) { @@ -36,7 +37,7 @@ class ReplaceDownloadedDialog extends ConsumerWidget { RadioListTile( dense: true, contentPadding: EdgeInsets.zero, - activeColor: Theme.of(context).primaryColor, + activeColor: theme.colorScheme.primary, value: false, groupValue: groupValue, onChanged: (value) { diff --git a/lib/components/shared/fallbacks/not_found.dart b/lib/components/shared/fallbacks/not_found.dart index cdd5b765..f45573ad 100644 --- a/lib/components/shared/fallbacks/not_found.dart +++ b/lib/components/shared/fallbacks/not_found.dart @@ -7,6 +7,7 @@ class NotFound extends StatelessWidget { @override Widget build(BuildContext context) { + final theme = Theme.of(context); final widgets = [ SizedBox( height: 150, @@ -17,10 +18,10 @@ class NotFound extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ - Text("Nothing found", style: Theme.of(context).textTheme.headline6), + Text("Nothing found", style: theme.textTheme.titleLarge), Text( "The box is empty", - style: Theme.of(context).textTheme.subtitle1, + style: theme.textTheme.titleMedium, ), ], ), diff --git a/lib/components/shared/playbutton_card.dart b/lib/components/shared/playbutton_card.dart index c36e1fe4..34384e6d 100644 --- a/lib/components/shared/playbutton_card.dart +++ b/lib/components/shared/playbutton_card.dart @@ -37,11 +37,6 @@ class PlaybuttonCard extends HookWidget { final theme = Theme.of(context); final radius = BorderRadius.circular(15); - final shadowColor = useBrightnessValue( - theme.colorScheme.background, - theme.colorScheme.background, - ); - final double size = useBreakpointValue( sm: 130, md: 150, @@ -64,7 +59,7 @@ class PlaybuttonCard extends HookWidget { useBrightnessValue(.9, .7), ), borderRadius: radius, - shadowColor: shadowColor, + shadowColor: theme.colorScheme.background, elevation: 3, child: InkWell( mouseCursor: SystemMouseCursors.click, diff --git a/lib/components/shared/shimmers/shimmer_playbutton_card.dart b/lib/components/shared/shimmers/shimmer_playbutton_card.dart index 23ffdc09..49703622 100644 --- a/lib/components/shared/shimmers/shimmer_playbutton_card.dart +++ b/lib/components/shared/shimmers/shimmer_playbutton_card.dart @@ -91,7 +91,7 @@ class ShimmerPlaybuttonCard extends HookWidget { others: const Size(170, 240), ); - final isDark = Theme.of(context).brightness == Brightness.dark; + final isDark = theme.brightness == Brightness.dark; final bgColor = theme.colorScheme.surfaceVariant.withOpacity(.2); final fgColor = Color.lerp( theme.colorScheme.surfaceVariant, diff --git a/lib/components/shared/shimmers/shimmer_track_tile.dart b/lib/components/shared/shimmers/shimmer_track_tile.dart index 8caee3e3..7d50c106 100644 --- a/lib/components/shared/shimmers/shimmer_track_tile.dart +++ b/lib/components/shared/shimmers/shimmer_track_tile.dart @@ -67,7 +67,8 @@ class ShimmerTrackTile extends StatelessWidget { @override Widget build(BuildContext context) { - final isDark = Theme.of(context).brightness == Brightness.dark; + final theme = Theme.of(context); + final isDark = theme.brightness == Brightness.dark; final shimmerTheme = ShimmerColorTheme( shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200], shimmerColor: isDark ? Colors.grey[800] : Colors.grey[300], @@ -83,9 +84,8 @@ class ShimmerTrackTile extends StatelessWidget { size: const Size(double.infinity, 50), painter: ShimmerTrackTilePainter( background: shimmerTheme.shimmerBackgroundColor ?? - Theme.of(context).scaffoldBackgroundColor, - foreground: - shimmerTheme.shimmerColor ?? Theme.of(context).cardColor, + theme.scaffoldBackgroundColor, + foreground: shimmerTheme.shimmerColor ?? theme.cardColor, ), ), ); @@ -101,9 +101,8 @@ class ShimmerTrackTile extends StatelessWidget { size: const Size(double.infinity, 50), painter: ShimmerTrackTilePainter( background: shimmerTheme.shimmerBackgroundColor ?? - Theme.of(context).scaffoldBackgroundColor, - foreground: - shimmerTheme.shimmerColor ?? Theme.of(context).cardColor, + theme.scaffoldBackgroundColor, + foreground: shimmerTheme.shimmerColor ?? theme.cardColor, ), ), ), diff --git a/lib/components/shared/themed_button_tab_bar.dart b/lib/components/shared/themed_button_tab_bar.dart index 58c68fdd..ecf64134 100644 --- a/lib/components/shared/themed_button_tab_bar.dart +++ b/lib/components/shared/themed_button_tab_bar.dart @@ -11,9 +11,10 @@ class ThemedButtonsTabBar extends HookWidget implements PreferredSizeWidget { @override Widget build(BuildContext context) { + final theme = Theme.of(context); final bgColor = useBrightnessValue( - Theme.of(context).colorScheme.primaryContainer, - Color.lerp(Theme.of(context).colorScheme.primary, Colors.black, 0.7)!, + theme.colorScheme.primaryContainer, + Color.lerp(theme.colorScheme.primary, Colors.black, 0.7)!, ); final breakpoint = useBreakpointValue( @@ -34,18 +35,18 @@ class ThemedButtonsTabBar extends HookWidget implements PreferredSizeWidget { color: bgColor, borderRadius: BorderRadius.circular(15), ), - labelStyle: Theme.of(context).textTheme.labelLarge?.copyWith( - color: Theme.of(context).colorScheme.primary, - fontWeight: FontWeight.bold, - ), + labelStyle: theme.textTheme.labelLarge?.copyWith( + color: theme.colorScheme.primary, + fontWeight: FontWeight.bold, + ), borderWidth: 0, unselectedDecoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: theme.colorScheme.background, borderRadius: BorderRadius.circular(15), ), - unselectedLabelStyle: Theme.of(context).textTheme.labelLarge?.copyWith( - color: Theme.of(context).colorScheme.primary, - ), + unselectedLabelStyle: theme.textTheme.labelLarge?.copyWith( + color: theme.colorScheme.primary, + ), tabs: tabs.map((tab) { return Tab(text: " $tab "); }).toList(), diff --git a/lib/components/shared/track_table/track_collection_view.dart b/lib/components/shared/track_table/track_collection_view.dart index b6e76c1a..b846d460 100644 --- a/lib/components/shared/track_table/track_collection_view.dart +++ b/lib/components/shared/track_table/track_collection_view.dart @@ -64,6 +64,7 @@ class TrackCollectionView extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { + final theme = Theme.of(context); final auth = ref.watch(AuthenticationNotifier.provider); final color = usePaletteGenerator( context, @@ -101,7 +102,7 @@ class TrackCollectionView extends HookConsumerWidget { // play playlist IconButton( style: IconButton.styleFrom( - backgroundColor: Theme.of(context).primaryColor, + backgroundColor: theme.colorScheme.primary, ), onPressed: tracksSnapshot.data != null ? onPlay : null, icon: Icon(isPlaying ? SpotubeIcons.stop : SpotubeIcons.play), @@ -138,7 +139,7 @@ class TrackCollectionView extends HookConsumerWidget { }, [tracksSnapshot.data, searchText.value]); useCustomStatusBarColor( - color?.color ?? Theme.of(context).scaffoldBackgroundColor, + color?.color ?? theme.scaffoldBackgroundColor, GoRouter.of(context).location == routePath, ); @@ -168,11 +169,11 @@ class TrackCollectionView extends HookConsumerWidget { decoration: InputDecoration( hintText: "Search tracks...", hintStyle: TextStyle(color: color?.titleTextColor), - border: Theme.of(context).inputDecorationTheme.border?.copyWith( - borderSide: BorderSide( - color: color?.titleTextColor ?? Colors.white, - ), - ), + border: theme.inputDecorationTheme.border?.copyWith( + borderSide: BorderSide( + color: color?.titleTextColor ?? Colors.white, + ), + ), prefixIconColor: color?.titleTextColor, prefixIcon: const Icon(SpotubeIcons.search), ), @@ -228,11 +229,10 @@ class TrackCollectionView extends HookConsumerWidget { title: collapsed.value ? Text( title, - style: - Theme.of(context).textTheme.titleLarge!.copyWith( - color: color?.titleTextColor, - fontWeight: FontWeight.w600, - ), + style: theme.textTheme.titleLarge!.copyWith( + color: color?.titleTextColor, + fontWeight: FontWeight.w600, + ), ) : null, centerTitle: true, @@ -242,7 +242,7 @@ class TrackCollectionView extends HookConsumerWidget { gradient: LinearGradient( colors: [ color?.color ?? Colors.transparent, - Theme.of(context).canvasColor, + theme.canvasColor, ], begin: const FractionalOffset(0, 0), end: const FractionalOffset(0, 1), @@ -282,13 +282,10 @@ class TrackCollectionView extends HookConsumerWidget { children: [ Text( title, - style: Theme.of(context) - .textTheme - .titleLarge! - .copyWith( - color: color?.titleTextColor, - fontWeight: FontWeight.w600, - ), + style: theme.textTheme.titleLarge!.copyWith( + color: color?.titleTextColor, + fontWeight: FontWeight.w600, + ), ), if (description != null) Text( diff --git a/lib/components/shared/track_table/track_tile.dart b/lib/components/shared/track_table/track_tile.dart index 5e6df7f9..1e20a6b4 100644 --- a/lib/components/shared/track_table/track_tile.dart +++ b/lib/components/shared/track_table/track_tile.dart @@ -174,6 +174,7 @@ class TrackTile extends HookConsumerWidget { ); final toggler = useTrackToggleLike(track.value, ref); + final theme = Theme.of(context); return AnimatedContainer( duration: const Duration(milliseconds: 500), @@ -181,7 +182,7 @@ class TrackTile extends HookConsumerWidget { color: isBlackListed ? Colors.red[100] : isActive - ? Theme.of(context).popupMenuTheme.color + ? theme.popupMenuTheme.color : Colors.transparent, borderRadius: BorderRadius.circular(isActive ? 10 : 0), ), @@ -234,8 +235,8 @@ class TrackTile extends HookConsumerWidget { color: Colors.white, ), style: IconButton.styleFrom( - backgroundColor: Theme.of(context).primaryColor, - hoverColor: Theme.of(context).primaryColor.withOpacity(0.5), + backgroundColor: theme.colorScheme.primary, + hoverColor: theme.colorScheme.primary.withOpacity(0.5), ), onPressed: !isBlackListed ? () => onTrackPlayButtonPressed?.call( diff --git a/lib/pages/artist/artist.dart b/lib/pages/artist/artist.dart index e00108a6..e71e0922 100644 --- a/lib/pages/artist/artist.dart +++ b/lib/pages/artist/artist.dart @@ -33,7 +33,8 @@ class ArtistPage extends HookConsumerWidget { Widget build(BuildContext context, ref) { SpotifyApi spotify = ref.watch(spotifyProvider); final parentScrollController = useScrollController(); - final textTheme = Theme.of(context).textTheme; + final theme = Theme.of(context); + final textTheme = theme.textTheme; final chipTextVariant = useBreakpointValue( sm: textTheme.bodySmall, md: textTheme.bodyMedium, @@ -42,12 +43,14 @@ class ArtistPage extends HookConsumerWidget { xxl: textTheme.titleMedium, ); + final mediaQuery = MediaQuery.of(context); + 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, + sm: mediaQuery.size.width * 0.50, + md: mediaQuery.size.width * 0.40, + lg: mediaQuery.size.width * 0.18, + xl: mediaQuery.size.width * 0.18, + xxl: mediaQuery.size.width * 0.18, ); final breakpoint = useBreakpoints(); @@ -316,8 +319,7 @@ class ArtistPage extends HookConsumerWidget { children: [ Text( "Top Tracks", - style: - Theme.of(context).textTheme.headlineSmall, + style: theme.textTheme.headlineSmall, ), if (!isPlaylistPlaying) IconButton( @@ -347,8 +349,7 @@ class ArtistPage extends HookConsumerWidget { color: Colors.white, ), style: IconButton.styleFrom( - backgroundColor: - Theme.of(context).primaryColor, + backgroundColor: theme.colorScheme.primary, ), onPressed: () => playPlaylist(topTracks.toList()), @@ -377,14 +378,14 @@ class ArtistPage extends HookConsumerWidget { const SizedBox(height: 50), Text( "Albums", - style: Theme.of(context).textTheme.headlineSmall, + style: theme.textTheme.headlineSmall, ), const SizedBox(height: 10), ArtistAlbumList(artistId), const SizedBox(height: 20), Text( "Fans also likes", - style: Theme.of(context).textTheme.headlineSmall, + style: theme.textTheme.headlineSmall, ), const SizedBox(height: 10), HookBuilder( diff --git a/lib/pages/desktop_login/desktop_login.dart b/lib/pages/desktop_login/desktop_login.dart index a99e9050..51b38436 100644 --- a/lib/pages/desktop_login/desktop_login.dart +++ b/lib/pages/desktop_login/desktop_login.dart @@ -13,6 +13,8 @@ class DesktopLoginPage extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { final breakpoint = useBreakpoints(); + final theme = Theme.of(context); + final color = theme.colorScheme.surfaceVariant.withOpacity(.3); return SafeArea( child: Scaffold( @@ -25,7 +27,7 @@ class DesktopLoginPage extends HookConsumerWidget { margin: const EdgeInsets.all(10), padding: const EdgeInsets.all(10), decoration: BoxDecoration( - color: Theme.of(context).cardColor, + color: color, borderRadius: BorderRadius.circular(10), ), child: Column( @@ -36,11 +38,11 @@ class DesktopLoginPage extends HookConsumerWidget { ), Text( "Add your spotify credentials to get started", - style: Theme.of(context).textTheme.titleMedium, + style: theme.textTheme.titleMedium, ), Text( "Don't worry, any of your credentials won't be collected or shared with anyone", - style: Theme.of(context).textTheme.labelMedium, + style: theme.textTheme.labelMedium, ), const SizedBox(height: 10), TokenLoginForm( diff --git a/lib/pages/desktop_login/login_tutorial.dart b/lib/pages/desktop_login/login_tutorial.dart index de725553..d8e5a05c 100644 --- a/lib/pages/desktop_login/login_tutorial.dart +++ b/lib/pages/desktop_login/login_tutorial.dart @@ -19,10 +19,11 @@ class LoginTutorial extends ConsumerWidget { final authenticationNotifier = ref.watch(AuthenticationNotifier.provider.notifier); final key = GlobalKey>(); + final theme = Theme.of(context); final pageDecoration = PageDecoration( - bodyTextStyle: Theme.of(context).textTheme.bodyMedium!, - titleTextStyle: Theme.of(context).textTheme.headlineMedium!, + bodyTextStyle: theme.textTheme.bodyMedium!, + titleTextStyle: theme.textTheme.headlineMedium!, ); return Scaffold( appBar: PageWindowTitleBar( @@ -35,7 +36,7 @@ class LoginTutorial extends ConsumerWidget { ), body: IntroductionScreen( key: key, - globalBackgroundColor: Theme.of(context).scaffoldBackgroundColor, + globalBackgroundColor: theme.scaffoldBackgroundColor, overrideBack: OutlinedButton( child: const Center(child: Text("Previous")), onPressed: () { @@ -113,7 +114,7 @@ class LoginTutorial extends ConsumerWidget { children: [ Text( "Paste the copied \"sp_dc\" and \"sp_key\" values in the respective fields", - style: Theme.of(context).textTheme.labelMedium, + style: theme.textTheme.labelMedium, ), const SizedBox(height: 10), TokenLoginForm( diff --git a/lib/pages/player/player.dart b/lib/pages/player/player.dart index b45c80a1..c287a41f 100644 --- a/lib/pages/player/player.dart +++ b/lib/pages/player/player.dart @@ -28,6 +28,7 @@ class PlayerView extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { + final theme = Theme.of(context); final currentTrack = ref.watch(PlaylistQueueNotifier.provider.select( (value) => value?.activeTrack, )); @@ -94,13 +95,10 @@ class PlayerView extends HookConsumerWidget { height: 30, child: Text( currentTrack?.name ?? "Not playing", - style: Theme.of(context) - .textTheme - .headlineSmall - ?.copyWith( - fontWeight: FontWeight.bold, - color: paletteColor.titleTextColor, - ), + style: theme.textTheme.headlineSmall?.copyWith( + fontWeight: FontWeight.bold, + color: paletteColor.titleTextColor, + ), ), ), if (isLocalTrack) @@ -108,24 +106,18 @@ class PlayerView extends HookConsumerWidget { TypeConversionUtils.artists_X_String( currentTrack?.artists ?? [], ), - style: Theme.of(context) - .textTheme - .titleLarge! - .copyWith( - fontWeight: FontWeight.bold, - color: paletteColor.bodyTextColor, - ), + style: theme.textTheme.titleLarge!.copyWith( + fontWeight: FontWeight.bold, + color: paletteColor.bodyTextColor, + ), ) else TypeConversionUtils.artists_X_ClickableArtists( currentTrack?.artists ?? [], - textStyle: Theme.of(context) - .textTheme - .titleLarge! - .copyWith( - fontWeight: FontWeight.bold, - color: paletteColor.bodyTextColor, - ), + textStyle: theme.textTheme.titleLarge!.copyWith( + fontWeight: FontWeight.bold, + color: paletteColor.bodyTextColor, + ), onRouteChange: (route) { GoRouter.of(context).pop(); GoRouter.of(context).push(route); diff --git a/lib/pages/search/search.dart b/lib/pages/search/search.dart index a131b1fc..81cdfb00 100644 --- a/lib/pages/search/search.dart +++ b/lib/pages/search/search.dart @@ -33,6 +33,7 @@ class SearchPage extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { + final theme = Theme.of(context); ref.watch(AuthenticationNotifier.provider); final authenticationNotifier = ref.watch(AuthenticationNotifier.provider.notifier); @@ -74,7 +75,7 @@ class SearchPage extends HookConsumerWidget { horizontal: 20, vertical: 10, ), - color: Theme.of(context).scaffoldBackgroundColor, + color: theme.scaffoldBackgroundColor, child: TextField( decoration: const InputDecoration( prefixIcon: Icon(SpotubeIcons.search), @@ -134,9 +135,7 @@ class SearchPage extends HookConsumerWidget { if (tracks.isNotEmpty) Text( "Songs", - style: Theme.of(context) - .textTheme - .titleLarge!, + style: theme.textTheme.titleLarge!, ), if (searchTrack.isLoadingPage) const CircularProgressIndicator() @@ -198,9 +197,7 @@ class SearchPage extends HookConsumerWidget { if (playlists.isNotEmpty) Text( "Playlists", - style: Theme.of(context) - .textTheme - .titleLarge!, + style: theme.textTheme.titleLarge!, ), const SizedBox(height: 10), ScrollConfiguration( @@ -258,9 +255,7 @@ class SearchPage extends HookConsumerWidget { if (artists.isNotEmpty) Text( "Artists", - style: Theme.of(context) - .textTheme - .titleLarge!, + style: theme.textTheme.titleLarge!, ), const SizedBox(height: 10), ScrollConfiguration( @@ -317,9 +312,7 @@ class SearchPage extends HookConsumerWidget { if (albums.isNotEmpty) Text( "Albums", - style: Theme.of(context) - .textTheme - .titleMedium!, + style: theme.textTheme.titleMedium!, ), const SizedBox(height: 10), ScrollConfiguration( diff --git a/lib/pages/settings/about.dart b/lib/pages/settings/about.dart index 27dec2e1..069eb1f5 100644 --- a/lib/pages/settings/about.dart +++ b/lib/pages/settings/about.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:spotube/collections/assets.gen.dart'; import 'package:spotube/components/shared/image/universal_image.dart'; import 'package:spotube/components/shared/page_window_title_bar.dart'; @@ -9,12 +10,18 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher_string.dart'; +final _licenseProvider = FutureProvider((ref) async { + return await rootBundle.loadString("LICENSE"); +}); + class AboutSpotube extends HookConsumerWidget { const AboutSpotube({Key? key}) : super(key: key); @override Widget build(BuildContext context, ref) { final packageInfo = usePackageInfo(); + final license = ref.watch(_licenseProvider); + final theme = Theme.of(context); return Scaffold( appBar: const PageWindowTitleBar( @@ -36,7 +43,7 @@ class AboutSpotube extends HookConsumerWidget { children: [ Text( "Spotube, a light-weight, cross-platform, free-for-all spotify client", - style: Theme.of(context).textTheme.titleLarge, + style: theme.textTheme.titleLarge, ), const SizedBox(height: 20), Row( @@ -181,21 +188,35 @@ class AboutSpotube extends HookConsumerWidget { Text( "Made with ❤️ in Bangladesh🇧🇩", textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodySmall, + style: theme.textTheme.bodySmall, ), Text( "© 2021-${DateTime.now().year} Kingkor Roy Tirtho", textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodySmall, + style: theme.textTheme.bodySmall, ), const SizedBox(height: 20), ConstrainedBox( constraints: const BoxConstraints(maxWidth: 750), child: SafeArea( - child: Text( - licenseText, - textAlign: TextAlign.justify, - style: Theme.of(context).textTheme.bodySmall, + child: license.when( + data: (data) { + return Text( + data, + style: theme.textTheme.bodySmall, + ); + }, + loading: () { + return const Center( + child: CircularProgressIndicator(), + ); + }, + error: (e, s) { + return Text( + e.toString(), + style: theme.textTheme.bodySmall, + ); + }, ), ), ), @@ -206,18 +227,3 @@ class AboutSpotube extends HookConsumerWidget { ); } } - -const licenseText = """ -BSD-4-Clause License - -Copyright (c) 2022 Kingkor Roy Tirtho. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -3. All advertising materials mentioning features or use of this software must display the following acknowledgement: -This product includes software developed by Kingkor Roy Tirtho. -4. Neither the name of the Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY KINGKOR ROY TIRTHO AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KINGKOR ROY TIRTHO AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -"""; diff --git a/lib/pages/settings/settings.dart b/lib/pages/settings/settings.dart index 8a1a4236..a6f429bc 100644 --- a/lib/pages/settings/settings.dart +++ b/lib/pages/settings/settings.dart @@ -21,6 +21,7 @@ class SettingsPage extends HookConsumerWidget { Widget build(BuildContext context, ref) { final UserPreferences preferences = ref.watch(userPreferencesProvider); final auth = ref.watch(AuthenticationNotifier.provider); + final theme = Theme.of(context); final pickColorScheme = useCallback((ColorSchemeType schemeType) { return () => showDialog( @@ -57,16 +58,14 @@ class SettingsPage extends HookConsumerWidget { children: [ Text( " Account", - style: Theme.of(context) - .textTheme - .headlineSmall + style: theme.textTheme.headlineSmall ?.copyWith(fontWeight: FontWeight.bold), ), if (auth == null) AdaptiveListTile( leading: Icon( SpotubeIcons.login, - color: Theme.of(context).primaryColor, + color: theme.colorScheme.primary, ), title: SizedBox( height: 50, @@ -77,7 +76,7 @@ class SettingsPage extends HookConsumerWidget { "Login with your Spotify account", maxLines: 1, style: TextStyle( - color: Theme.of(context).primaryColor, + color: theme.colorScheme.primary, ), ), ), @@ -131,9 +130,7 @@ class SettingsPage extends HookConsumerWidget { }), Text( " Appearance", - style: Theme.of(context) - .textTheme - .headlineSmall + style: theme.textTheme.headlineSmall ?.copyWith(fontWeight: FontWeight.bold), ), AdaptiveListTile( @@ -217,9 +214,7 @@ class SettingsPage extends HookConsumerWidget { ), Text( " Playback", - style: Theme.of(context) - .textTheme - .headlineSmall + style: theme.textTheme.headlineSmall ?.copyWith(fontWeight: FontWeight.bold), ), AdaptiveListTile( @@ -279,9 +274,7 @@ class SettingsPage extends HookConsumerWidget { ), Text( " Search", - style: Theme.of(context) - .textTheme - .headlineSmall + style: theme.textTheme.headlineSmall ?.copyWith(fontWeight: FontWeight.bold), ), AdaptiveListTile( @@ -312,9 +305,7 @@ class SettingsPage extends HookConsumerWidget { ), Text( " Downloads", - style: Theme.of(context) - .textTheme - .headlineSmall + style: theme.textTheme.headlineSmall ?.copyWith(fontWeight: FontWeight.bold), ), ListTile( @@ -337,9 +328,7 @@ class SettingsPage extends HookConsumerWidget { ), Text( " About", - style: Theme.of(context) - .textTheme - .headlineSmall + style: theme.textTheme.headlineSmall ?.copyWith(fontWeight: FontWeight.bold), ), AdaptiveListTile( diff --git a/pubspec.yaml b/pubspec.yaml index b94489fd..979057fb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -102,6 +102,7 @@ flutter: - assets/ - assets/tutorial/ - .env + - LICENSE flutter_icons: android: true