feat: artist card redesign

chore: add license as asset for about
This commit is contained in:
Kingkor Roy Tirtho 2023-03-12 10:06:37 +06:00
parent dcdb16676d
commit 92a418c8a8
27 changed files with 247 additions and 261 deletions

View File

@ -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:

View File

@ -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,
final radius = BorderRadius.circular(15);
final double size = useBreakpointValue<double>(
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: 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,
),
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,
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(
overflow: TextOverflow.ellipsis,
style: theme.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
],
),
),
);
}),
)),
),
);
}

View File

@ -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: SingleChildScrollView(
controller: controller,
child: SizedBox(
width: double.infinity,
child: SafeArea(
child: GridView.builder(
itemCount: filteredArtists.length,
physics: const AlwaysScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200,
mainAxisExtent: 250,
crossAxisSpacing: 20,
mainAxisSpacing: 20,
),
padding: const EdgeInsets.all(10),
itemBuilder: (context, index) {
return HookBuilder(builder: (context) {
child: Center(
child: Wrap(
spacing: 15,
runSpacing: 5,
children: filteredArtists.mapIndexed((index, artist) {
if (index == artistQuery.pages.length - 1 &&
hasNextPage) {
return Waypoint(
controller: useScrollController(),
controller: controller,
isGrid: true,
onTouchEdge: () {
artistQuery.fetchNext();
},
child: ArtistCard(filteredArtists[index]),
onTouchEdge: artistQuery.fetchNext,
child: ArtistCard(artist),
);
}
return ArtistCard(filteredArtists[index]);
});
},
return ArtistCard(artist);
}).toList(),
),
),
),
),
),
),

View File

@ -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: [

View File

@ -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

View File

@ -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,
),
);
},

View File

@ -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 [

View File

@ -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,7 +51,7 @@ class PlayerTrackDetails extends HookConsumerWidget {
Text(
playback?.activeTrack.name ?? "",
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
style: theme.textTheme.bodyMedium?.copyWith(
color: color,
),
),
@ -59,10 +60,7 @@ class PlayerTrackDetails extends HookConsumerWidget {
playback?.activeTrack.artists ?? [],
),
overflow: TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(color: color),
style: theme.textTheme.bodySmall!.copyWith(color: color),
)
],
),

View File

@ -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 !=

View File

@ -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: [

View File

@ -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,14 +149,13 @@ 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.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.primary,
selectedTextStyle: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.primary,
fontWeight: FontWeight.w600,
),
itemTextPadding: 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),
),
),

View File

@ -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<bool>(
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<bool>(
dense: true,
contentPadding: EdgeInsets.zero,
activeColor: Theme.of(context).primaryColor,
activeColor: theme.colorScheme.primary,
value: false,
groupValue: groupValue,
onChanged: (value) {

View File

@ -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,
),
],
),

View File

@ -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<double>(
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,

View File

@ -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,

View File

@ -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,
),
),
),

View File

@ -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,17 +35,17 @@ 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,
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 ");

View File

@ -64,6 +64,7 @@ class TrackCollectionView<T> 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<T> 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<T> extends HookConsumerWidget {
}, [tracksSnapshot.data, searchText.value]);
useCustomStatusBarColor(
color?.color ?? Theme.of(context).scaffoldBackgroundColor,
color?.color ?? theme.scaffoldBackgroundColor,
GoRouter.of(context).location == routePath,
);
@ -168,7 +169,7 @@ class TrackCollectionView<T> extends HookConsumerWidget {
decoration: InputDecoration(
hintText: "Search tracks...",
hintStyle: TextStyle(color: color?.titleTextColor),
border: Theme.of(context).inputDecorationTheme.border?.copyWith(
border: theme.inputDecorationTheme.border?.copyWith(
borderSide: BorderSide(
color: color?.titleTextColor ?? Colors.white,
),
@ -228,8 +229,7 @@ class TrackCollectionView<T> extends HookConsumerWidget {
title: collapsed.value
? Text(
title,
style:
Theme.of(context).textTheme.titleLarge!.copyWith(
style: theme.textTheme.titleLarge!.copyWith(
color: color?.titleTextColor,
fontWeight: FontWeight.w600,
),
@ -242,7 +242,7 @@ class TrackCollectionView<T> 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,10 +282,7 @@ class TrackCollectionView<T> extends HookConsumerWidget {
children: [
Text(
title,
style: Theme.of(context)
.textTheme
.titleLarge!
.copyWith(
style: theme.textTheme.titleLarge!.copyWith(
color: color?.titleTextColor,
fontWeight: FontWeight.w600,
),

View File

@ -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(

View File

@ -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(

View File

@ -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(

View File

@ -19,10 +19,11 @@ class LoginTutorial extends ConsumerWidget {
final authenticationNotifier =
ref.watch(AuthenticationNotifier.provider.notifier);
final key = GlobalKey<State<IntroductionScreen>>();
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(

View File

@ -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,10 +95,7 @@ class PlayerView extends HookConsumerWidget {
height: 30,
child: Text(
currentTrack?.name ?? "Not playing",
style: Theme.of(context)
.textTheme
.headlineSmall
?.copyWith(
style: theme.textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
color: paletteColor.titleTextColor,
),
@ -108,10 +106,7 @@ class PlayerView extends HookConsumerWidget {
TypeConversionUtils.artists_X_String<Artist>(
currentTrack?.artists ?? [],
),
style: Theme.of(context)
.textTheme
.titleLarge!
.copyWith(
style: theme.textTheme.titleLarge!.copyWith(
fontWeight: FontWeight.bold,
color: paletteColor.bodyTextColor,
),
@ -119,10 +114,7 @@ class PlayerView extends HookConsumerWidget {
else
TypeConversionUtils.artists_X_ClickableArtists(
currentTrack?.artists ?? [],
textStyle: Theme.of(context)
.textTheme
.titleLarge!
.copyWith(
textStyle: theme.textTheme.titleLarge!.copyWith(
fontWeight: FontWeight.bold,
color: paletteColor.bodyTextColor,
),

View File

@ -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(

View File

@ -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<String>((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.
""";

View File

@ -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(

View File

@ -102,6 +102,7 @@ flutter:
- assets/
- assets/tutorial/
- .env
- LICENSE
flutter_icons:
android: true