Fix floating bottom bar filling the screen when search is enabled
Build APK / build (push) Successful in 5m40s

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 <noreply@anthropic.com>
This commit is contained in:
2026-09-29 22:59:52 -04:00
co-authored by Claude Sonnet 5
parent 4927416277
commit 5f4caa2a3b
2 changed files with 57 additions and 11 deletions
+23 -11
View File
@@ -101,17 +101,29 @@ class NooBottomBar extends StatelessWidget {
child: Padding( child: Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 12), padding: const EdgeInsets.fromLTRB(16, 0, 16, 12),
child: hasSearch child: hasSearch
? Row( // A fixed-height SizedBox, not just a Row with
crossAxisAlignment: CrossAxisAlignment.stretch, // crossAxisAlignment.stretch - the bottomNavigationBar slot
children: [ // gives this widget a *loose* (unbounded-max) height
Expanded(child: pill), // constraint, and stretch on an unbounded cross axis blows up
const SizedBox(width: 8), // to fill the screen in profile/release (the assertion that
_SearchSatellite( // would catch it in debug is stripped there) - the same
destination: searchDestination!, // silent-oversizing bug files_controls_row.dart hit earlier
onTap: onSearchTap!, // from a different cause. Both children already size
size: barHeight, // 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, : pill,
), ),
+34
View File
@@ -303,5 +303,39 @@ void main() {
await tester.pump(); await tester.pump();
expect(searchTaps, 1); 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));
});
}); });
} }