Unverified Commit 0bc5a2bc authored by Qun Cheng's avatar Qun Cheng Committed by GitHub

Add `textCapitalization` property for `SearchBar` and `SearchAnchor` (#131459)

This is to add `textCapitalization` property for `SearchBar` and `SearchAnchor`.
Fixes: #131260
parent 40a3e481
......@@ -71,6 +71,9 @@ class _SearchBarDefaultsM3 extends SearchBarThemeData {
@override
BoxConstraints get constraints =>
const BoxConstraints(minWidth: 360.0, maxWidth: 800.0, minHeight: ${getToken('md.comp.search-bar.container.height')});
@override
TextCapitalization get textCapitalization => TextCapitalization.none;
}
''';
}
......@@ -126,6 +126,7 @@ class SearchAnchor extends StatefulWidget {
this.headerHintStyle,
this.dividerColor,
this.viewConstraints,
this.textCapitalization,
required this.builder,
required this.suggestionsBuilder,
});
......@@ -170,6 +171,7 @@ class SearchAnchor extends StatefulWidget {
BoxConstraints? viewConstraints,
bool? isFullScreen,
SearchController searchController,
TextCapitalization textCapitalization,
required SuggestionsBuilder suggestionsBuilder
}) = _SearchAnchorWithSearchBar;
......@@ -286,6 +288,9 @@ class SearchAnchor extends StatefulWidget {
/// ```
final BoxConstraints? viewConstraints;
/// {@macro flutter.widgets.editableText.textCapitalization}
final TextCapitalization? textCapitalization;
/// Called to create a widget which can open a search view route when it is tapped.
///
/// The widget returned by this builder is faded out when it is tapped.
......@@ -361,6 +366,7 @@ class _SearchAnchorState extends State<SearchAnchor> {
anchorKey: _anchorKey,
searchController: _searchController,
suggestionsBuilder: widget.suggestionsBuilder,
textCapitalization: widget.textCapitalization,
));
}
......@@ -426,6 +432,7 @@ class _SearchViewRoute extends PopupRoute<_SearchViewRoute> {
this.viewHeaderHintStyle,
this.dividerColor,
this.viewConstraints,
this.textCapitalization,
required this.showFullScreenView,
required this.anchorKey,
required this.searchController,
......@@ -447,6 +454,7 @@ class _SearchViewRoute extends PopupRoute<_SearchViewRoute> {
final TextStyle? viewHeaderHintStyle;
final Color? dividerColor;
final BoxConstraints? viewConstraints;
final TextCapitalization? textCapitalization;
final bool showFullScreenView;
final GlobalKey anchorKey;
final SearchController searchController;
......@@ -595,6 +603,7 @@ class _SearchViewRoute extends PopupRoute<_SearchViewRoute> {
viewBuilder: viewBuilder,
searchController: searchController,
suggestionsBuilder: suggestionsBuilder,
textCapitalization: textCapitalization,
),
);
}
......@@ -620,6 +629,7 @@ class _ViewContent extends StatefulWidget {
this.viewHeaderTextStyle,
this.viewHeaderHintStyle,
this.dividerColor,
this.textCapitalization,
required this.showFullScreenView,
required this.topPadding,
required this.animation,
......@@ -644,6 +654,7 @@ class _ViewContent extends StatefulWidget {
final TextStyle? viewHeaderTextStyle;
final TextStyle? viewHeaderHintStyle;
final Color? dividerColor;
final TextCapitalization? textCapitalization;
final bool showFullScreenView;
final double topPadding;
final Animation<double> animation;
......@@ -824,6 +835,7 @@ class _ViewContentState extends State<_ViewContent> {
onChanged: (_) {
updateSuggestions();
},
textCapitalization: widget.textCapitalization,
),
),
),
......@@ -884,6 +896,7 @@ class _SearchAnchorWithSearchBar extends SearchAnchor {
super.viewConstraints,
super.isFullScreen,
super.searchController,
super.textCapitalization,
required super.suggestionsBuilder
}) : super(
viewHintText: viewHintText ?? barHintText,
......@@ -911,6 +924,7 @@ class _SearchAnchorWithSearchBar extends SearchAnchor {
padding: barPadding ?? const MaterialStatePropertyAll<EdgeInsets>(EdgeInsets.symmetric(horizontal: 16.0)),
leading: barLeading ?? const Icon(Icons.search),
trailing: barTrailing,
textCapitalization: textCapitalization,
);
}
);
......@@ -1022,6 +1036,7 @@ class SearchBar extends StatefulWidget {
this.padding,
this.textStyle,
this.hintStyle,
this.textCapitalization,
});
/// Controls the text being edited in the search bar's text field.
......@@ -1140,6 +1155,9 @@ class SearchBar extends StatefulWidget {
/// The default text color is [ColorScheme.onSurfaceVariant].
final MaterialStateProperty<TextStyle?>? hintStyle;
/// {@macro flutter.widgets.editableText.textCapitalization}
final TextCapitalization? textCapitalization;
@override
State<SearchBar> createState() => _SearchBarState();
}
......@@ -1190,6 +1208,7 @@ class _SearchBarState extends State<SearchBar> {
final BorderSide? effectiveSide = resolve<BorderSide?>(widget.side, searchBarTheme.side, defaults.side);
final EdgeInsetsGeometry? effectivePadding = resolve<EdgeInsetsGeometry?>(widget.padding, searchBarTheme.padding, defaults.padding);
final MaterialStateProperty<Color?>? effectiveOverlayColor = widget.overlayColor ?? searchBarTheme.overlayColor ?? defaults.overlayColor;
final TextCapitalization effectiveTextCapitalization = widget.textCapitalization ?? searchBarTheme.textCapitalization ?? defaults.textCapitalization!;
final Set<MaterialState> states = _internalStatesController.value;
final TextStyle? effectiveHintStyle = widget.hintStyle?.resolve(states)
......@@ -1273,6 +1292,7 @@ class _SearchBarState extends State<SearchBar> {
// smaller than 48.0
isDense: true,
)),
textCapitalization: effectiveTextCapitalization,
),
),
)
......@@ -1353,6 +1373,9 @@ class _SearchBarDefaultsM3 extends SearchBarThemeData {
@override
BoxConstraints get constraints =>
const BoxConstraints(minWidth: 360.0, maxWidth: 800.0, minHeight: 56.0);
@override
TextCapitalization get textCapitalization => TextCapitalization.none;
}
// END GENERATED TOKEN PROPERTIES - SearchBar
......
......@@ -8,6 +8,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import '../../services.dart';
import 'material_state.dart';
import 'theme.dart';
......@@ -47,6 +48,7 @@ class SearchBarThemeData with Diagnosticable {
this.textStyle,
this.hintStyle,
this.constraints,
this.textCapitalization,
});
/// Overrides the default value of the [SearchBar.elevation].
......@@ -82,6 +84,9 @@ class SearchBarThemeData with Diagnosticable {
/// Overrides the value of size constraints for [SearchBar].
final BoxConstraints? constraints;
/// Overrides the value of [SearchBar.textCapitalization].
final TextCapitalization? textCapitalization;
/// Creates a copy of this object but with the given fields replaced with the
/// new values.
SearchBarThemeData copyWith({
......@@ -96,6 +101,7 @@ class SearchBarThemeData with Diagnosticable {
MaterialStateProperty<TextStyle?>? textStyle,
MaterialStateProperty<TextStyle?>? hintStyle,
BoxConstraints? constraints,
TextCapitalization? textCapitalization,
}) {
return SearchBarThemeData(
elevation: elevation ?? this.elevation,
......@@ -109,6 +115,7 @@ class SearchBarThemeData with Diagnosticable {
textStyle: textStyle ?? this.textStyle,
hintStyle: hintStyle ?? this.hintStyle,
constraints: constraints ?? this.constraints,
textCapitalization: textCapitalization ?? this.textCapitalization,
);
}
......@@ -131,6 +138,7 @@ class SearchBarThemeData with Diagnosticable {
textStyle: MaterialStateProperty.lerp<TextStyle?>(a?.textStyle, b?.textStyle, t, TextStyle.lerp),
hintStyle: MaterialStateProperty.lerp<TextStyle?>(a?.hintStyle, b?.hintStyle, t, TextStyle.lerp),
constraints: BoxConstraints.lerp(a?.constraints, b?.constraints, t),
textCapitalization: t < 0.5 ? a?.textCapitalization : b?.textCapitalization,
);
}
......@@ -147,6 +155,7 @@ class SearchBarThemeData with Diagnosticable {
textStyle,
hintStyle,
constraints,
textCapitalization,
);
@override
......@@ -168,7 +177,8 @@ class SearchBarThemeData with Diagnosticable {
&& other.padding == padding
&& other.textStyle == textStyle
&& other.hintStyle == hintStyle
&& other.constraints == constraints;
&& other.constraints == constraints
&& other.textCapitalization == textCapitalization;
}
@override
......@@ -185,6 +195,7 @@ class SearchBarThemeData with Diagnosticable {
properties.add(DiagnosticsProperty<MaterialStateProperty<TextStyle?>>('textStyle', textStyle, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<TextStyle?>>('hintStyle', hintStyle, defaultValue: null));
properties.add(DiagnosticsProperty<BoxConstraints>('constraints', constraints, defaultValue: null));
properties.add(DiagnosticsProperty<TextCapitalization>('textCapitalization', textCapitalization, defaultValue: null));
}
// Special case because BorderSide.lerp() doesn't support null arguments
......
......@@ -691,6 +691,108 @@ void main() {
expect(inputText.style.color, focusedColor);
});
testWidgets('SearchBar respects textCapitalization property', (WidgetTester tester) async {
Widget buildSearchBar(TextCapitalization textCapitalization) {
return MaterialApp(
home: Center(
child: Material(
child: SearchBar(
textCapitalization: textCapitalization,
),
),
),
);
}
await tester.pumpWidget(buildSearchBar(TextCapitalization.characters));
await tester.pump();
TextField textField = tester.widget(find.byType(TextField));
expect(textField.textCapitalization, TextCapitalization.characters);
await tester.pumpWidget(buildSearchBar(TextCapitalization.sentences));
await tester.pump();
textField = tester.widget(find.byType(TextField));
expect(textField.textCapitalization, TextCapitalization.sentences);
await tester.pumpWidget(buildSearchBar(TextCapitalization.words));
await tester.pump();
textField = tester.widget(find.byType(TextField));
expect(textField.textCapitalization, TextCapitalization.words);
await tester.pumpWidget(buildSearchBar(TextCapitalization.none));
await tester.pump();
textField = tester.widget(find.byType(TextField));
expect(textField.textCapitalization, TextCapitalization.none);
});
testWidgets('SearchAnchor respects textCapitalization property', (WidgetTester tester) async {
Widget buildSearchAnchor(TextCapitalization textCapitalization) {
return MaterialApp(
home: Center(
child: Material(
child: SearchAnchor(
textCapitalization: textCapitalization,
builder: (BuildContext context, SearchController controller) {
return IconButton(
icon: const Icon(Icons.ac_unit),
onPressed: () {
controller.openView();
},
);
},
suggestionsBuilder: (BuildContext context, SearchController controller) {
return <Widget>[];
},
),
),
),
);
}
await tester.pumpWidget(buildSearchAnchor(TextCapitalization.characters));
await tester.pump();
await tester.tap(find.widgetWithIcon(IconButton, Icons.ac_unit));
await tester.pumpAndSettle();
TextField textField = tester.widget(find.byType(TextField));
expect(textField.textCapitalization, TextCapitalization.characters);
await tester.tap(find.widgetWithIcon(IconButton, Icons.arrow_back));
await tester.pump();
await tester.pumpWidget(buildSearchAnchor(TextCapitalization.none));
await tester.pump();
await tester.tap(find.widgetWithIcon(IconButton, Icons.ac_unit));
await tester.pumpAndSettle();
textField = tester.widget(find.byType(TextField));
expect(textField.textCapitalization, TextCapitalization.none);
});
testWidgets('SearchAnchor.bar respects textCapitalization property', (WidgetTester tester) async {
Widget buildSearchAnchor(TextCapitalization textCapitalization) {
return MaterialApp(
home: Center(
child: Material(
child: SearchAnchor.bar(
textCapitalization: textCapitalization,
suggestionsBuilder: (BuildContext context, SearchController controller) {
return <Widget>[];
},
),
),
),
);
}
await tester.pumpWidget(buildSearchAnchor(TextCapitalization.characters));
await tester.pump();
await tester.tap(find.byType(SearchBar)); // Open search view.
await tester.pumpAndSettle();
final Finder textFieldFinder = find.descendant(of: findViewContent(), matching: find.byType(TextField));
final TextField textFieldInView = tester.widget<TextField>(textFieldFinder);
expect(textFieldInView.textCapitalization, TextCapitalization.characters);
// Close search view.
await tester.tap(find.widgetWithIcon(IconButton, Icons.arrow_back));
await tester.pumpAndSettle();
final TextField textField = tester.widget(find.byType(TextField));
expect(textField.textCapitalization, TextCapitalization.characters);
});
testWidgets('hintStyle can override textStyle for hintText', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
......
......@@ -35,6 +35,7 @@ void main() {
expect(themeData.textStyle, null);
expect(themeData.hintStyle, null);
expect(themeData.constraints, null);
expect(themeData.textCapitalization, null);
const SearchBarTheme theme = SearchBarTheme(data: SearchBarThemeData(), child: SizedBox());
expect(theme.data.elevation, null);
......@@ -48,6 +49,7 @@ void main() {
expect(theme.data.textStyle, null);
expect(theme.data.hintStyle, null);
expect(theme.data.constraints, null);
expect(theme.data.textCapitalization, null);
});
testWidgetsWithLeakTracking('Default SearchBarThemeData debugFillProperties', (WidgetTester tester) async {
......@@ -77,6 +79,7 @@ void main() {
textStyle: MaterialStatePropertyAll<TextStyle>(TextStyle(fontSize: 24.0)),
hintStyle: MaterialStatePropertyAll<TextStyle>(TextStyle(fontSize: 16.0)),
constraints: BoxConstraints(minWidth: 350, maxWidth: 850),
textCapitalization: TextCapitalization.characters,
).debugFillProperties(builder);
final List<String> description = builder.properties
......@@ -95,6 +98,7 @@ void main() {
expect(description[8], 'textStyle: MaterialStatePropertyAll(TextStyle(inherit: true, size: 24.0))');
expect(description[9], 'hintStyle: MaterialStatePropertyAll(TextStyle(inherit: true, size: 16.0))');
expect(description[10], 'constraints: BoxConstraints(350.0<=w<=850.0, 0.0<=h<=Infinity)');
expect(description[11], 'textCapitalization: TextCapitalization.characters');
});
group('[Theme, SearchBarTheme, SearchBar properties overrides]', () {
......@@ -120,6 +124,7 @@ void main() {
const MaterialStateProperty<TextStyle?> textStyle = MaterialStatePropertyAll<TextStyle>(textStyleValue);
const MaterialStateProperty<TextStyle?> hintStyle = MaterialStatePropertyAll<TextStyle>(hintStyleValue);
const BoxConstraints constraints = BoxConstraints(minWidth: 250.0, maxWidth: 300.0, minHeight: 80.0);
const TextCapitalization textCapitalization = TextCapitalization.words;
const SearchBarThemeData searchBarTheme = SearchBarThemeData(
elevation: elevation,
......@@ -133,6 +138,7 @@ void main() {
textStyle: textStyle,
hintStyle: hintStyle,
constraints: constraints,
textCapitalization: textCapitalization,
);
Widget buildFrame({
......@@ -164,6 +170,7 @@ void main() {
textStyle: textStyle,
hintStyle: hintStyle,
constraints: constraints,
textCapitalization: textCapitalization,
);
},
);
......@@ -223,6 +230,7 @@ void main() {
final EditableText inputText = tester.widget(find.text('input'));
expect(inputText.style.color, textStyleValue.color);
expect(inputText.style.fontSize, textStyleValue.fontSize);
expect(inputText.textCapitalization, textCapitalization);
final Rect barRect = tester.getRect(find.byType(SearchBar));
final Rect leadingRect = tester.getRect(find.byIcon(Icons.search));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment