diff --git a/packages/material_ui/lib/src/search_anchor.dart b/packages/material_ui/lib/src/search_anchor.dart index 845de4d3ddb..1052c7a45a7 100644 --- a/packages/material_ui/lib/src/search_anchor.dart +++ b/packages/material_ui/lib/src/search_anchor.dart @@ -1145,6 +1145,13 @@ class _ViewContentState extends State<_ViewContent> { return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { + final double keyboardInset = MediaQuery.viewInsetsOf(context).bottom; + // Keep non-fullscreen search views above the keyboard so suggestions + // remain visible while the search field is focused. + final double maxNonFullScreenHeight = math.max( + minHeight, + math.min(_viewRect.height, constraints.maxHeight - keyboardInset - _viewRect.top), + ); return Align( alignment: Alignment.topLeft, child: Transform.translate( @@ -1158,7 +1165,7 @@ class _ViewContentState extends State<_ViewContent> { minHeight: minHeight, maxHeight: widget.showFullScreenView ? math.max(constraints.maxHeight, minHeight) - : _viewRect.height, + : maxNonFullScreenHeight, ), child: Padding( padding: widget.showFullScreenView @@ -1242,9 +1249,12 @@ class _ViewContentState extends State<_ViewContent> { context: context, removeTop: true, child: ListView( - padding: EdgeInsets.only( - bottom: MediaQuery.viewInsetsOf(context).bottom, - ), + // Non-fullscreen views shrink to sit above the keyboard. + // Fullscreen views keep bottom padding so the list can + // scroll clear of the keyboard. + padding: widget.showFullScreenView + ? EdgeInsets.only(bottom: keyboardInset) + : EdgeInsets.zero, shrinkWrap: effectiveShrinkWrap, children: result.toList(), ), diff --git a/packages/material_ui/pending_changelogs/change_2026_09_11_1789108615837.yaml b/packages/material_ui/pending_changelogs/change_2026_09_11_1789108615837.yaml new file mode 100644 index 00000000000..8591a8493d5 --- /dev/null +++ b/packages/material_ui/pending_changelogs/change_2026_09_11_1789108615837.yaml @@ -0,0 +1,3 @@ +changelog: | + - Keeps non-fullscreen `SearchAnchor` suggestion views above the on-screen keyboard. +version: patch diff --git a/packages/material_ui/test/search_anchor_test.dart b/packages/material_ui/test/search_anchor_test.dart index d810d74dce0..86278342039 100644 --- a/packages/material_ui/test/search_anchor_test.dart +++ b/packages/material_ui/test/search_anchor_test.dart @@ -3671,6 +3671,51 @@ void main() { expect(suggestionsLoadingCount, 1); }); + // Regression test for https://github.com/flutter/flutter/issues/179503. + testWidgets('non-fullscreen SearchAnchor stays above the keyboard', (WidgetTester tester) async { + const double keyboardHeight = 300.0; + tester.view.physicalSize = const Size(800, 1000); + tester.view.devicePixelRatio = 1.0; + addTearDown(tester.view.reset); + + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Align( + alignment: Alignment.topCenter, + child: SearchAnchor( + isFullScreen: false, + builder: (BuildContext context, SearchController controller) { + return IconButton( + icon: const Icon(Icons.search), + onPressed: () { + controller.openView(); + }, + ); + }, + suggestionsBuilder: (BuildContext context, SearchController controller) { + return List.generate(20, (int index) { + return ListTile(title: Text('Suggestion $index')); + }); + }, + ), + ), + ), + ), + ); + + await tester.tap(find.widgetWithIcon(IconButton, Icons.search)); + await tester.pumpAndSettle(); + + tester.view.viewInsets = const FakeViewPadding(bottom: keyboardHeight); + await tester.pumpAndSettle(); + + final Rect searchViewRect = tester.getRect( + find.descendant(of: findViewContent(), matching: find.byType(ConstrainedBox)).first, + ); + expect(searchViewRect.bottom, lessThanOrEqualTo(1000 - keyboardHeight)); + }); + // This is a regression test for https://github.com/flutter/flutter/issues/139880. testWidgets('suggestionsBuilder is not called when the search value does not change', ( WidgetTester tester,