Skip to content

Commit 18b239f

Browse files
authored
Added keyboardType & textInputAction props to SearchBar, SearchAnchor & SearchAnchor.bar (#138553)
Added `keyboardType` & `textInputAction` props to `SearchBar`, `SearchAnchor` & `SearchAnchor.bar` Fixes #138483
1 parent 89edac4 commit 18b239f

File tree

3 files changed

+230
-2
lines changed

3 files changed

+230
-2
lines changed

packages/flutter/lib/src/material/search_anchor.dart

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ class SearchAnchor extends StatefulWidget {
131131
this.viewOnSubmitted,
132132
required this.builder,
133133
required this.suggestionsBuilder,
134+
this.textInputAction,
135+
this.keyboardType,
134136
});
135137

136138
/// Create a [SearchAnchor] that has a [SearchBar] which opens a search view.
@@ -174,7 +176,9 @@ class SearchAnchor extends StatefulWidget {
174176
bool? isFullScreen,
175177
SearchController searchController,
176178
TextCapitalization textCapitalization,
177-
required SuggestionsBuilder suggestionsBuilder
179+
required SuggestionsBuilder suggestionsBuilder,
180+
TextInputAction? textInputAction,
181+
TextInputType? keyboardType,
178182
}) = _SearchAnchorWithSearchBar;
179183

180184
/// Whether the search view grows to fill the entire screen when the
@@ -323,6 +327,14 @@ class SearchAnchor extends StatefulWidget {
323327
/// To get a different layout, use [viewBuilder] to override.
324328
final SuggestionsBuilder suggestionsBuilder;
325329

330+
/// {@macro flutter.widgets.TextField.textInputAction}
331+
final TextInputAction? textInputAction;
332+
333+
/// The type of action button to use for the keyboard.
334+
///
335+
/// Defaults to the default value specified in [TextField].
336+
final TextInputType? keyboardType;
337+
326338
@override
327339
State<SearchAnchor> createState() => _SearchAnchorState();
328340
}
@@ -396,6 +408,8 @@ class _SearchAnchorState extends State<SearchAnchor> {
396408
suggestionsBuilder: widget.suggestionsBuilder,
397409
textCapitalization: widget.textCapitalization,
398410
capturedThemes: InheritedTheme.capture(from: context, to: navigator.context),
411+
textInputAction: widget.textInputAction,
412+
keyboardType: widget.keyboardType,
399413
));
400414
}
401415

@@ -469,6 +483,8 @@ class _SearchViewRoute extends PopupRoute<_SearchViewRoute> {
469483
required this.searchController,
470484
required this.suggestionsBuilder,
471485
required this.capturedThemes,
486+
this.textInputAction,
487+
this.keyboardType,
472488
});
473489

474490
final ValueChanged<String>? viewOnChanged;
@@ -494,6 +510,8 @@ class _SearchViewRoute extends PopupRoute<_SearchViewRoute> {
494510
final SearchController searchController;
495511
final SuggestionsBuilder suggestionsBuilder;
496512
final CapturedThemes capturedThemes;
513+
final TextInputAction? textInputAction;
514+
final TextInputType? keyboardType;
497515

498516
@override
499517
Color? get barrierColor => Colors.transparent;
@@ -637,6 +655,8 @@ class _SearchViewRoute extends PopupRoute<_SearchViewRoute> {
637655
searchController: searchController,
638656
suggestionsBuilder: suggestionsBuilder,
639657
textCapitalization: textCapitalization,
658+
textInputAction: textInputAction,
659+
keyboardType: keyboardType,
640660
),
641661
),
642662
);
@@ -673,6 +693,8 @@ class _ViewContent extends StatefulWidget {
673693
required this.viewRect,
674694
required this.searchController,
675695
required this.suggestionsBuilder,
696+
this.textInputAction,
697+
this.keyboardType,
676698
});
677699

678700
final ValueChanged<String>? viewOnChanged;
@@ -697,6 +719,8 @@ class _ViewContent extends StatefulWidget {
697719
final Rect viewRect;
698720
final SearchController searchController;
699721
final SuggestionsBuilder suggestionsBuilder;
722+
final TextInputAction? textInputAction;
723+
final TextInputType? keyboardType;
700724

701725
@override
702726
State<_ViewContent> createState() => _ViewContentState();
@@ -876,6 +900,8 @@ class _ViewContentState extends State<_ViewContent> {
876900
},
877901
onSubmitted: widget.viewOnSubmitted,
878902
textCapitalization: widget.textCapitalization,
903+
textInputAction: widget.textInputAction,
904+
keyboardType: widget.keyboardType,
879905
),
880906
),
881907
),
@@ -939,7 +965,9 @@ class _SearchAnchorWithSearchBar extends SearchAnchor {
939965
super.textCapitalization,
940966
ValueChanged<String>? onChanged,
941967
ValueChanged<String>? onSubmitted,
942-
required super.suggestionsBuilder
968+
required super.suggestionsBuilder,
969+
super.textInputAction,
970+
super.keyboardType,
943971
}) : super(
944972
viewHintText: viewHintText ?? barHintText,
945973
headerTextStyle: viewHeaderTextStyle,
@@ -970,6 +998,8 @@ class _SearchAnchorWithSearchBar extends SearchAnchor {
970998
leading: barLeading ?? const Icon(Icons.search),
971999
trailing: barTrailing,
9721000
textCapitalization: textCapitalization,
1001+
textInputAction: textInputAction,
1002+
keyboardType: keyboardType,
9731003
);
9741004
}
9751005
);
@@ -1086,6 +1116,8 @@ class SearchBar extends StatefulWidget {
10861116
this.hintStyle,
10871117
this.textCapitalization,
10881118
this.autoFocus = false,
1119+
this.textInputAction,
1120+
this.keyboardType,
10891121
});
10901122

10911123
/// Controls the text being edited in the search bar's text field.
@@ -1210,6 +1242,14 @@ class SearchBar extends StatefulWidget {
12101242
/// {@macro flutter.widgets.editableText.autofocus}
12111243
final bool autoFocus;
12121244

1245+
/// {@macro flutter.widgets.TextField.textInputAction}
1246+
final TextInputAction? textInputAction;
1247+
1248+
/// The type of action button to use for the keyboard.
1249+
///
1250+
/// Defaults to the default value specified in [TextField].
1251+
final TextInputType? keyboardType;
1252+
12131253
@override
12141254
State<SearchBar> createState() => _SearchBarState();
12151255
}
@@ -1349,6 +1389,8 @@ class _SearchBarState extends State<SearchBar> {
13491389
isDense: true,
13501390
)),
13511391
textCapitalization: effectiveTextCapitalization,
1392+
textInputAction: widget.textInputAction,
1393+
keyboardType: widget.keyboardType,
13521394
),
13531395
),
13541396
),

packages/flutter/lib/src/material/text_field.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,10 +419,12 @@ class TextField extends StatefulWidget {
419419
/// {@macro flutter.widgets.editableText.keyboardType}
420420
final TextInputType keyboardType;
421421

422+
/// {@template flutter.widgets.TextField.textInputAction}
422423
/// The type of action button to use for the keyboard.
423424
///
424425
/// Defaults to [TextInputAction.newline] if [keyboardType] is
425426
/// [TextInputType.multiline] and [TextInputAction.done] otherwise.
427+
/// {@endtemplate}
426428
final TextInputAction? textInputAction;
427429

428430
/// {@macro flutter.widgets.editableText.textCapitalization}

packages/flutter/test/material/search_anchor_test.dart

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2762,6 +2762,190 @@ void main() {
27622762
).first);
27632763
expect(suggestionMaterial.color, localTheme.cardTheme.color);
27642764
});
2765+
2766+
testWidgetsWithLeakTracking('SearchBar respects keyboardType property', (WidgetTester tester) async {
2767+
Widget buildSearchBar(TextInputType keyboardType) {
2768+
return MaterialApp(
2769+
home: Center(
2770+
child: Material(
2771+
child: SearchBar(
2772+
keyboardType: keyboardType,
2773+
),
2774+
),
2775+
),
2776+
);
2777+
}
2778+
await tester.pumpWidget(buildSearchBar(TextInputType.number));
2779+
await tester.pump();
2780+
TextField textField = tester.widget(find.byType(TextField));
2781+
expect(textField.keyboardType, TextInputType.number);
2782+
2783+
await tester.pumpWidget(buildSearchBar(TextInputType.phone));
2784+
await tester.pump();
2785+
textField = tester.widget(find.byType(TextField));
2786+
expect(textField.keyboardType, TextInputType.phone);
2787+
});
2788+
2789+
testWidgetsWithLeakTracking('SearchAnchor respects keyboardType property', (WidgetTester tester) async {
2790+
Widget buildSearchAnchor(TextInputType keyboardType) {
2791+
return MaterialApp(
2792+
home: Center(
2793+
child: Material(
2794+
child: SearchAnchor(
2795+
keyboardType: keyboardType,
2796+
builder: (BuildContext context, SearchController controller) {
2797+
return IconButton(
2798+
icon: const Icon(Icons.ac_unit),
2799+
onPressed: () {
2800+
controller.openView();
2801+
},
2802+
);
2803+
},
2804+
suggestionsBuilder: (BuildContext context, SearchController controller) {
2805+
return <Widget>[];
2806+
},
2807+
),
2808+
),
2809+
),
2810+
);
2811+
}
2812+
await tester.pumpWidget(buildSearchAnchor(TextInputType.number));
2813+
await tester.pump();
2814+
await tester.tap(find.widgetWithIcon(IconButton, Icons.ac_unit));
2815+
await tester.pumpAndSettle();
2816+
TextField textField = tester.widget(find.byType(TextField));
2817+
expect(textField.keyboardType, TextInputType.number);
2818+
await tester.tap(find.widgetWithIcon(IconButton, Icons.arrow_back));
2819+
await tester.pump();
2820+
2821+
await tester.pumpWidget(buildSearchAnchor(TextInputType.phone));
2822+
await tester.pump();
2823+
await tester.tap(find.widgetWithIcon(IconButton, Icons.ac_unit));
2824+
await tester.pumpAndSettle();
2825+
textField = tester.widget(find.byType(TextField));
2826+
expect(textField.keyboardType, TextInputType.phone);
2827+
});
2828+
2829+
testWidgetsWithLeakTracking('SearchAnchor.bar respects keyboardType property', (WidgetTester tester) async {
2830+
Widget buildSearchAnchor(TextInputType keyboardType) {
2831+
return MaterialApp(
2832+
home: Center(
2833+
child: Material(
2834+
child: SearchAnchor.bar(
2835+
keyboardType: keyboardType,
2836+
suggestionsBuilder: (BuildContext context, SearchController controller) {
2837+
return <Widget>[];
2838+
},
2839+
),
2840+
),
2841+
),
2842+
);
2843+
}
2844+
await tester.pumpWidget(buildSearchAnchor(TextInputType.number));
2845+
await tester.pump();
2846+
await tester.tap(find.byType(SearchBar)); // Open search view.
2847+
await tester.pumpAndSettle();
2848+
final Finder textFieldFinder = find.descendant(of: findViewContent(), matching: find.byType(TextField));
2849+
final TextField textFieldInView = tester.widget<TextField>(textFieldFinder);
2850+
expect(textFieldInView.keyboardType, TextInputType.number);
2851+
// Close search view.
2852+
await tester.tap(find.widgetWithIcon(IconButton, Icons.arrow_back));
2853+
await tester.pumpAndSettle();
2854+
final TextField textField = tester.widget(find.byType(TextField));
2855+
expect(textField.keyboardType, TextInputType.number);
2856+
});
2857+
2858+
testWidgetsWithLeakTracking('SearchBar respects textInputAction property', (WidgetTester tester) async {
2859+
Widget buildSearchBar(TextInputAction textInputAction) {
2860+
return MaterialApp(
2861+
home: Center(
2862+
child: Material(
2863+
child: SearchBar(
2864+
textInputAction: textInputAction,
2865+
),
2866+
),
2867+
),
2868+
);
2869+
}
2870+
await tester.pumpWidget(buildSearchBar(TextInputAction.previous));
2871+
await tester.pump();
2872+
TextField textField = tester.widget(find.byType(TextField));
2873+
expect(textField.textInputAction, TextInputAction.previous);
2874+
2875+
await tester.pumpWidget(buildSearchBar(TextInputAction.send));
2876+
await tester.pump();
2877+
textField = tester.widget(find.byType(TextField));
2878+
expect(textField.textInputAction, TextInputAction.send);
2879+
});
2880+
2881+
testWidgetsWithLeakTracking('SearchAnchor respects textInputAction property', (WidgetTester tester) async {
2882+
Widget buildSearchAnchor(TextInputAction textInputAction) {
2883+
return MaterialApp(
2884+
home: Center(
2885+
child: Material(
2886+
child: SearchAnchor(
2887+
textInputAction: textInputAction,
2888+
builder: (BuildContext context, SearchController controller) {
2889+
return IconButton(
2890+
icon: const Icon(Icons.ac_unit),
2891+
onPressed: () {
2892+
controller.openView();
2893+
},
2894+
);
2895+
},
2896+
suggestionsBuilder: (BuildContext context, SearchController controller) {
2897+
return <Widget>[];
2898+
},
2899+
),
2900+
),
2901+
),
2902+
);
2903+
}
2904+
await tester.pumpWidget(buildSearchAnchor(TextInputAction.previous));
2905+
await tester.pump();
2906+
await tester.tap(find.widgetWithIcon(IconButton, Icons.ac_unit));
2907+
await tester.pumpAndSettle();
2908+
TextField textField = tester.widget(find.byType(TextField));
2909+
expect(textField.textInputAction, TextInputAction.previous);
2910+
await tester.tap(find.widgetWithIcon(IconButton, Icons.arrow_back));
2911+
await tester.pump();
2912+
2913+
await tester.pumpWidget(buildSearchAnchor(TextInputAction.send));
2914+
await tester.pump();
2915+
await tester.tap(find.widgetWithIcon(IconButton, Icons.ac_unit));
2916+
await tester.pumpAndSettle();
2917+
textField = tester.widget(find.byType(TextField));
2918+
expect(textField.textInputAction, TextInputAction.send);
2919+
});
2920+
2921+
testWidgetsWithLeakTracking('SearchAnchor.bar respects textInputAction property', (WidgetTester tester) async {
2922+
Widget buildSearchAnchor(TextInputAction textInputAction) {
2923+
return MaterialApp(
2924+
home: Center(
2925+
child: Material(
2926+
child: SearchAnchor.bar(
2927+
textInputAction: textInputAction,
2928+
suggestionsBuilder: (BuildContext context, SearchController controller) {
2929+
return <Widget>[];
2930+
},
2931+
),
2932+
),
2933+
),
2934+
);
2935+
}
2936+
await tester.pumpWidget(buildSearchAnchor(TextInputAction.previous));
2937+
await tester.pump();
2938+
await tester.tap(find.byType(SearchBar)); // Open search view.
2939+
await tester.pumpAndSettle();
2940+
final Finder textFieldFinder = find.descendant(of: findViewContent(), matching: find.byType(TextField));
2941+
final TextField textFieldInView = tester.widget<TextField>(textFieldFinder);
2942+
expect(textFieldInView.textInputAction, TextInputAction.previous);
2943+
// Close search view.
2944+
await tester.tap(find.widgetWithIcon(IconButton, Icons.arrow_back));
2945+
await tester.pumpAndSettle();
2946+
final TextField textField = tester.widget(find.byType(TextField));
2947+
expect(textField.textInputAction, TextInputAction.previous);
2948+
});
27652949
}
27662950

27672951
Future<void> checkSearchBarDefaults(WidgetTester tester, ColorScheme colorScheme, Material material) async {

0 commit comments

Comments
 (0)