feat(player): replace bg blur with gradient, proper fg color and align title and artist name

This commit is contained in:
Kingkor Roy Tirtho 2023-04-06 11:13:14 +06:00
parent 36396b7583
commit 159f03e7ca
7 changed files with 158 additions and 136 deletions

View File

@ -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,21 +109,24 @@ 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: Row( child: DefaultTextStyle(
mainAxisAlignment: MainAxisAlignment.spaceBetween, style:
children: [ theme.textTheme.bodySmall!.copyWith(color: color),
Text( child: Row(
"$currentMinutes:$currentSeconds", mainAxisAlignment: MainAxisAlignment.spaceBetween,
), children: [
Text("$totalMinutes:$totalSeconds"), Text("$currentMinutes:$currentSeconds"),
], 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,
), ),

View File

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

View File

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

View File

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

View File

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

View File

@ -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,120 +54,145 @@ 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(
appBar: PageWindowTitleBar( data: theme.iconTheme.copyWith(color: bodyTextColor),
backgroundColor: Colors.transparent, child: Scaffold(
foregroundColor: paletteColor.titleTextColor, appBar: PageWindowTitleBar(
toolbarOpacity: 1, backgroundColor: Colors.transparent,
leading: BackButton(color: paletteColor.titleTextColor), foregroundColor: titleTextColor,
), toolbarOpacity: 1,
extendBodyBehindAppBar: true, leading: const BackButton(),
body: DecoratedBox(
decoration: BoxDecoration(
image: DecorationImage(
image: UniversalImage.imageProvider(albumArt),
fit: BoxFit.cover,
),
), ),
child: BackdropFilter( extendBodyBehindAppBar: true,
filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15), body: Container(
child: SafeArea( decoration: BoxDecoration(
child: Padding( color: palette.dominantColor?.color,
padding: const EdgeInsets.all(8.0), gradient: LinearGradient(
child: Column( colors: [
children: [ palette.dominantColor?.color ?? theme.colorScheme.primary,
ClipRRect( palette.mutedColor?.color ?? theme.colorScheme.secondary,
borderRadius: BorderRadius.circular(20), ],
child: UniversalImage( transform: const GradientRotation(0.5),
path: albumArt, ),
), ),
), alignment: Alignment.center,
const SizedBox(height: 10), child: ConstrainedBox(
Container( constraints: const BoxConstraints(maxWidth: 580),
padding: const EdgeInsets.symmetric(horizontal: 16), child: SafeArea(
alignment: Alignment.centerLeft, child: Padding(
child: Column( padding: const EdgeInsets.all(8.0),
crossAxisAlignment: CrossAxisAlignment.start, child: Column(
children: [ children: [
AutoSizeText( DecoratedBox(
currentTrack?.name ?? "Not playing", decoration: BoxDecoration(
style: TextStyle( borderRadius: BorderRadius.circular(20),
fontSize: 20, boxShadow: const [
color: paletteColor.titleTextColor, BoxShadow(
color: Colors.black26,
spreadRadius: 2,
blurRadius: 10,
offset: Offset(0, 0),
), ),
maxLines: 1, ],
textAlign: TextAlign.start, ),
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: UniversalImage(
path: albumArt,
placeholder: Assets.albumPlaceholder.path,
), ),
if (isLocalTrack) ),
Text( ),
TypeConversionUtils.artists_X_String<Artist>( const SizedBox(height: 10),
currentTrack?.artists ?? [], Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
alignment: Alignment.centerLeft,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AutoSizeText(
currentTrack?.name ?? "Not playing",
style: TextStyle(
fontSize: 20,
color: titleTextColor,
), ),
style: theme.textTheme.bodyMedium!.copyWith( maxLines: 1,
fontWeight: FontWeight.bold, textAlign: TextAlign.start,
color: paletteColor.bodyTextColor,
),
)
else
TypeConversionUtils.artists_X_ClickableArtists(
currentTrack?.artists ?? [],
textStyle: theme.textTheme.bodyMedium!.copyWith(
fontWeight: FontWeight.bold,
color: paletteColor.bodyTextColor,
),
onRouteChange: (route) {
GoRouter.of(context).pop();
GoRouter.of(context).push(route);
},
), ),
if (isLocalTrack)
Text(
TypeConversionUtils.artists_X_String<Artist>(
currentTrack?.artists ?? [],
),
style: theme.textTheme.bodyMedium!.copyWith(
fontWeight: FontWeight.bold,
color: bodyTextColor,
),
)
else
TypeConversionUtils.artists_X_ClickableArtists(
currentTrack?.artists ?? [],
textStyle: theme.textTheme.bodyMedium!.copyWith(
fontWeight: FontWeight.bold,
color: bodyTextColor,
),
onRouteChange: (route) {
GoRouter.of(context).pop();
GoRouter.of(context).push(route);
},
),
],
),
),
const SizedBox(height: 40),
PlayerControls(color: bodyTextColor),
const Spacer(),
PlayerActions(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
floatingQueue: false,
extraActions: [
if (auth != null)
IconButton(
tooltip: "Open Lyrics",
icon: const Icon(SpotubeIcons.music),
onPressed: () {
showModalBottomSheet(
context: context,
isDismissible: true,
enableDrag: true,
isScrollControlled: true,
backgroundColor: Colors.black38,
barrierColor: Colors.black12,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
),
constraints: BoxConstraints(
maxHeight:
MediaQuery.of(context).size.height * 0.8,
),
builder: (context) =>
const LyricsPage(isModal: true),
);
},
)
], ],
), ),
), ],
const SizedBox(height: 40), ),
PlayerControls(iconColor: paletteColor.bodyTextColor),
const Spacer(),
PlayerActions(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
floatingQueue: false,
extraActions: [
if (auth != null)
IconButton(
tooltip: "Open Lyrics",
icon: const Icon(SpotubeIcons.music),
onPressed: () {
showModalBottomSheet(
context: context,
isDismissible: true,
enableDrag: true,
isScrollControlled: true,
backgroundColor: Colors.black38,
barrierColor: Colors.black12,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
),
constraints: BoxConstraints(
maxHeight:
MediaQuery.of(context).size.height * 0.8,
),
builder: (context) =>
const LyricsPage(isModal: true),
);
},
)
],
),
],
), ),
), ),
), ),

View File

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