From 5f4caa2a3b25fd32478fd5388c074a87df76f6cc Mon Sep 17 00:00:00 2001 From: ayushya Date: Tue, 29 Sep 2026 22:59:52 -0400 Subject: [PATCH] Fix floating bottom bar filling the screen when search is enabled The Row wrapping the pill + search satellite used crossAxisAlignment.stretch, but Scaffold.bottomNavigationBar gives its child a loose (unbounded-max) height constraint - stretch on an unbounded cross axis blows up to fill all available space in profile/release builds (the debug assertion that would catch it is stripped there), same class of bug files_controls_row.dart hit earlier from a different cause. Both children already sized themselves explicitly, so stretch was never actually needed - wrapping the Row in a fixed-height SizedBox instead fixes it. Added a regression test that reproduces this inside a real Scaffold (the bare SizedBox host the other tests use gives a bounded height and doesn't trigger it) - confirmed it fails without the fix (height 600, the full test viewport) and passes with it. Co-Authored-By: Claude Sonnet 5 --- lib/widgets/noo/nav/noo_bottom_bar.dart | 34 +++++++++++++++++-------- test/widgets/noo/nav_test.dart | 34 +++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/lib/widgets/noo/nav/noo_bottom_bar.dart b/lib/widgets/noo/nav/noo_bottom_bar.dart index ef19ba7..380f922 100644 --- a/lib/widgets/noo/nav/noo_bottom_bar.dart +++ b/lib/widgets/noo/nav/noo_bottom_bar.dart @@ -101,17 +101,29 @@ class NooBottomBar extends StatelessWidget { child: Padding( padding: const EdgeInsets.fromLTRB(16, 0, 16, 12), child: hasSearch - ? Row( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Expanded(child: pill), - const SizedBox(width: 8), - _SearchSatellite( - destination: searchDestination!, - onTap: onSearchTap!, - size: barHeight, - ), - ], + // A fixed-height SizedBox, not just a Row with + // crossAxisAlignment.stretch - the bottomNavigationBar slot + // gives this widget a *loose* (unbounded-max) height + // constraint, and stretch on an unbounded cross axis blows up + // to fill the screen in profile/release (the assertion that + // would catch it in debug is stripped there) - the same + // silent-oversizing bug files_controls_row.dart hit earlier + // from a different cause. Both children already size + // themselves explicitly (pill's own `height`, the + // satellite's `size`), so stretch was never actually needed. + ? SizedBox( + height: barHeight, + child: Row( + children: [ + Expanded(child: pill), + const SizedBox(width: 8), + _SearchSatellite( + destination: searchDestination!, + onTap: onSearchTap!, + size: barHeight, + ), + ], + ), ) : pill, ), diff --git a/test/widgets/noo/nav_test.dart b/test/widgets/noo/nav_test.dart index ac3d53f..9a0ccf5 100644 --- a/test/widgets/noo/nav_test.dart +++ b/test/widgets/noo/nav_test.dart @@ -303,5 +303,39 @@ void main() { await tester.pump(); expect(searchTaps, 1); }); + + testWidgets('floating with search sizes correctly in a real Scaffold ' + '(regression: unbounded stretch used to fill the whole screen)', ( + tester, + ) async { + // A bare SizedBox host (as the other tests use) gives this widget a + // bounded height, masking this exact bug - Scaffold.bottomNavigationBar + // gives its child a *loose*, unbounded-max height instead, which is + // what a Row with crossAxisAlignment.stretch blows up against in + // profile/release (the debug assertion that would catch it is + // stripped there). Only a real Scaffold reproduces that. + await tester.pumpWidget( + MaterialApp( + theme: nooTheme(Brightness.light), + home: Scaffold( + body: const SizedBox.expand(), + bottomNavigationBar: NooBottomBar( + style: NooNavStyle.android, + barStyle: NooBottomBarStyle.floating, + destinations: _destinations, + selectedIndex: 0, + onSelected: (_) {}, + searchDestination: const NooNavDestination( + icon: LucideIcons.search, + label: 'Search', + ), + onSearchTap: () {}, + ), + ), + ), + ); + final size = tester.getSize(find.byType(NooBottomBar)); + expect(size.height, lessThan(150)); + }); }); }