diff --git a/dev/integration_tests/flutter_gallery/lib/demo/material/search_demo.dart b/dev/integration_tests/flutter_gallery/lib/demo/material/search_demo.dart index 2ef49eba6c4..d38d08096fb 100644 --- a/dev/integration_tests/flutter_gallery/lib/demo/material/search_demo.dart +++ b/dev/integration_tests/flutter_gallery/lib/demo/material/search_demo.dart @@ -223,6 +223,12 @@ class _SearchDemoSearchDelegate extends SearchDelegate { ), ]; } + + @override + PreferredSizeWidget buildBottom(BuildContext context) => const PreferredSize( + preferredSize: Size.fromHeight(56.0), + child: Text('Numbers'), + ); } class _ResultCard extends StatelessWidget { diff --git a/packages/flutter/lib/src/material/search.dart b/packages/flutter/lib/src/material/search.dart index c5bb50f0420..3d8475caa5a 100644 --- a/packages/flutter/lib/src/material/search.dart +++ b/packages/flutter/lib/src/material/search.dart @@ -69,8 +69,9 @@ Future showSearch({ /// /// The search page always shows an [AppBar] at the top where users can /// enter their search queries. The buttons shown before and after the search -/// query text field can be customized via [SearchDelegate.buildLeading] and -/// [SearchDelegate.buildActions]. +/// query text field can be customized via [SearchDelegate.buildLeading] +/// and [SearchDelegate.buildActions]. Additonally, a widget can be placed +/// across the bottom of the [AppBar] via [SearchDelegate.buildBottom]. /// /// The body below the [AppBar] can either show suggested queries (returned by /// [SearchDelegate.buildSuggestions]) or - once the user submits a search - the @@ -113,6 +114,12 @@ abstract class SearchDelegate { /// @override /// Widget buildLeading(BuildContext context) => Text("leading"); /// + /// PreferredSizeWidget buildBottom(BuildContext context) { + /// return PreferredSize( + /// preferredSize: Size.fromHeight(56.0), + /// child: Text("bottom")); + /// } + /// /// @override /// Widget buildSuggestions(BuildContext context) => Text("suggestions"); /// @@ -188,6 +195,16 @@ abstract class SearchDelegate { /// * [AppBar.actions], the intended use for the return value of this method. List buildActions(BuildContext context); + /// Widget to display across the bottom of the [AppBar]. + /// + /// Returns null by default, i.e. a bottom widget is not included. + /// + /// See also: + /// + /// * [AppBar.bottom], the intended use for the return value of this method. + /// + PreferredSizeWidget? buildBottom(BuildContext context) => null; + /// The theme used to configure the search page. /// /// The returned [ThemeData] will be used to wrap the entire search page, @@ -560,12 +577,13 @@ class _SearchPageState extends State<_SearchPage> { decoration: InputDecoration(hintText: searchFieldLabel), ), actions: widget.delegate.buildActions(context), + bottom: widget.delegate.buildBottom(context), ), body: AnimatedSwitcher( duration: const Duration(milliseconds: 300), child: body, ), - ), + ) ), ); } diff --git a/packages/flutter/test/material/search_test.dart b/packages/flutter/test/material/search_test.dart index 5a564c589b9..eb0a5c50d10 100644 --- a/packages/flutter/test/material/search_test.dart +++ b/packages/flutter/test/material/search_test.dart @@ -99,6 +99,7 @@ void main() { expect(find.text('HomeBody'), findsNothing); expect(find.text('HomeTitle'), findsNothing); expect(find.text('Suggestions'), findsOneWidget); + expect(find.text('Bottom'), findsOneWidget); // Simulate system back button final ByteData message = const JSONMethodCodec().encodeMethodCall(const MethodCall('popRoute')); @@ -633,6 +634,11 @@ void main() { textDirection: TextDirection.ltr, textSelection: const TextSelection(baseOffset: 0, extentOffset: 0), ), + TestSemantics( + id: 14, + label: 'Bottom', + textDirection: TextDirection.ltr, + ), ], ), TestSemantics( @@ -893,4 +899,12 @@ class _TestSearchDelegate extends SearchDelegate { List buildActions(BuildContext context) { return actions; } + + @override + PreferredSizeWidget buildBottom(BuildContext context) { + return const PreferredSize( + preferredSize: Size.fromHeight(56.0), + child: Text('Bottom'), + ); + } }