mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-08-02 09:08:35 +02:00
feat(appearance): manual toggle to force backdrop blur on lower device tiers
This commit is contained in:
@@ -2914,6 +2914,18 @@ abstract class AppLocalizations {
|
||||
/// **'Fly covers between screens, e.g. when opening the player'**
|
||||
String get appearanceHeroAnimationsSubtitle;
|
||||
|
||||
/// Toggle that forces backdrop blur on even when the device profile disabled it
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Always use blur effects'**
|
||||
String get appearanceForceBlur;
|
||||
|
||||
/// Subtitle for the force-blur toggle
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Enable the navigation bar blur even on devices where it is off by default. May cost performance.'**
|
||||
String get appearanceForceBlurSubtitle;
|
||||
|
||||
/// Button - clear all queue items
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
||||
@@ -1598,6 +1598,13 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
String get appearanceHeroAnimationsSubtitle =>
|
||||
'Fly covers between screens, e.g. when opening the player';
|
||||
|
||||
@override
|
||||
String get appearanceForceBlur => 'Always use blur effects';
|
||||
|
||||
@override
|
||||
String get appearanceForceBlurSubtitle =>
|
||||
'Enable the navigation bar blur even on devices where it is off by default. May cost performance.';
|
||||
|
||||
@override
|
||||
String get queueClearAll => 'Alles löschen';
|
||||
|
||||
|
||||
@@ -1576,6 +1576,13 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
String get appearanceHeroAnimationsSubtitle =>
|
||||
'Fly covers between screens, e.g. when opening the player';
|
||||
|
||||
@override
|
||||
String get appearanceForceBlur => 'Always use blur effects';
|
||||
|
||||
@override
|
||||
String get appearanceForceBlurSubtitle =>
|
||||
'Enable the navigation bar blur even on devices where it is off by default. May cost performance.';
|
||||
|
||||
@override
|
||||
String get queueClearAll => 'Clear All';
|
||||
|
||||
|
||||
@@ -1576,6 +1576,13 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||
String get appearanceHeroAnimationsSubtitle =>
|
||||
'Fly covers between screens, e.g. when opening the player';
|
||||
|
||||
@override
|
||||
String get appearanceForceBlur => 'Always use blur effects';
|
||||
|
||||
@override
|
||||
String get appearanceForceBlurSubtitle =>
|
||||
'Enable the navigation bar blur even on devices where it is off by default. May cost performance.';
|
||||
|
||||
@override
|
||||
String get queueClearAll => 'Clear All';
|
||||
|
||||
|
||||
@@ -1620,6 +1620,13 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||
String get appearanceHeroAnimationsSubtitle =>
|
||||
'Des fenêtres contextuelles apparaissent entre les écrans, par exemple lors de l\'ouverture du lecteur';
|
||||
|
||||
@override
|
||||
String get appearanceForceBlur => 'Always use blur effects';
|
||||
|
||||
@override
|
||||
String get appearanceForceBlurSubtitle =>
|
||||
'Enable the navigation bar blur even on devices where it is off by default. May cost performance.';
|
||||
|
||||
@override
|
||||
String get queueClearAll => 'Tout effacer';
|
||||
|
||||
|
||||
@@ -1585,6 +1585,13 @@ class AppLocalizationsId extends AppLocalizations {
|
||||
String get appearanceHeroAnimationsSubtitle =>
|
||||
'Fly covers between screens, e.g. when opening the player';
|
||||
|
||||
@override
|
||||
String get appearanceForceBlur => 'Always use blur effects';
|
||||
|
||||
@override
|
||||
String get appearanceForceBlurSubtitle =>
|
||||
'Enable the navigation bar blur even on devices where it is off by default. May cost performance.';
|
||||
|
||||
@override
|
||||
String get queueClearAll => 'Hapus Semua';
|
||||
|
||||
|
||||
@@ -1567,6 +1567,13 @@ class AppLocalizationsJa extends AppLocalizations {
|
||||
String get appearanceHeroAnimationsSubtitle =>
|
||||
'Fly covers between screens, e.g. when opening the player';
|
||||
|
||||
@override
|
||||
String get appearanceForceBlur => 'Always use blur effects';
|
||||
|
||||
@override
|
||||
String get appearanceForceBlurSubtitle =>
|
||||
'Enable the navigation bar blur even on devices where it is off by default. May cost performance.';
|
||||
|
||||
@override
|
||||
String get queueClearAll => 'すべて消去';
|
||||
|
||||
|
||||
@@ -1543,6 +1543,13 @@ class AppLocalizationsKo extends AppLocalizations {
|
||||
String get appearanceHeroAnimationsSubtitle =>
|
||||
'화면 간 이동 시 표지 이미지가 날아가는 애니메이션을 표시합니다 (예시: 플레이어를 실행할 경우)';
|
||||
|
||||
@override
|
||||
String get appearanceForceBlur => 'Always use blur effects';
|
||||
|
||||
@override
|
||||
String get appearanceForceBlurSubtitle =>
|
||||
'Enable the navigation bar blur even on devices where it is off by default. May cost performance.';
|
||||
|
||||
@override
|
||||
String get queueClearAll => '모두 지우기';
|
||||
|
||||
|
||||
@@ -1576,6 +1576,13 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||
String get appearanceHeroAnimationsSubtitle =>
|
||||
'Fly covers between screens, e.g. when opening the player';
|
||||
|
||||
@override
|
||||
String get appearanceForceBlur => 'Always use blur effects';
|
||||
|
||||
@override
|
||||
String get appearanceForceBlurSubtitle =>
|
||||
'Enable the navigation bar blur even on devices where it is off by default. May cost performance.';
|
||||
|
||||
@override
|
||||
String get queueClearAll => 'Clear All';
|
||||
|
||||
|
||||
@@ -1591,6 +1591,13 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||
String get appearanceHeroAnimationsSubtitle =>
|
||||
'Fly covers between screens, e.g. when opening the player';
|
||||
|
||||
@override
|
||||
String get appearanceForceBlur => 'Always use blur effects';
|
||||
|
||||
@override
|
||||
String get appearanceForceBlurSubtitle =>
|
||||
'Enable the navigation bar blur even on devices where it is off by default. May cost performance.';
|
||||
|
||||
@override
|
||||
String get queueClearAll => 'Очистить всё';
|
||||
|
||||
|
||||
@@ -1593,6 +1593,13 @@ class AppLocalizationsTr extends AppLocalizations {
|
||||
String get appearanceHeroAnimationsSubtitle =>
|
||||
'Fly covers between screens, e.g. when opening the player';
|
||||
|
||||
@override
|
||||
String get appearanceForceBlur => 'Always use blur effects';
|
||||
|
||||
@override
|
||||
String get appearanceForceBlurSubtitle =>
|
||||
'Enable the navigation bar blur even on devices where it is off by default. May cost performance.';
|
||||
|
||||
@override
|
||||
String get queueClearAll => 'Tümünü Temizle';
|
||||
|
||||
|
||||
@@ -1599,6 +1599,13 @@ class AppLocalizationsUk extends AppLocalizations {
|
||||
String get appearanceHeroAnimationsSubtitle =>
|
||||
'Fly covers between screens, e.g. when opening the player';
|
||||
|
||||
@override
|
||||
String get appearanceForceBlur => 'Always use blur effects';
|
||||
|
||||
@override
|
||||
String get appearanceForceBlurSubtitle =>
|
||||
'Enable the navigation bar blur even on devices where it is off by default. May cost performance.';
|
||||
|
||||
@override
|
||||
String get queueClearAll => 'Усі';
|
||||
|
||||
|
||||
@@ -2091,6 +2091,14 @@
|
||||
"@appearanceHeroAnimationsSubtitle": {
|
||||
"description": "Subtitle for the Hero animations toggle"
|
||||
},
|
||||
"appearanceForceBlur": "Always use blur effects",
|
||||
"@appearanceForceBlur": {
|
||||
"description": "Toggle that forces backdrop blur on even when the device profile disabled it"
|
||||
},
|
||||
"appearanceForceBlurSubtitle": "Enable the navigation bar blur even on devices where it is off by default. May cost performance.",
|
||||
"@appearanceForceBlurSubtitle": {
|
||||
"description": "Subtitle for the force-blur toggle"
|
||||
},
|
||||
"queueClearAll": "Clear All",
|
||||
"@queueClearAll": {
|
||||
"description": "Button - clear all queue items"
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ void main() {
|
||||
lowEndDeviceProvider.overrideWithValue(
|
||||
runtimeProfile.disableOverscrollEffects,
|
||||
),
|
||||
backdropBlurEnabledProvider.overrideWithValue(
|
||||
deviceSupportsBackdropBlurProvider.overrideWithValue(
|
||||
runtimeProfile.enableBackdropBlur,
|
||||
),
|
||||
initialSettingsProvider.overrideWithValue(bootstrapSettings),
|
||||
|
||||
@@ -49,6 +49,11 @@ class AppSettings {
|
||||
/// Shared-element (Hero) flights, e.g. the mini player artwork expanding
|
||||
/// into the full player. Off skips the flights entirely.
|
||||
final bool heroAnimationsEnabled;
|
||||
|
||||
/// Forces the shell's backdrop blur on even when the startup runtime
|
||||
/// profile disabled it for this device tier. Off means "follow the
|
||||
/// device default".
|
||||
final bool forceBackdropBlur;
|
||||
final String
|
||||
extensionVerificationBrowserMode; // 'external_first' or 'in_app_first'
|
||||
final String locale;
|
||||
@@ -139,6 +144,7 @@ class AppSettings {
|
||||
this.albumFolderStructure = 'artist_album',
|
||||
this.showExtensionStore = true,
|
||||
this.heroAnimationsEnabled = true,
|
||||
this.forceBackdropBlur = false,
|
||||
this.extensionVerificationBrowserMode = 'in_app_first',
|
||||
this.locale = 'system',
|
||||
this.lyricsMode = 'embed',
|
||||
@@ -211,6 +217,7 @@ class AppSettings {
|
||||
String? albumFolderStructure,
|
||||
bool? showExtensionStore,
|
||||
bool? heroAnimationsEnabled,
|
||||
bool? forceBackdropBlur,
|
||||
String? extensionVerificationBrowserMode,
|
||||
String? locale,
|
||||
String? lyricsMode,
|
||||
@@ -293,6 +300,7 @@ class AppSettings {
|
||||
showExtensionStore: showExtensionStore ?? this.showExtensionStore,
|
||||
heroAnimationsEnabled:
|
||||
heroAnimationsEnabled ?? this.heroAnimationsEnabled,
|
||||
forceBackdropBlur: forceBackdropBlur ?? this.forceBackdropBlur,
|
||||
extensionVerificationBrowserMode:
|
||||
extensionVerificationBrowserMode ??
|
||||
this.extensionVerificationBrowserMode,
|
||||
|
||||
@@ -50,6 +50,7 @@ AppSettings _$AppSettingsFromJson(Map<String, dynamic> json) => AppSettings(
|
||||
json['albumFolderStructure'] as String? ?? 'artist_album',
|
||||
showExtensionStore: json['showExtensionStore'] as bool? ?? true,
|
||||
heroAnimationsEnabled: json['heroAnimationsEnabled'] as bool? ?? true,
|
||||
forceBackdropBlur: json['forceBackdropBlur'] as bool? ?? false,
|
||||
extensionVerificationBrowserMode:
|
||||
json['extensionVerificationBrowserMode'] as String? ?? 'in_app_first',
|
||||
locale: json['locale'] as String? ?? 'system',
|
||||
@@ -133,6 +134,7 @@ Map<String, dynamic> _$AppSettingsToJson(
|
||||
'albumFolderStructure': instance.albumFolderStructure,
|
||||
'showExtensionStore': instance.showExtensionStore,
|
||||
'heroAnimationsEnabled': instance.heroAnimationsEnabled,
|
||||
'forceBackdropBlur': instance.forceBackdropBlur,
|
||||
'extensionVerificationBrowserMode': instance.extensionVerificationBrowserMode,
|
||||
'locale': instance.locale,
|
||||
'lyricsMode': instance.lyricsMode,
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:spotiflac_android/providers/settings_provider.dart';
|
||||
|
||||
/// True on low-end hardware (arm32-only or low-RAM, resolved at startup in
|
||||
/// main.dart via a ProviderScope override). UI uses it to skip expensive
|
||||
/// effects like the shell's backdrop blur.
|
||||
final lowEndDeviceProvider = Provider<bool>((ref) => false);
|
||||
|
||||
/// Generic visual capability decided by the startup runtime profile. Effects
|
||||
/// can consume this without hardcoding a device model or platform source.
|
||||
final backdropBlurEnabledProvider = Provider<bool>((ref) => false);
|
||||
/// Raw device capability decided by the startup runtime profile (overridden
|
||||
/// in main.dart). Only the `high` tier enables blur by default.
|
||||
final deviceSupportsBackdropBlurProvider = Provider<bool>((ref) => false);
|
||||
|
||||
/// Whether backdrop blur effects should render: the device default, or the
|
||||
/// user's manual override from appearance settings (issue #488 — let lower
|
||||
/// tiers opt back in).
|
||||
final backdropBlurEnabledProvider = Provider<bool>((ref) {
|
||||
return ref.watch(deviceSupportsBackdropBlurProvider) ||
|
||||
ref.watch(settingsProvider.select((s) => s.forceBackdropBlur));
|
||||
});
|
||||
|
||||
@@ -658,6 +658,11 @@ class SettingsNotifier extends Notifier<AppSettings> {
|
||||
_saveSettings();
|
||||
}
|
||||
|
||||
void setForceBackdropBlur(bool enabled) {
|
||||
state = state.copyWith(forceBackdropBlur: enabled);
|
||||
_saveSettings();
|
||||
}
|
||||
|
||||
void setExtensionVerificationBrowserMode(String mode) {
|
||||
state = state.copyWith(
|
||||
extensionVerificationBrowserMode:
|
||||
|
||||
@@ -891,8 +891,9 @@ class _MainShellState extends ConsumerState<MainShell>
|
||||
],
|
||||
);
|
||||
// The backdrop blur re-filters everything scrolling underneath on
|
||||
// every frame; low-end devices get an opaque base instead.
|
||||
if (!ref.read(backdropBlurEnabledProvider)) {
|
||||
// every frame; low-end devices get an opaque base instead unless
|
||||
// the user forces blur on in appearance settings.
|
||||
if (!ref.watch(backdropBlurEnabledProvider)) {
|
||||
return ColoredBox(
|
||||
color: settingsGroupColor(context),
|
||||
child: bottomBar,
|
||||
|
||||
@@ -101,6 +101,15 @@ class AppearanceSettingsPage extends ConsumerWidget {
|
||||
onChanged: (value) => ref
|
||||
.read(settingsProvider.notifier)
|
||||
.setHeroAnimationsEnabled(value),
|
||||
),
|
||||
SettingsSwitchItem(
|
||||
icon: Icons.blur_on,
|
||||
title: context.l10n.appearanceForceBlur,
|
||||
subtitle: context.l10n.appearanceForceBlurSubtitle,
|
||||
value: settings.forceBackdropBlur,
|
||||
onChanged: (value) => ref
|
||||
.read(settingsProvider.notifier)
|
||||
.setForceBackdropBlur(value),
|
||||
showDivider: false,
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user