mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
feat: artist card redesign
chore: add license as asset for about
This commit is contained in:
parent
dcdb16676d
commit
92a418c8a8
2
LICENSE
2
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:
|
||||
|
||||
|
@ -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<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: 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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -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]);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -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: [
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -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 [
|
||||
|
@ -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<Artist>(
|
||||
playback?.activeTrack.artists ?? [],
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(color: color),
|
||||
style: theme.textTheme.bodySmall!.copyWith(color: color),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -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 !=
|
||||
|
@ -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: [
|
||||
|
@ -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),
|
||||
),
|
||||
),
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -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(),
|
||||
|
@ -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,11 +169,11 @@ class TrackCollectionView<T> 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<T> 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<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,13 +282,10 @@ class TrackCollectionView<T> 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(
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
|
@ -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<Artist>(
|
||||
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);
|
||||
|
@ -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(
|
||||
|
@ -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.
|
||||
""";
|
||||
|
@ -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(
|
||||
|
@ -102,6 +102,7 @@ flutter:
|
||||
- assets/
|
||||
- assets/tutorial/
|
||||
- .env
|
||||
- LICENSE
|
||||
|
||||
flutter_icons:
|
||||
android: true
|
||||
|
Loading…
Reference in New Issue
Block a user