mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 16:05:18 +00:00
feat(player): replace bg blur with gradient, proper fg color and align title and artist name
This commit is contained in:
parent
36396b7583
commit
159f03e7ca
@ -11,10 +11,10 @@ import 'package:spotube/provider/playlist_queue_provider.dart';
|
|||||||
import 'package:spotube/utils/primitive_utils.dart';
|
import 'package:spotube/utils/primitive_utils.dart';
|
||||||
|
|
||||||
class PlayerControls extends HookConsumerWidget {
|
class PlayerControls extends HookConsumerWidget {
|
||||||
final Color? iconColor;
|
final Color? color;
|
||||||
|
|
||||||
PlayerControls({
|
PlayerControls({
|
||||||
this.iconColor,
|
this.color,
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@ -109,23 +109,26 @@ class PlayerControls extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
activeColor: iconColor,
|
activeColor: color,
|
||||||
|
inactiveColor: color?.withOpacity(0.15),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 8.0,
|
horizontal: 8.0,
|
||||||
),
|
),
|
||||||
|
child: DefaultTextStyle(
|
||||||
|
style:
|
||||||
|
theme.textTheme.bodySmall!.copyWith(color: color),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text("$currentMinutes:$currentSeconds"),
|
||||||
"$currentMinutes:$currentSeconds",
|
|
||||||
),
|
|
||||||
Text("$totalMinutes:$totalSeconds"),
|
Text("$totalMinutes:$totalSeconds"),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -157,7 +160,7 @@ class PlayerControls extends HookConsumerWidget {
|
|||||||
tooltip: "Previous track",
|
tooltip: "Previous track",
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
SpotubeIcons.skipBack,
|
SpotubeIcons.skipBack,
|
||||||
color: iconColor,
|
color: color,
|
||||||
),
|
),
|
||||||
onPressed: playlistNotifier.previous,
|
onPressed: playlistNotifier.previous,
|
||||||
),
|
),
|
||||||
@ -171,7 +174,7 @@ class PlayerControls extends HookConsumerWidget {
|
|||||||
)
|
)
|
||||||
: Icon(
|
: Icon(
|
||||||
playing ? SpotubeIcons.pause : SpotubeIcons.play,
|
playing ? SpotubeIcons.pause : SpotubeIcons.play,
|
||||||
color: iconColor,
|
color: color,
|
||||||
),
|
),
|
||||||
onPressed: Actions.handler<PlayPauseIntent>(
|
onPressed: Actions.handler<PlayPauseIntent>(
|
||||||
context,
|
context,
|
||||||
@ -182,7 +185,7 @@ class PlayerControls extends HookConsumerWidget {
|
|||||||
tooltip: "Next track",
|
tooltip: "Next track",
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
SpotubeIcons.skipForward,
|
SpotubeIcons.skipForward,
|
||||||
color: iconColor,
|
color: color,
|
||||||
),
|
),
|
||||||
onPressed: playlistNotifier.next,
|
onPressed: playlistNotifier.next,
|
||||||
),
|
),
|
||||||
|
@ -144,10 +144,7 @@ class PlaylistHeartButton extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
[playlist.images]);
|
[playlist.images]);
|
||||||
|
|
||||||
final color = usePaletteGenerator(
|
final color = usePaletteGenerator(titleImage).dominantColor;
|
||||||
context,
|
|
||||||
titleImage,
|
|
||||||
).dominantColor;
|
|
||||||
|
|
||||||
if (me.isLoading || !me.hasData) {
|
if (me.isLoading || !me.hasData) {
|
||||||
return const CircularProgressIndicator();
|
return const CircularProgressIndicator();
|
||||||
|
@ -68,7 +68,7 @@ class _PageWindowTitleBarState extends State<PageWindowTitleBar> {
|
|||||||
automaticallyImplyLeading: widget.automaticallyImplyLeading,
|
automaticallyImplyLeading: widget.automaticallyImplyLeading,
|
||||||
actions: [
|
actions: [
|
||||||
...?widget.actions,
|
...?widget.actions,
|
||||||
const WindowTitleBarButtons(),
|
WindowTitleBarButtons(foregroundColor: widget.foregroundColor),
|
||||||
],
|
],
|
||||||
backgroundColor: widget.backgroundColor,
|
backgroundColor: widget.backgroundColor,
|
||||||
foregroundColor: widget.foregroundColor,
|
foregroundColor: widget.foregroundColor,
|
||||||
@ -86,7 +86,11 @@ class _PageWindowTitleBarState extends State<PageWindowTitleBar> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class WindowTitleBarButtons extends HookWidget {
|
class WindowTitleBarButtons extends HookWidget {
|
||||||
const WindowTitleBarButtons({Key? key}) : super(key: key);
|
final Color? foregroundColor;
|
||||||
|
const WindowTitleBarButtons({
|
||||||
|
Key? key,
|
||||||
|
this.foregroundColor,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -110,7 +114,7 @@ class WindowTitleBarButtons extends HookWidget {
|
|||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final colors = WindowButtonColors(
|
final colors = WindowButtonColors(
|
||||||
normal: Colors.transparent,
|
normal: Colors.transparent,
|
||||||
iconNormal: theme.colorScheme.onBackground,
|
iconNormal: foregroundColor ?? theme.colorScheme.onBackground,
|
||||||
mouseOver: theme.colorScheme.onBackground.withOpacity(0.1),
|
mouseOver: theme.colorScheme.onBackground.withOpacity(0.1),
|
||||||
mouseDown: theme.colorScheme.onBackground.withOpacity(0.2),
|
mouseDown: theme.colorScheme.onBackground.withOpacity(0.2),
|
||||||
iconMouseOver: theme.colorScheme.onBackground,
|
iconMouseOver: theme.colorScheme.onBackground,
|
||||||
@ -119,7 +123,7 @@ class WindowTitleBarButtons extends HookWidget {
|
|||||||
|
|
||||||
final closeColors = WindowButtonColors(
|
final closeColors = WindowButtonColors(
|
||||||
normal: Colors.transparent,
|
normal: Colors.transparent,
|
||||||
iconNormal: theme.colorScheme.onBackground,
|
iconNormal: foregroundColor ?? theme.colorScheme.onBackground,
|
||||||
mouseOver: Colors.red,
|
mouseOver: Colors.red,
|
||||||
mouseDown: Colors.red[800]!,
|
mouseDown: Colors.red[800]!,
|
||||||
iconMouseOver: Colors.white,
|
iconMouseOver: Colors.white,
|
||||||
|
@ -66,10 +66,7 @@ class TrackCollectionView<T> extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final auth = ref.watch(AuthenticationNotifier.provider);
|
final auth = ref.watch(AuthenticationNotifier.provider);
|
||||||
final color = usePaletteGenerator(
|
final color = usePaletteGenerator(titleImage).dominantColor;
|
||||||
context,
|
|
||||||
titleImage,
|
|
||||||
).dominantColor;
|
|
||||||
|
|
||||||
final List<Widget> buttons = [
|
final List<Widget> buttons = [
|
||||||
if (showShare)
|
if (showShare)
|
||||||
|
@ -12,6 +12,7 @@ final _paletteColorState = StateProvider<PaletteColor>(
|
|||||||
|
|
||||||
PaletteColor usePaletteColor(String imageUrl, WidgetRef ref) {
|
PaletteColor usePaletteColor(String imageUrl, WidgetRef ref) {
|
||||||
final context = useContext();
|
final context = useContext();
|
||||||
|
final theme = Theme.of(context);
|
||||||
final paletteColor = ref.watch(_paletteColorState);
|
final paletteColor = ref.watch(_paletteColorState);
|
||||||
final mounted = useIsMounted();
|
final mounted = useIsMounted();
|
||||||
|
|
||||||
@ -25,7 +26,7 @@ PaletteColor usePaletteColor(String imageUrl, WidgetRef ref) {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (!mounted()) return;
|
if (!mounted()) return;
|
||||||
final color = Theme.of(context).brightness == Brightness.light
|
final color = theme.brightness == Brightness.light
|
||||||
? palette.lightMutedColor ?? palette.lightVibrantColor
|
? palette.lightMutedColor ?? palette.lightVibrantColor
|
||||||
: palette.darkMutedColor ?? palette.darkVibrantColor;
|
: palette.darkMutedColor ?? palette.darkVibrantColor;
|
||||||
if (color != null) {
|
if (color != null) {
|
||||||
@ -38,10 +39,7 @@ PaletteColor usePaletteColor(String imageUrl, WidgetRef ref) {
|
|||||||
return paletteColor;
|
return paletteColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
PaletteGenerator usePaletteGenerator(
|
PaletteGenerator usePaletteGenerator(String imageUrl) {
|
||||||
BuildContext context,
|
|
||||||
String imageUrl,
|
|
||||||
) {
|
|
||||||
final palette = useState(PaletteGenerator.fromColors([]));
|
final palette = useState(PaletteGenerator.fromColors([]));
|
||||||
final mounted = useIsMounted();
|
final mounted = useIsMounted();
|
||||||
|
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:auto_size_text/auto_size_text.dart';
|
import 'package:auto_size_text/auto_size_text.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:palette_generator/palette_generator.dart';
|
|
||||||
|
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
|
import 'package:spotube/collections/assets.gen.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/player/player_actions.dart';
|
import 'package:spotube/components/player/player_actions.dart';
|
||||||
import 'package:spotube/components/player/player_controls.dart';
|
import 'package:spotube/components/player/player_controls.dart';
|
||||||
@ -56,40 +54,64 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
[currentTrack?.album?.images],
|
[currentTrack?.album?.images],
|
||||||
);
|
);
|
||||||
|
|
||||||
final PaletteColor paletteColor = usePaletteColor(albumArt, ref);
|
final palette = usePaletteGenerator(albumArt);
|
||||||
|
final bgColor = palette.dominantColor?.color ?? theme.colorScheme.primary;
|
||||||
|
final titleTextColor = palette.dominantColor?.titleTextColor;
|
||||||
|
final bodyTextColor = palette.dominantColor?.bodyTextColor;
|
||||||
|
|
||||||
useCustomStatusBarColor(
|
useCustomStatusBarColor(
|
||||||
paletteColor.color,
|
bgColor,
|
||||||
GoRouter.of(context).location == "/player",
|
GoRouter.of(context).location == "/player",
|
||||||
noSetBGColor: true,
|
noSetBGColor: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
return Scaffold(
|
return IconTheme(
|
||||||
|
data: theme.iconTheme.copyWith(color: bodyTextColor),
|
||||||
|
child: Scaffold(
|
||||||
appBar: PageWindowTitleBar(
|
appBar: PageWindowTitleBar(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
foregroundColor: paletteColor.titleTextColor,
|
foregroundColor: titleTextColor,
|
||||||
toolbarOpacity: 1,
|
toolbarOpacity: 1,
|
||||||
leading: BackButton(color: paletteColor.titleTextColor),
|
leading: const BackButton(),
|
||||||
),
|
),
|
||||||
extendBodyBehindAppBar: true,
|
extendBodyBehindAppBar: true,
|
||||||
body: DecoratedBox(
|
body: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
image: DecorationImage(
|
color: palette.dominantColor?.color,
|
||||||
image: UniversalImage.imageProvider(albumArt),
|
gradient: LinearGradient(
|
||||||
fit: BoxFit.cover,
|
colors: [
|
||||||
|
palette.dominantColor?.color ?? theme.colorScheme.primary,
|
||||||
|
palette.mutedColor?.color ?? theme.colorScheme.secondary,
|
||||||
|
],
|
||||||
|
transform: const GradientRotation(0.5),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: BackdropFilter(
|
alignment: Alignment.center,
|
||||||
filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15),
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(maxWidth: 580),
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
ClipRRect(
|
DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
boxShadow: const [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black26,
|
||||||
|
spreadRadius: 2,
|
||||||
|
blurRadius: 10,
|
||||||
|
offset: Offset(0, 0),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
child: UniversalImage(
|
child: UniversalImage(
|
||||||
path: albumArt,
|
path: albumArt,
|
||||||
|
placeholder: Assets.albumPlaceholder.path,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
@ -103,7 +125,7 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
currentTrack?.name ?? "Not playing",
|
currentTrack?.name ?? "Not playing",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
color: paletteColor.titleTextColor,
|
color: titleTextColor,
|
||||||
),
|
),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
@ -115,7 +137,7 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
style: theme.textTheme.bodyMedium!.copyWith(
|
style: theme.textTheme.bodyMedium!.copyWith(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: paletteColor.bodyTextColor,
|
color: bodyTextColor,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
@ -123,7 +145,7 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
currentTrack?.artists ?? [],
|
currentTrack?.artists ?? [],
|
||||||
textStyle: theme.textTheme.bodyMedium!.copyWith(
|
textStyle: theme.textTheme.bodyMedium!.copyWith(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: paletteColor.bodyTextColor,
|
color: bodyTextColor,
|
||||||
),
|
),
|
||||||
onRouteChange: (route) {
|
onRouteChange: (route) {
|
||||||
GoRouter.of(context).pop();
|
GoRouter.of(context).pop();
|
||||||
@ -134,7 +156,7 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 40),
|
const SizedBox(height: 40),
|
||||||
PlayerControls(iconColor: paletteColor.bodyTextColor),
|
PlayerControls(color: bodyTextColor),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
PlayerActions(
|
PlayerActions(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
@ -175,6 +197,7 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1778,4 +1778,4 @@ packages:
|
|||||||
version: "1.12.3"
|
version: "1.12.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.19.2 <3.0.0"
|
dart: ">=2.19.2 <3.0.0"
|
||||||
flutter: ">=3.7.0"
|
flutter: ">=3.3.0"
|
||||||
|
Loading…
Reference in New Issue
Block a user