mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-09-29 04:42:02 +02:00
feat(appearance): add an optional system font
This commit is contained in:
@@ -43,6 +43,8 @@
|
||||
"appearanceAccentBlue": "Blue",
|
||||
"appearanceAccentPurple": "Purple",
|
||||
"appearanceAccentPink": "Pink",
|
||||
"appearanceUseSystemFont": "Use system font",
|
||||
"appearanceUseSystemFontSubtitle": "Use your device’s default typeface for app text.",
|
||||
"@appearanceStyle": {
|
||||
"description": "Appearance section for choosing the app visual style"
|
||||
},
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
"appearanceAccentBlue": "Biru",
|
||||
"appearanceAccentPurple": "Ungu",
|
||||
"appearanceAccentPink": "Merah muda",
|
||||
"appearanceUseSystemFont": "Gunakan font sistem",
|
||||
"appearanceUseSystemFontSubtitle": "Gunakan jenis huruf bawaan perangkat untuk teks aplikasi.",
|
||||
"@appearanceStyle": {
|
||||
"description": "Bagian tampilan untuk memilih gaya visual aplikasi"
|
||||
},
|
||||
|
||||
@@ -6,6 +6,7 @@ const String kSeedColorKey = 'seed_color';
|
||||
const String kUseAmoledKey = 'use_amoled';
|
||||
const String kThemeStyleKey = 'theme_style';
|
||||
const String kMornyeAccentKey = 'mornye_accent';
|
||||
const String kUseSystemFontKey = 'use_system_font';
|
||||
|
||||
enum AppThemeStyle { material, mornye }
|
||||
|
||||
@@ -33,6 +34,7 @@ class ThemeSettings {
|
||||
final bool useAmoled;
|
||||
final AppThemeStyle style;
|
||||
final MornyeAccent mornyeAccent;
|
||||
final bool useSystemFont;
|
||||
|
||||
const ThemeSettings({
|
||||
this.themeMode = ThemeMode.system,
|
||||
@@ -41,6 +43,7 @@ class ThemeSettings {
|
||||
this.useAmoled = false,
|
||||
this.style = AppThemeStyle.material,
|
||||
this.mornyeAccent = MornyeAccent.red,
|
||||
this.useSystemFont = false,
|
||||
});
|
||||
|
||||
Color get seedColor => Color(seedColorValue);
|
||||
@@ -52,6 +55,7 @@ class ThemeSettings {
|
||||
bool? useAmoled,
|
||||
AppThemeStyle? style,
|
||||
MornyeAccent? mornyeAccent,
|
||||
bool? useSystemFont,
|
||||
}) {
|
||||
return ThemeSettings(
|
||||
themeMode: themeMode ?? this.themeMode,
|
||||
@@ -60,6 +64,7 @@ class ThemeSettings {
|
||||
useAmoled: useAmoled ?? this.useAmoled,
|
||||
style: style ?? this.style,
|
||||
mornyeAccent: mornyeAccent ?? this.mornyeAccent,
|
||||
useSystemFont: useSystemFont ?? this.useSystemFont,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -70,6 +75,7 @@ class ThemeSettings {
|
||||
kUseAmoledKey: useAmoled,
|
||||
kThemeStyleKey: style.name,
|
||||
kMornyeAccentKey: mornyeAccent.name,
|
||||
kUseSystemFontKey: useSystemFont,
|
||||
};
|
||||
|
||||
factory ThemeSettings.fromJson(Map<String, dynamic> json) {
|
||||
@@ -80,6 +86,7 @@ class ThemeSettings {
|
||||
useAmoled: json[kUseAmoledKey] as bool? ?? false,
|
||||
style: themeStyleFromString(json[kThemeStyleKey] as String?),
|
||||
mornyeAccent: mornyeAccentFromString(json[kMornyeAccentKey] as String?),
|
||||
useSystemFont: json[kUseSystemFontKey] as bool? ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -92,7 +99,8 @@ class ThemeSettings {
|
||||
other.seedColorValue == seedColorValue &&
|
||||
other.useAmoled == useAmoled &&
|
||||
other.style == style &&
|
||||
other.mornyeAccent == mornyeAccent;
|
||||
other.mornyeAccent == mornyeAccent &&
|
||||
other.useSystemFont == useSystemFont;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -102,7 +110,8 @@ class ThemeSettings {
|
||||
seedColorValue.hashCode ^
|
||||
useAmoled.hashCode ^
|
||||
style.hashCode ^
|
||||
mornyeAccent.hashCode;
|
||||
mornyeAccent.hashCode ^
|
||||
useSystemFont.hashCode;
|
||||
}
|
||||
|
||||
ThemeMode themeModeFromString(String? value) {
|
||||
|
||||
@@ -15,6 +15,7 @@ ThemeSettings loadBootstrapThemeSettings(SharedPreferences prefs) {
|
||||
useAmoled: prefs.getBool(kUseAmoledKey) ?? false,
|
||||
style: themeStyleFromString(prefs.getString(kThemeStyleKey)),
|
||||
mornyeAccent: mornyeAccentFromString(prefs.getString(kMornyeAccentKey)),
|
||||
useSystemFont: prefs.getBool(kUseSystemFontKey) ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -49,6 +50,7 @@ class ThemeNotifier extends Notifier<ThemeSettings> {
|
||||
await prefs.setBool(kUseAmoledKey, state.useAmoled);
|
||||
await prefs.setString(kThemeStyleKey, state.style.name);
|
||||
await prefs.setString(kMornyeAccentKey, state.mornyeAccent.name);
|
||||
await prefs.setBool(kUseSystemFontKey, state.useSystemFont);
|
||||
} catch (e) {
|
||||
debugPrint('Error saving theme settings: $e');
|
||||
}
|
||||
@@ -88,4 +90,9 @@ class ThemeNotifier extends Notifier<ThemeSettings> {
|
||||
state = state.copyWith(mornyeAccent: accent);
|
||||
await _saveToStorage();
|
||||
}
|
||||
|
||||
Future<void> setUseSystemFont(bool value) async {
|
||||
state = state.copyWith(useSystemFont: value);
|
||||
await _saveToStorage();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,6 +177,15 @@ class AppearanceSettingsPage extends ConsumerWidget {
|
||||
SliverToBoxAdapter(
|
||||
child: SettingsGroup(
|
||||
children: [
|
||||
SettingsSwitchItem(
|
||||
icon: Icons.font_download_outlined,
|
||||
title: context.l10n.appearanceUseSystemFont,
|
||||
subtitle: context.l10n.appearanceUseSystemFontSubtitle,
|
||||
value: themeSettings.useSystemFont,
|
||||
onChanged: ref
|
||||
.read(themeProvider.notifier)
|
||||
.setUseSystemFont,
|
||||
),
|
||||
SettingsSwitchItem(
|
||||
icon: Icons.animation,
|
||||
title: context.l10n.appearanceHeroAnimations,
|
||||
|
||||
@@ -114,6 +114,18 @@ class SettingsSearchCatalog {
|
||||
subtitle: l10n.appearanceAmoledDarkSubtitle,
|
||||
keywords: const ['black theme', 'oled'],
|
||||
),
|
||||
SettingsSearchEntry(
|
||||
icon: Icons.font_download_outlined,
|
||||
title: l10n.appearanceUseSystemFont,
|
||||
subtitle: l10n.appearanceUseSystemFontSubtitle,
|
||||
keywords: const [
|
||||
'font',
|
||||
'typeface',
|
||||
'typography',
|
||||
'system font',
|
||||
'huruf',
|
||||
],
|
||||
),
|
||||
SettingsSearchEntry(
|
||||
icon: Icons.animation,
|
||||
title: l10n.appearanceHeroAnimations,
|
||||
|
||||
@@ -325,13 +325,21 @@ class _ThemePicker extends ConsumerWidget {
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
child: Theme(
|
||||
data: style == AppThemeStyle.mornye
|
||||
? MornyeTheme.build(brightness, accent: settings.mornyeAccent)
|
||||
? MornyeTheme.build(
|
||||
brightness,
|
||||
accent: settings.mornyeAccent,
|
||||
useSystemFont: settings.useSystemFont,
|
||||
)
|
||||
: brightness == Brightness.dark
|
||||
? AppTheme.dark(
|
||||
seedColor: settings.seedColor,
|
||||
isAmoled: settings.useAmoled,
|
||||
useSystemFont: settings.useSystemFont,
|
||||
)
|
||||
: AppTheme.light(seedColor: settings.seedColor),
|
||||
: AppTheme.light(
|
||||
seedColor: settings.seedColor,
|
||||
useSystemFont: settings.useSystemFont,
|
||||
),
|
||||
child: _ThemeOption(
|
||||
style: style,
|
||||
selected: settings.style == style,
|
||||
|
||||
@@ -28,7 +28,11 @@ class AppTheme {
|
||||
},
|
||||
);
|
||||
|
||||
static ThemeData light({ColorScheme? dynamicScheme, Color? seedColor}) {
|
||||
static ThemeData light({
|
||||
ColorScheme? dynamicScheme,
|
||||
Color? seedColor,
|
||||
bool useSystemFont = false,
|
||||
}) {
|
||||
final scheme =
|
||||
dynamicScheme ??
|
||||
ColorScheme.fromSeed(
|
||||
@@ -58,7 +62,7 @@ class AppTheme {
|
||||
chipTheme: _chipTheme(scheme),
|
||||
dividerTheme: _dividerTheme(scheme),
|
||||
extensions: const <ThemeExtension<dynamic>>[AppTokens.standard],
|
||||
fontFamily: 'Google Sans Flex',
|
||||
fontFamily: useSystemFont ? null : 'Google Sans Flex',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -66,6 +70,7 @@ class AppTheme {
|
||||
ColorScheme? dynamicScheme,
|
||||
Color? seedColor,
|
||||
bool isAmoled = false,
|
||||
bool useSystemFont = false,
|
||||
}) {
|
||||
final scheme =
|
||||
dynamicScheme ??
|
||||
@@ -97,7 +102,7 @@ class AppTheme {
|
||||
chipTheme: _chipTheme(scheme),
|
||||
dividerTheme: _dividerTheme(scheme),
|
||||
extensions: const <ThemeExtension<dynamic>>[AppTokens.standard],
|
||||
fontFamily: 'Google Sans Flex',
|
||||
fontFamily: useSystemFont ? null : 'Google Sans Flex',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,16 @@ class DynamicColorWrapper extends ConsumerWidget {
|
||||
|
||||
if (themeSettings.style == AppThemeStyle.mornye) {
|
||||
return builder(
|
||||
MornyeTheme.build(Brightness.light, accent: themeSettings.mornyeAccent),
|
||||
MornyeTheme.build(Brightness.dark, accent: themeSettings.mornyeAccent),
|
||||
MornyeTheme.build(
|
||||
Brightness.light,
|
||||
accent: themeSettings.mornyeAccent,
|
||||
useSystemFont: themeSettings.useSystemFont,
|
||||
),
|
||||
MornyeTheme.build(
|
||||
Brightness.dark,
|
||||
accent: themeSettings.mornyeAccent,
|
||||
useSystemFont: themeSettings.useSystemFont,
|
||||
),
|
||||
themeSettings.themeMode,
|
||||
);
|
||||
}
|
||||
@@ -51,10 +59,14 @@ class DynamicColorWrapper extends ConsumerWidget {
|
||||
darkScheme = _applyAmoledColors(darkScheme);
|
||||
}
|
||||
|
||||
final lightTheme = AppTheme.light(dynamicScheme: lightScheme);
|
||||
final lightTheme = AppTheme.light(
|
||||
dynamicScheme: lightScheme,
|
||||
useSystemFont: themeSettings.useSystemFont,
|
||||
);
|
||||
final darkTheme = AppTheme.dark(
|
||||
dynamicScheme: darkScheme,
|
||||
isAmoled: themeSettings.useAmoled,
|
||||
useSystemFont: themeSettings.useSystemFont,
|
||||
);
|
||||
|
||||
return builder(lightTheme, darkTheme, themeSettings.themeMode);
|
||||
|
||||
+41
-13
@@ -9,10 +9,15 @@ import 'package:spotiflac_android/theme/app_tokens.dart';
|
||||
/// in the Mornye project; keep this palette independent of wallpaper colors.
|
||||
@immutable
|
||||
class MornyeTheme extends ThemeExtension<MornyeTheme> {
|
||||
const MornyeTheme({this.chromeSurface, this.accent = MornyeAccent.red});
|
||||
const MornyeTheme({
|
||||
this.chromeSurface,
|
||||
this.accent = MornyeAccent.red,
|
||||
this.useSystemFont = false,
|
||||
});
|
||||
|
||||
final Color? chromeSurface;
|
||||
final MornyeAccent accent;
|
||||
final bool useSystemFont;
|
||||
|
||||
/// A single translucent fill for controls inside an existing glass surface.
|
||||
static Color controlFill(BuildContext context, {bool enabled = true}) {
|
||||
@@ -97,14 +102,21 @@ class MornyeTheme extends ThemeExtension<MornyeTheme> {
|
||||
// many artists cannot grow the cache indefinitely. Platform is part of the
|
||||
// key because it controls fonts and route transitions.
|
||||
static final _themeCache =
|
||||
<(TargetPlatform, Brightness, Color?, MornyeAccent), ThemeData>{};
|
||||
<(TargetPlatform, Brightness, Color?, MornyeAccent, bool), ThemeData>{};
|
||||
|
||||
static ThemeData build(
|
||||
Brightness brightness, {
|
||||
Color? chromeSurface,
|
||||
MornyeAccent accent = MornyeAccent.red,
|
||||
bool useSystemFont = false,
|
||||
}) {
|
||||
final key = (defaultTargetPlatform, brightness, chromeSurface, accent);
|
||||
final key = (
|
||||
defaultTargetPlatform,
|
||||
brightness,
|
||||
chromeSurface,
|
||||
accent,
|
||||
useSystemFont,
|
||||
);
|
||||
final cached = _themeCache.remove(key);
|
||||
if (cached != null) {
|
||||
_themeCache[key] = cached;
|
||||
@@ -114,6 +126,7 @@ class MornyeTheme extends ThemeExtension<MornyeTheme> {
|
||||
brightness,
|
||||
chromeSurface: chromeSurface,
|
||||
selectedAccent: accent,
|
||||
useSystemFont: useSystemFont,
|
||||
);
|
||||
if (_themeCache.length >= 16) {
|
||||
_themeCache.remove(_themeCache.keys.first);
|
||||
@@ -134,6 +147,7 @@ class MornyeTheme extends ThemeExtension<MornyeTheme> {
|
||||
brightness ?? theme.brightness,
|
||||
chromeSurface: chromeSurface,
|
||||
accent: preferences?.accent ?? MornyeAccent.red,
|
||||
useSystemFont: preferences?.useSystemFont ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -141,6 +155,7 @@ class MornyeTheme extends ThemeExtension<MornyeTheme> {
|
||||
Brightness brightness, {
|
||||
Color? chromeSurface,
|
||||
required MornyeAccent selectedAccent,
|
||||
required bool useSystemFont,
|
||||
}) {
|
||||
final dark = brightness == Brightness.dark;
|
||||
final accent = accentColor(selectedAccent, brightness);
|
||||
@@ -179,8 +194,8 @@ class MornyeTheme extends ThemeExtension<MornyeTheme> {
|
||||
: const Color(0xffc6c6c8),
|
||||
surfaceTint: Colors.transparent,
|
||||
);
|
||||
// Apple platforms use the installed SF system faces. Inter is bundled for
|
||||
// other platforms; Cupertino's family names alone cannot provide SF there.
|
||||
// Cupertino's font proxies resolve to SF on Apple and the platform fallback
|
||||
// elsewhere. Inter remains the default on other platforms unless opted out.
|
||||
final apple =
|
||||
defaultTargetPlatform == TargetPlatform.iOS ||
|
||||
defaultTargetPlatform == TargetPlatform.macOS;
|
||||
@@ -188,12 +203,14 @@ class MornyeTheme extends ThemeExtension<MornyeTheme> {
|
||||
final text = type.textStyle.copyWith(
|
||||
inherit: true,
|
||||
color: foreground,
|
||||
fontFamily: apple ? type.textStyle.fontFamily : 'Inter',
|
||||
fontFamily: apple || useSystemFont ? type.textStyle.fontFamily : 'Inter',
|
||||
);
|
||||
final display = type.navLargeTitleTextStyle.copyWith(
|
||||
inherit: true,
|
||||
color: foreground,
|
||||
fontFamily: apple ? type.navLargeTitleTextStyle.fontFamily : 'Inter',
|
||||
fontFamily: apple || useSystemFont
|
||||
? type.navLargeTitleTextStyle.fontFamily
|
||||
: 'Inter',
|
||||
);
|
||||
// Supply complete styles: replacing a Material role with a bare TextStyle
|
||||
// loses its system family and retains Material tracking in other roles.
|
||||
@@ -243,7 +260,11 @@ class MornyeTheme extends ThemeExtension<MornyeTheme> {
|
||||
fontFamily: text.fontFamily,
|
||||
splashFactory: NoSplash.splashFactory,
|
||||
extensions: <ThemeExtension<dynamic>>[
|
||||
MornyeTheme(chromeSurface: chromeSurface, accent: selectedAccent),
|
||||
MornyeTheme(
|
||||
chromeSurface: chromeSurface,
|
||||
accent: selectedAccent,
|
||||
useSystemFont: useSystemFont,
|
||||
),
|
||||
tokens,
|
||||
],
|
||||
);
|
||||
@@ -335,16 +356,23 @@ class MornyeTheme extends ThemeExtension<MornyeTheme> {
|
||||
}
|
||||
|
||||
@override
|
||||
MornyeTheme copyWith({Color? chromeSurface, MornyeAccent? accent}) =>
|
||||
MornyeTheme(
|
||||
chromeSurface: chromeSurface ?? this.chromeSurface,
|
||||
accent: accent ?? this.accent,
|
||||
);
|
||||
MornyeTheme copyWith({
|
||||
Color? chromeSurface,
|
||||
MornyeAccent? accent,
|
||||
bool? useSystemFont,
|
||||
}) => MornyeTheme(
|
||||
chromeSurface: chromeSurface ?? this.chromeSurface,
|
||||
accent: accent ?? this.accent,
|
||||
useSystemFont: useSystemFont ?? this.useSystemFont,
|
||||
);
|
||||
|
||||
@override
|
||||
MornyeTheme lerp(covariant MornyeTheme? other, double t) => MornyeTheme(
|
||||
chromeSurface: Color.lerp(chromeSurface, other?.chromeSurface, t),
|
||||
accent: t < 0.5 ? accent : other?.accent ?? accent,
|
||||
useSystemFont: t < 0.5
|
||||
? useSystemFont
|
||||
: other?.useSystemFont ?? useSystemFont,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user