mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
refactor: color scheme support based on shadcn colors
This commit is contained in:
parent
696875e4b5
commit
6c005592e3
@ -26,6 +26,7 @@ import 'package:spotube/hooks/configurators/use_fix_window_stretching.dart';
|
||||
import 'package:spotube/hooks/configurators/use_get_storage_perms.dart';
|
||||
import 'package:spotube/hooks/configurators/use_has_touch.dart';
|
||||
import 'package:spotube/models/database/database.dart';
|
||||
import 'package:spotube/modules/settings/color_scheme_picker_dialog.dart';
|
||||
import 'package:spotube/provider/audio_player/audio_player_streams.dart';
|
||||
import 'package:spotube/provider/database/database.dart';
|
||||
import 'package:spotube/provider/glance/glance.dart';
|
||||
@ -43,7 +44,6 @@ import 'package:spotube/services/logger/logger.dart';
|
||||
import 'package:spotube/services/wm_tools/wm_tools.dart';
|
||||
import 'package:spotube/utils/migrations/sandbox.dart';
|
||||
import 'package:spotube/utils/platform.dart';
|
||||
import 'package:system_theme/system_theme.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
||||
@ -83,8 +83,6 @@ Future<void> main(List<String> rawArgs) async {
|
||||
await windowManager.setPreventClose(true);
|
||||
}
|
||||
|
||||
await SystemTheme.accentColor.load();
|
||||
|
||||
if (!kIsWeb) {
|
||||
MetadataGod.initialize();
|
||||
}
|
||||
@ -133,8 +131,8 @@ class Spotube extends HookConsumerWidget {
|
||||
final themeMode =
|
||||
ref.watch(userPreferencesProvider.select((s) => s.themeMode));
|
||||
final locale = ref.watch(userPreferencesProvider.select((s) => s.locale));
|
||||
// final accentMaterialColor =
|
||||
// ref.watch(userPreferencesProvider.select((s) => s.accentColorScheme));
|
||||
final accentMaterialColor =
|
||||
ref.watch(userPreferencesProvider.select((s) => s.accentColorScheme));
|
||||
// final isAmoledTheme =
|
||||
// ref.watch(userPreferencesProvider.select((s) => s.amoledDarkTheme));
|
||||
// final paletteColor =
|
||||
@ -217,14 +215,18 @@ class Spotube extends HookConsumerWidget {
|
||||
theme: ThemeData(
|
||||
radius: .5,
|
||||
iconTheme: const IconThemeProperties(),
|
||||
colorScheme: ColorSchemes.lightOrange(),
|
||||
colorScheme:
|
||||
colorSchemeMap[accentMaterialColor.name]?.call(ThemeMode.light) ??
|
||||
ColorSchemes.lightOrange(),
|
||||
surfaceOpacity: .8,
|
||||
surfaceBlur: 10,
|
||||
),
|
||||
darkTheme: ThemeData(
|
||||
radius: .5,
|
||||
iconTheme: const IconThemeProperties(),
|
||||
colorScheme: ColorSchemes.darkOrange(),
|
||||
colorScheme:
|
||||
colorSchemeMap[accentMaterialColor.name]?.call(ThemeMode.dark) ??
|
||||
ColorSchemes.darkOrange(),
|
||||
surfaceOpacity: .8,
|
||||
surfaceBlur: 10,
|
||||
),
|
||||
|
@ -1,6 +1,5 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
||||
import 'package:spotube/collections/spotube_icons.dart';
|
||||
import 'package:spotube/components/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart';
|
||||
import 'package:spotube/extensions/context.dart';
|
||||
import 'package:spotube/pages/home/feed/feed_section.dart';
|
||||
@ -38,11 +37,8 @@ class HomePageFeedSection extends HookConsumerWidget {
|
||||
hasNextPage: false,
|
||||
isLoadingNextPage: false,
|
||||
onFetchMore: () {},
|
||||
titleTrailing: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Button.link(
|
||||
leading: const Icon(SpotubeIcons.angleRight),
|
||||
child: Text(context.l10n.browse_more),
|
||||
titleTrailing: Button.text(
|
||||
child: Text(context.l10n.browse_all),
|
||||
onPressed: () => ServiceUtils.pushNamed(
|
||||
context,
|
||||
HomeFeedSectionPage.name,
|
||||
@ -51,7 +47,6 @@ class HomePageFeedSection extends HookConsumerWidget {
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -74,7 +74,6 @@ class HomeGenresSection extends HookConsumerWidget {
|
||||
onPressed: () {
|
||||
context.pushNamed(GenrePage.name);
|
||||
},
|
||||
trailing: const Icon(SpotubeIcons.angleRight),
|
||||
child: Text(
|
||||
context.l10n.browse_all,
|
||||
).muted(),
|
||||
|
@ -1,10 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
||||
import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
|
||||
import 'package:spotube/extensions/context.dart';
|
||||
|
||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||
import 'package:system_theme/system_theme.dart';
|
||||
|
||||
class SpotubeColor extends Color {
|
||||
final String name;
|
||||
@ -25,23 +26,33 @@ class SpotubeColor extends Color {
|
||||
}
|
||||
|
||||
final Set<SpotubeColor> colorsMap = {
|
||||
SpotubeColor(SystemTheme.accentColor.accent.value, name: "System"),
|
||||
SpotubeColor(Colors.red.value, name: "Red"),
|
||||
SpotubeColor(Colors.pink.value, name: "Pink"),
|
||||
SpotubeColor(Colors.purple.value, name: "Purple"),
|
||||
SpotubeColor(Colors.deepPurple.value, name: "DeepPurple"),
|
||||
SpotubeColor(Colors.indigo.value, name: "Indigo"),
|
||||
SpotubeColor(Colors.blue.value, name: "Blue"),
|
||||
SpotubeColor(Colors.lightBlue.value, name: "LightBlue"),
|
||||
SpotubeColor(Colors.cyan.value, name: "Cyan"),
|
||||
SpotubeColor(Colors.teal.value, name: "Teal"),
|
||||
SpotubeColor(Colors.green.value, name: "Green"),
|
||||
SpotubeColor(Colors.lightGreen.value, name: "LightGreen"),
|
||||
SpotubeColor(Colors.yellow.value, name: "Yellow"),
|
||||
SpotubeColor(Colors.amber.value, name: "Amber"),
|
||||
SpotubeColor(Colors.orange.value, name: "Orange"),
|
||||
SpotubeColor(Colors.deepOrange.value, name: "DeepOrange"),
|
||||
SpotubeColor(Colors.brown.value, name: "Brown"),
|
||||
SpotubeColor(Colors.slate.value, name: "slate"),
|
||||
SpotubeColor(Colors.gray.value, name: "gray"),
|
||||
SpotubeColor(Colors.zinc.value, name: "zinc"),
|
||||
SpotubeColor(Colors.neutral.value, name: "neutral"),
|
||||
SpotubeColor(Colors.stone.value, name: "stone"),
|
||||
SpotubeColor(Colors.red.value, name: "red"),
|
||||
SpotubeColor(Colors.orange.value, name: "orange"),
|
||||
SpotubeColor(Colors.yellow.value, name: "yellow"),
|
||||
SpotubeColor(Colors.green.value, name: "green"),
|
||||
SpotubeColor(Colors.blue.value, name: "blue"),
|
||||
SpotubeColor(Colors.violet.value, name: "violet"),
|
||||
SpotubeColor(Colors.rose.value, name: "rose"),
|
||||
};
|
||||
|
||||
final colorSchemeMap = {
|
||||
"slate": ColorSchemes.slate,
|
||||
"gray": ColorSchemes.gray,
|
||||
"zinc": ColorSchemes.zinc,
|
||||
"neutral": ColorSchemes.neutral,
|
||||
"stone": ColorSchemes.stone,
|
||||
"red": ColorSchemes.red,
|
||||
"orange": ColorSchemes.orange,
|
||||
"yellow": ColorSchemes.yellow,
|
||||
"green": ColorSchemes.green,
|
||||
"blue": ColorSchemes.blue,
|
||||
"violet": ColorSchemes.violet,
|
||||
"rose": ColorSchemes.rose,
|
||||
};
|
||||
|
||||
class ColorSchemePickerDialog extends HookConsumerWidget {
|
||||
@ -51,180 +62,93 @@ class ColorSchemePickerDialog extends HookConsumerWidget {
|
||||
Widget build(BuildContext context, ref) {
|
||||
final preferences = ref.watch(userPreferencesProvider);
|
||||
final preferencesNotifier = ref.watch(userPreferencesProvider.notifier);
|
||||
|
||||
final scheme = preferences.accentColorScheme;
|
||||
final active = useState<String>(colorsMap.firstWhere(
|
||||
final active = useState<String?>(
|
||||
colorsMap.firstWhereOrNull(
|
||||
(element) {
|
||||
return scheme.name == element.name;
|
||||
},
|
||||
).name);
|
||||
|
||||
onOk() {
|
||||
preferencesNotifier.setAccentColorScheme(
|
||||
colorsMap.firstWhere(
|
||||
(element) {
|
||||
return element.name == active.value;
|
||||
},
|
||||
),
|
||||
)?.name,
|
||||
);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
return AlertDialog(
|
||||
title: Text(context.l10n.pick_color_scheme),
|
||||
title: Text(
|
||||
context.l10n.pick_color_scheme,
|
||||
style: TextStyle(color: context.theme.colorScheme.foreground),
|
||||
).large(),
|
||||
actions: [
|
||||
OutlinedButton(
|
||||
Button.outline(
|
||||
child: Text(context.l10n.cancel),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: onOk,
|
||||
Button.primary(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(context.l10n.save),
|
||||
),
|
||||
],
|
||||
content: SizedBox(
|
||||
height: 200,
|
||||
width: 400,
|
||||
child: ListView.separated(
|
||||
separatorBuilder: (context, index) {
|
||||
return const SizedBox(height: 10);
|
||||
},
|
||||
itemCount: colorsMap.length,
|
||||
itemBuilder: (context, index) {
|
||||
final color = colorsMap.elementAt(index);
|
||||
return ColorTile(
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: colorsMap.map(
|
||||
(color) {
|
||||
return ColorChip(
|
||||
name: color.name,
|
||||
color: color,
|
||||
isActive: active.value == color.name,
|
||||
isActive: color.name == active.value,
|
||||
onPressed: () {
|
||||
active.value = color.name;
|
||||
preferencesNotifier.setAccentColorScheme(
|
||||
colorsMap.firstWhere(
|
||||
(element) {
|
||||
return element.name == color.name;
|
||||
},
|
||||
tooltip: color.name,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ColorTile extends StatelessWidget {
|
||||
class ColorChip extends StatelessWidget {
|
||||
final String name;
|
||||
final Color color;
|
||||
final bool isActive;
|
||||
final void Function()? onPressed;
|
||||
final String? tooltip;
|
||||
final bool isCompact;
|
||||
const ColorTile({
|
||||
required this.color,
|
||||
this.isActive = false,
|
||||
this.onPressed,
|
||||
this.tooltip = "",
|
||||
this.isCompact = false,
|
||||
final VoidCallback onPressed;
|
||||
const ColorChip({
|
||||
super.key,
|
||||
required this.name,
|
||||
required this.color,
|
||||
required this.isActive,
|
||||
required this.onPressed,
|
||||
});
|
||||
|
||||
factory ColorTile.compact({
|
||||
required Color color,
|
||||
bool isActive = false,
|
||||
void Function()? onPressed,
|
||||
String? tooltip = "",
|
||||
Key? key,
|
||||
}) {
|
||||
return ColorTile(
|
||||
color: color,
|
||||
isActive: isActive,
|
||||
onPressed: onPressed,
|
||||
tooltip: tooltip,
|
||||
isCompact: true,
|
||||
key: key,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final lead = Container(
|
||||
height: 40,
|
||||
width: 40,
|
||||
decoration: BoxDecoration(
|
||||
border: isActive
|
||||
? Border.fromBorderSide(
|
||||
BorderSide(
|
||||
color: Color.lerp(
|
||||
theme.colorScheme.primary,
|
||||
theme.colorScheme.onPrimary,
|
||||
0.5,
|
||||
)!,
|
||||
width: 4,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: color,
|
||||
),
|
||||
);
|
||||
|
||||
if (isCompact) {
|
||||
return GestureDetector(
|
||||
onTap: onPressed,
|
||||
child: lead,
|
||||
);
|
||||
}
|
||||
|
||||
final colorScheme = ColorScheme.fromSeed(seedColor: color);
|
||||
|
||||
final palette = [
|
||||
colorScheme.primary,
|
||||
colorScheme.inversePrimary,
|
||||
colorScheme.primaryContainer,
|
||||
colorScheme.secondary,
|
||||
colorScheme.secondaryContainer,
|
||||
colorScheme.surface,
|
||||
colorScheme.surface,
|
||||
colorScheme.surfaceContainerHighest,
|
||||
colorScheme.onPrimary,
|
||||
colorScheme.onSurface,
|
||||
];
|
||||
|
||||
return GestureDetector(
|
||||
onTap: onPressed,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
lead,
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
tooltip!,
|
||||
style: theme.textTheme.bodyLarge?.copyWith(
|
||||
color: theme.colorScheme.primary,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Wrap(
|
||||
alignment: WrapAlignment.start,
|
||||
spacing: 10,
|
||||
runSpacing: 10,
|
||||
children: [
|
||||
...palette.map(
|
||||
(e) => Container(
|
||||
height: 20,
|
||||
return Chip(
|
||||
leading: Container(
|
||||
width: 20,
|
||||
height: 20,
|
||||
decoration: BoxDecoration(
|
||||
color: e,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: color,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
onPressed: onPressed,
|
||||
style: isActive ? ButtonVariance.primary : ButtonVariance.outline,
|
||||
child: Text(name),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -94,10 +94,11 @@ class SettingsAppearanceSection extends HookConsumerWidget {
|
||||
horizontal: 15,
|
||||
vertical: 5,
|
||||
),
|
||||
trailing: ColorTile.compact(
|
||||
trailing: ColorChip(
|
||||
color: preferences.accentColorScheme,
|
||||
name: preferences.accentColorScheme.name,
|
||||
onPressed: pickColorScheme(),
|
||||
isActive: true,
|
||||
isActive: false,
|
||||
),
|
||||
onTap: pickColorScheme(),
|
||||
),
|
||||
|
Loading…
Reference in New Issue
Block a user