From 9f0831624ae3b4d05a492b54621a37cf7885f796 Mon Sep 17 00:00:00 2001 From: knaeckeKami Date: Tue, 30 Jul 2019 00:35:40 +0200 Subject: [PATCH] Add searchFieldLabel to SearchDelegate in order to show a custom hint (#36409) * Add searchFieldLabel to SearchDelegate in order to show a custom hint label. * Add support for specifying textInputAction and keyboardType in SearchDelegate --- packages/flutter/lib/src/material/search.dart | 56 ++++++++++++++++++- .../flutter/test/material/search_test.dart | 51 +++++++++++++++-- 2 files changed, 101 insertions(+), 6 deletions(-) diff --git a/packages/flutter/lib/src/material/search.dart b/packages/flutter/lib/src/material/search.dart index 356854a2be..016cf604bf 100644 --- a/packages/flutter/lib/src/material/search.dart +++ b/packages/flutter/lib/src/material/search.dart @@ -89,6 +89,40 @@ Future showSearch({ /// for another [showSearch] call. abstract class SearchDelegate { + /// Constructor to be called by subclasses which may specify [searchFieldLabel], [keyboardType] and/or + /// [textInputAction]. + /// + /// {@tool sample} + /// ```dart + /// class CustomSearchHintDelegate extends SearchDelegate { + /// CustomSearchHintDelegate({ + /// String hintText, + /// }) : super( + /// searchFieldLabel: hintText, + /// keyboardType: TextInputType.text, + /// textInputAction: TextInputAction.search, + /// ); + /// + /// @override + /// Widget buildLeading(BuildContext context) => Text("leading"); + /// + /// @override + /// Widget buildSuggestions(BuildContext context) => Text("suggestions"); + /// + /// @override + /// Widget buildResults(BuildContext context) => Text('results'); + /// + /// @override + /// List buildActions(BuildContext context) => []; + /// } + /// ``` + /// {@end-tool} + SearchDelegate({ + this.searchFieldLabel, + this.keyboardType, + this.textInputAction = TextInputAction.search, + }); + /// Suggestions shown in the body of the search page while the user types a /// query into the search field. /// @@ -225,6 +259,22 @@ abstract class SearchDelegate { ..pop(result); } + /// The hint text that is shown in the search field when it is empty. + /// + /// If this value is set to null, the value of MaterialLocalizations.of(context).searchFieldLabel will be used instead. + final String searchFieldLabel; + + /// The type of action button to use for the keyboard. + /// + /// Defaults to the default value specified in [TextField]. + final TextInputType keyboardType; + + /// The text input action configuring the soft keyboard to a particular action + /// button. + /// + /// Defaults to [TextInputAction.search]. + final TextInputAction textInputAction; + /// [Animation] triggered when the search pages fades in or out. /// /// This animation is commonly used to animate [AnimatedIcon]s of @@ -417,7 +467,8 @@ class _SearchPageState extends State<_SearchPage> { Widget build(BuildContext context) { assert(debugCheckHasMaterialLocalizations(context)); final ThemeData theme = widget.delegate.appBarTheme(context); - final String searchFieldLabel = MaterialLocalizations.of(context).searchFieldLabel; + final String searchFieldLabel = widget.delegate.searchFieldLabel + ?? MaterialLocalizations.of(context).searchFieldLabel; Widget body; switch(widget.delegate._currentBody) { case _SearchBody.suggestions: @@ -459,7 +510,8 @@ class _SearchPageState extends State<_SearchPage> { controller: widget.delegate._queryTextController, focusNode: focusNode, style: theme.textTheme.title, - textInputAction: TextInputAction.search, + textInputAction: widget.delegate.textInputAction, + keyboardType: widget.delegate.keyboardType, onSubmitted: (String _) { widget.delegate.showResults(context); }, diff --git a/packages/flutter/test/material/search_test.dart b/packages/flutter/test/material/search_test.dart index 38e5ee306c..af3ae89dc3 100644 --- a/packages/flutter/test/material/search_test.dart +++ b/packages/flutter/test/material/search_test.dart @@ -479,7 +479,37 @@ void main() { expect(selectedResults, ['Result Foo']); }); - testWidgets('keyboard show search button', (WidgetTester tester) async { + testWidgets('Custom searchFieldLabel value', (WidgetTester tester) async { + const String searchHint = 'custom search hint'; + final String defaultSearchHint = const DefaultMaterialLocalizations().searchFieldLabel; + + final _TestSearchDelegate delegate = _TestSearchDelegate(searchHint: searchHint); + + await tester.pumpWidget(TestHomePage( + delegate: delegate, + )); + await tester.tap(find.byTooltip('Search')); + await tester.pumpAndSettle(); + + expect(find.text(searchHint), findsOneWidget); + expect(find.text(defaultSearchHint), findsNothing); + }); + + testWidgets('Default searchFieldLabel is used when it is set to null', (WidgetTester tester) async { + final String searchHint = const DefaultMaterialLocalizations().searchFieldLabel; + + final _TestSearchDelegate delegate = _TestSearchDelegate(); + + await tester.pumpWidget(TestHomePage( + delegate: delegate, + )); + await tester.tap(find.byTooltip('Search')); + await tester.pumpAndSettle(); + + expect(find.text(searchHint), findsOneWidget); + }); + + testWidgets('keyboard show search button by default', (WidgetTester tester) async { final _TestSearchDelegate delegate = _TestSearchDelegate(); await tester.pumpWidget(TestHomePage( @@ -493,6 +523,18 @@ void main() { expect(tester.testTextInput.setClientArgs['inputAction'], TextInputAction.search.toString()); }); + testWidgets('Custom textInputAction results in keyboard with corresponding button', (WidgetTester tester) async { + final _TestSearchDelegate delegate = _TestSearchDelegate(textInputAction: TextInputAction.done); + + await tester.pumpWidget(TestHomePage( + delegate: delegate, + )); + await tester.tap(find.byTooltip('Search')); + await tester.pumpAndSettle(); + await tester.showKeyboard(find.byType(TextField)); + expect(tester.testTextInput.setClientArgs['inputAction'], TextInputAction.done.toString()); + }); + group('contributes semantics', () { TestSemantics buildExpected({ String routeName }) { return TestSemantics.root( @@ -649,12 +691,13 @@ class TestHomePage extends StatelessWidget { } class _TestSearchDelegate extends SearchDelegate { - _TestSearchDelegate({ this.suggestions = 'Suggestions', this.result = 'Result', this.actions = const [], - }); + String searchHint, + TextInputAction textInputAction = TextInputAction.search, + }) : super(searchFieldLabel: searchHint, textInputAction: textInputAction); final String suggestions; final String result; @@ -704,4 +747,4 @@ class _TestSearchDelegate extends SearchDelegate { List buildActions(BuildContext context) { return actions; } -} \ No newline at end of file +}