perf(store): skip invisible backdrop blur on repo cards

Repo cards, category chips and the Store search field scroll over the plain page background, where a backdrop blur changes nothing visible but re-filters each panel on every scroll frame. MornyeGlass.navigation gains samplesBackdrop; these panels keep the same tint, rim, shadow and glass clarity without the blur pass. Floating chrome keeps its blur.
This commit is contained in:
zarzet committed 2026-10-01 19:41:44 +07:00
1 parent a21e46e6dd
commit be9e9903d9
5 files changed
+90 -2

No files matched your search

+3
View File
@@ -128,6 +128,7 @@ class _RepoTabState extends ConsumerState<RepoTab> {
controller: _searchController,
hintText: context.l10n.storeSearch,
clearTooltip: context.l10n.dialogClear,
samplesBackdrop: false,
onChanged: (value) =>
ref.read(repoProvider.notifier).setSearchQuery(value),
onClear: () =>
@@ -657,9 +658,11 @@ class _CategoryChip extends ConsumerWidget {
label: label,
onTap: onTap,
excludeSemantics: true,
// Scrolls with the list over the plain page: see ExtensionRepoCard.
child: MornyeGlass.navigation(
radius: 24,
blurEnabled: ref.watch(mornyeBlurEnabledProvider),
samplesBackdrop: false,
child: Material(
color: Colors.transparent,
child: InkWell(
+6
View File
@@ -24,6 +24,7 @@ class AppSearchField extends StatelessWidget {
this.suffixIcon,
this.focusNode,
this.autofocus = false,
this.samplesBackdrop = true,
});
final TextEditingController controller;
@@ -37,6 +38,10 @@ class AppSearchField extends StatelessWidget {
final FocusNode? focusNode;
final bool autofocus;
/// See [MornyeGlass.samplesBackdrop]: false where the field scrolls over
/// the plain page background.
final bool samplesBackdrop;
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
@@ -107,6 +112,7 @@ class AppSearchField extends StatelessWidget {
builder: (context, ref, child) => MornyeGlass.navigation(
radius: 28,
blurEnabled: ref.watch(mornyeBlurEnabledProvider),
samplesBackdrop: samplesBackdrop,
child: child!,
),
child: field,
+3
View File
@@ -34,9 +34,12 @@ class ExtensionRepoCard extends ConsumerWidget {
}
return Padding(
padding: margin,
// Repo pages scroll these cards over the plain page background, where a
// backdrop blur is invisible but re-filters the whole card every frame.
child: MornyeGlass.navigation(
radius: 24,
blurEnabled: ref.watch(mornyeBlurEnabledProvider),
samplesBackdrop: false,
child: Material(color: Colors.transparent, child: child),
),
);
+13 -2
View File
@@ -257,7 +257,8 @@ class MornyeGlass extends StatelessWidget {
this.backdropFilter,
}) : _useLens = true,
firstInGroup = true,
lastInGroup = true;
lastInGroup = true,
samplesBackdrop = true;
/// Shares the navigation bar's tint, blur and subtle edge reflections.
const MornyeGlass.navigation({
@@ -271,10 +272,17 @@ class MornyeGlass extends StatelessWidget {
this.tintOpacity,
this.tintColor,
this.backdropFilter,
this.samplesBackdrop = true,
}) : _useLens = false;
final Widget child;
final bool blurEnabled;
/// False for panels that only ever scroll over the plain page background.
/// Blurring a uniform backdrop changes nothing visible but re-filters the
/// panel's whole area on every scroll frame; the translucent tint, rim and
/// clarity preference stay exactly as with sampling.
final bool samplesBackdrop;
final double radius;
final bool _useLens;
final bool firstInGroup;
@@ -311,6 +319,7 @@ class MornyeGlass extends StatelessWidget {
tintOpacity: tintOpacity,
tintColor: tintColor,
backdropFilter: backdropFilter,
samplesBackdrop: samplesBackdrop,
child: useGlass ? lens(child) : child,
);
}
@@ -389,6 +398,7 @@ class _MornyeGlassSurface extends StatelessWidget {
this.tintOpacity,
this.tintColor,
this.backdropFilter,
this.samplesBackdrop = true,
});
final Widget child;
@@ -400,6 +410,7 @@ class _MornyeGlassSurface extends StatelessWidget {
final double? tintOpacity;
final Color? tintColor;
final ImageFilter? backdropFilter;
final bool samplesBackdrop;
@override
Widget build(BuildContext context) {
@@ -452,7 +463,7 @@ class _MornyeGlassSurface extends StatelessWidget {
),
child: ClipRRect(
borderRadius: shape,
child: useBlur
child: useBlur && samplesBackdrop
? BackdropFilter(
filter:
backdropFilter ?? (dark ? _darkBackdrop : _backdropBlur),
+65
View File
@@ -5,6 +5,7 @@ import 'package:liquid_glass_easy/liquid_glass_easy.dart';
import 'package:spotiflac_android/providers/runtime_profile_provider.dart';
import 'package:spotiflac_android/theme/mornye_theme.dart';
import 'package:spotiflac_android/widgets/app_switch.dart';
import 'package:spotiflac_android/widgets/extension_repo_card.dart';
import 'package:spotiflac_android/widgets/mornye_chrome.dart';
MornyeGlassLevel _levelFor({
@@ -183,6 +184,70 @@ void main() {
});
}
testWidgets('plain-page panels keep the glass tint without a backdrop pass', (
tester,
) async {
Color? tintOf(Finder panel) {
final decorated = tester
.widgetList<DecoratedBox>(
find.descendant(of: panel, matching: find.byType(DecoratedBox)),
)
.map((box) => box.decoration)
.whereType<BoxDecoration>()
.firstWhere((decoration) => decoration.color != null);
return decorated.color;
}
await tester.pumpWidget(
ProviderScope(
child: MaterialApp(
theme: MornyeTheme.build(Brightness.dark),
home: const Scaffold(
body: Column(
children: [
MornyeGlass.navigation(
key: ValueKey('sampled'),
blurEnabled: true,
child: SizedBox(width: 200, height: 40),
),
MornyeGlass.navigation(
key: ValueKey('plain-page'),
blurEnabled: true,
samplesBackdrop: false,
child: SizedBox(width: 200, height: 40),
),
ExtensionRepoCard(child: SizedBox(width: 200, height: 40)),
],
),
),
),
),
);
await tester.pump();
final sampled = find.byKey(const ValueKey('sampled'));
final plain = find.byKey(const ValueKey('plain-page'));
expect(
find.descendant(of: sampled, matching: find.byType(BackdropFilter)),
findsOneWidget,
);
expect(
find.descendant(of: plain, matching: find.byType(BackdropFilter)),
findsNothing,
);
expect(
find.descendant(
of: find.byType(ExtensionRepoCard),
matching: find.byType(BackdropFilter),
),
findsNothing,
);
// Same translucent material, so the page looks identical behind it.
expect(tintOf(plain), tintOf(sampled));
expect(tintOf(plain)!.a, lessThan(1));
expect(tester.takeException(), isNull);
});
testWidgets('liquid level keeps the shader glass', (tester) async {
await tester.pumpWidget(
ProviderScope(