From 617ecc801cbe61887908282ef204fb8ce227d311 Mon Sep 17 00:00:00 2001 From: kenny lopez Date: Thu, 15 Oct 2026 07:59:31 -0400 Subject: [PATCH] Animate mobile search from the bottom Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz> Signed-off-by: kenny lopez --- mobile/lib/features/home/home_page.dart | 20 +++++++++- mobile/test/features/home/home_page_test.dart | 37 ++++++++++++++++++- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/mobile/lib/features/home/home_page.dart b/mobile/lib/features/home/home_page.dart index c1b3e7395e..134cba566d 100644 --- a/mobile/lib/features/home/home_page.dart +++ b/mobile/lib/features/home/home_page.dart @@ -105,8 +105,24 @@ class HomePage extends HookConsumerWidget { onSearch: () => Navigator.of(context).push( PageRouteBuilder( allowSnapshotting: false, - transitionDuration: Duration.zero, - reverseTransitionDuration: Duration.zero, + transitionDuration: reducedMotion + ? Duration.zero + : const Duration(milliseconds: 260), + reverseTransitionDuration: reducedMotion + ? Duration.zero + : const Duration(milliseconds: 220), + transitionsBuilder: (_, animation, secondaryAnimation, child) => + reducedMotion + ? child + : SlideTransition( + position: animation.drive( + Tween( + begin: const Offset(0, 1), + end: Offset.zero, + ).chain(CurveTween(curve: Curves.easeOutCubic)), + ), + child: child, + ), pageBuilder: (_, animation, secondaryAnimation) => const SearchPage(), ), diff --git a/mobile/test/features/home/home_page_test.dart b/mobile/test/features/home/home_page_test.dart index 14f7a35a6a..33361dab53 100644 --- a/mobile/test/features/home/home_page_test.dart +++ b/mobile/test/features/home/home_page_test.dart @@ -147,8 +147,11 @@ void main() { expect(find.byType(SearchPage), findsOneWidget); final searchRoute = ModalRoute.of(tester.element(find.byType(SearchPage)))!; expect(searchRoute.allowSnapshotting, isFalse); - expect(searchRoute.transitionDuration, Duration.zero); - expect(searchRoute.reverseTransitionDuration, Duration.zero); + expect(searchRoute.transitionDuration, const Duration(milliseconds: 260)); + expect( + searchRoute.reverseTransitionDuration, + const Duration(milliseconds: 220), + ); Navigator.of(tester.element(find.byType(SearchPage))).pop(); await tester.pumpAndSettle(); expect(find.byType(SearchPage), findsNothing); @@ -157,6 +160,36 @@ void main() { debugDefaultTargetPlatformOverride = null; }); + for (final reducedMotion in [false, true]) { + testWidgets('search slides vertically, reduced motion: $reducedMotion', ( + tester, + ) async { + await tester.pumpWidget( + await buildHome(disableAnimations: reducedMotion), + ); + await tester.pumpAndSettle(); + await tester.tap(find.byTooltip('Search')); + await tester.pump(); + await tester.pump(const Duration(milliseconds: 100)); + final search = find.byType(SearchPage); + expect(search, findsOneWidget); + expect(tester.getTopLeft(search).dx, 0); + expect(tester.getTopLeft(search).dy, reducedMotion ? 0 : greaterThan(0)); + await tester.pumpAndSettle(); + expect(tester.getTopLeft(search).dy, 0); + Navigator.of(tester.element(search)).pop(); + await tester.pump(); + await tester.pump(const Duration(milliseconds: 100)); + if (reducedMotion) { + expect(search, findsNothing); + } else { + expect(tester.getTopLeft(search).dy, greaterThan(0)); + } + await tester.pumpAndSettle(); + expect(search, findsNothing); + }); + } + testWidgets('keeps creation beside the tabs and search beside the avatar', ( tester, ) async {