From b8a143dc6734db08fa73eeb14bed0b7be7a2b94f Mon Sep 17 00:00:00 2001 From: kenny lopez Date: Thu, 15 Oct 2026 08:11:29 -0400 Subject: [PATCH] Keep Android header backdrop composited beneath search Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz> Signed-off-by: kenny lopez --- mobile/lib/features/home/home_page.dart | 3 ++ .../features/channels/channels_page_test.dart | 51 +++++++++++++++++++ mobile/test/features/home/home_page_test.dart | 2 + 3 files changed, 56 insertions(+) diff --git a/mobile/lib/features/home/home_page.dart b/mobile/lib/features/home/home_page.dart index 134cba566d..0e609d7249 100644 --- a/mobile/lib/features/home/home_page.dart +++ b/mobile/lib/features/home/home_page.dart @@ -104,6 +104,9 @@ class HomePage extends HookConsumerWidget { settingsPageBuilder: settingsPageBuilder, onSearch: () => Navigator.of(context).push( PageRouteBuilder( + // Keep Android backdrop filters composited while search is open. + // UIKit controls must instead be hidden beneath the iOS route. + opaque: defaultTargetPlatform == TargetPlatform.iOS, allowSnapshotting: false, transitionDuration: reducedMotion ? Duration.zero diff --git a/mobile/test/features/channels/channels_page_test.dart b/mobile/test/features/channels/channels_page_test.dart index 5d6047e9b8..e4c0c84c98 100644 --- a/mobile/test/features/channels/channels_page_test.dart +++ b/mobile/test/features/channels/channels_page_test.dart @@ -3,6 +3,7 @@ import 'package:buzz/shared/widgets/app_route_observer.dart'; import 'package:buzz/features/pairing/pairing_provider.dart'; import 'package:buzz/shared/community/paired_community_landing.dart'; import 'dart:async'; +import 'package:shared_preferences/shared_preferences.dart'; import '../profile/presence_snapshot_test.dart' show PresenceTestRelay, presenceEvent; @@ -677,6 +678,56 @@ void main() { expect(appBar.frostedBlurSigma, 23.12); }); + testWidgets('Home keeps header blur after returning from search', ( + tester, + ) async { + SharedPreferences.setMockInitialValues({}); + final prefs = await SharedPreferences.getInstance(); + tester.view.physicalSize = const Size(320, 320); + tester.view.devicePixelRatio = 1; + addTearDown(tester.view.reset); + await tester.pumpWidget( + buildTestable( + realHome: true, + topSectionGradient: const LinearGradient( + colors: [Colors.yellow, Colors.blue], + ), + overrides: [ + savedPrefsProvider.overrideWithValue(prefs), + channelsProvider.overrideWith(() => _FakeNotifier(testChannels)), + ], + ), + ); + await tester.pumpAndSettle(); + final position = tester + .state( + find + .descendant( + of: find.byType(CustomScrollView), + matching: find.byType(Scrollable), + ) + .first, + ) + .position; + position.jumpTo(position.maxScrollExtent); + await tester.pumpAndSettle(); + final header = find.byType(FrostedAppBar).first; + expect( + find.descendant(of: header, matching: find.byType(BackdropFilter)), + findsOneWidget, + ); + final offset = position.pixels; + await tester.tap(find.byTooltip('Search')); + await tester.pumpAndSettle(); + tester.state(find.byType(Navigator).first).pop(); + await tester.pumpAndSettle(); + expect(position.pixels, offset); + expect( + find.descendant(of: header, matching: find.byType(BackdropFilter)), + findsOneWidget, + ); + }); + for (final customRoute in [false, true]) { testWidgets( 'Home replays native scroll state on return: custom=$customRoute', diff --git a/mobile/test/features/home/home_page_test.dart b/mobile/test/features/home/home_page_test.dart index 33361dab53..1da7c17442 100644 --- a/mobile/test/features/home/home_page_test.dart +++ b/mobile/test/features/home/home_page_test.dart @@ -147,6 +147,7 @@ void main() { expect(find.byType(SearchPage), findsOneWidget); final searchRoute = ModalRoute.of(tester.element(find.byType(SearchPage)))!; expect(searchRoute.allowSnapshotting, isFalse); + expect(searchRoute.opaque, isTrue); expect(searchRoute.transitionDuration, const Duration(milliseconds: 260)); expect( searchRoute.reverseTransitionDuration, @@ -177,6 +178,7 @@ void main() { expect(tester.getTopLeft(search).dy, reducedMotion ? 0 : greaterThan(0)); await tester.pumpAndSettle(); expect(tester.getTopLeft(search).dy, 0); + expect(ModalRoute.of(tester.element(search))!.opaque, isFalse); Navigator.of(tester.element(search)).pop(); await tester.pump(); await tester.pump(const Duration(milliseconds: 100));