Add padding options to SearchAnchor (#152508)

Cherry-picked padding changes from https://github.com/flutter/flutter/pull/148856

Adds padding configuration options to `SearchAnchor`. This PR adds the following:
- `viewBarPadding`: The padding for items inside the `SearchBar` in the `SearchAnchor` view
- `viewPadding`: The padding to use around the entire `SearchAnchor` view

Working towards https://github.com/flutter/flutter/issues/148852
This commit is contained in:
Rexios
2024-10-15 16:24:48 -04:00
committed by GitHub
parent 2dad95b21c
commit e96aaab5e0
5 changed files with 202 additions and 51 deletions

View File

@@ -47,6 +47,9 @@ class _${blockName}DefaultsM3 extends ${blockName}ThemeData {
@override
BoxConstraints get constraints => const BoxConstraints(minWidth: 360.0, minHeight: 240.0);
@override
EdgeInsetsGeometry? get barPadding => const EdgeInsets.symmetric(horizontal: 8.0);
@override
Color? get dividerColor => ${componentColor('md.comp.search-view.divider')};
}

View File

@@ -126,11 +126,13 @@ class SearchAnchor extends StatefulWidget {
this.viewSurfaceTintColor,
this.viewSide,
this.viewShape,
this.viewBarPadding,
this.headerHeight,
this.headerTextStyle,
this.headerHintStyle,
this.dividerColor,
this.viewConstraints,
this.viewPadding,
this.textCapitalization,
this.viewOnChanged,
this.viewOnSubmitted,
@@ -165,6 +167,7 @@ class SearchAnchor extends StatefulWidget {
MaterialStateProperty<BorderSide?>? barSide,
MaterialStateProperty<OutlinedBorder?>? barShape,
MaterialStateProperty<EdgeInsetsGeometry?>? barPadding,
EdgeInsetsGeometry? viewBarPadding,
MaterialStateProperty<TextStyle?>? barTextStyle,
MaterialStateProperty<TextStyle?>? barHintStyle,
Widget? viewLeading,
@@ -180,6 +183,7 @@ class SearchAnchor extends StatefulWidget {
Color? dividerColor,
BoxConstraints? constraints,
BoxConstraints? viewConstraints,
EdgeInsetsGeometry? viewPadding,
bool? isFullScreen,
SearchController searchController,
TextCapitalization textCapitalization,
@@ -272,6 +276,11 @@ class SearchAnchor extends StatefulWidget {
/// mode and a [RoundedRectangleBorder] shape with a 28.0 radius otherwise.
final OutlinedBorder? viewShape;
/// The padding to use for the search view's search bar.
///
/// If null, then the default value is 8.0 horizontally.
final EdgeInsetsGeometry? viewBarPadding;
/// The height of the search field on the search view.
///
/// If null, the value of [SearchViewThemeData.headerHeight] will be used. If
@@ -312,6 +321,13 @@ class SearchAnchor extends StatefulWidget {
/// ```
final BoxConstraints? viewConstraints;
/// The padding to use for the search view.
///
/// Has no effect if the search view is full-screen.
///
/// If null, the value of [SearchViewThemeData.padding] will be used.
final EdgeInsetsGeometry? viewPadding;
/// {@macro flutter.widgets.editableText.textCapitalization}
final TextCapitalization? textCapitalization;
@@ -431,11 +447,13 @@ class _SearchAnchorState extends State<SearchAnchor> {
viewSurfaceTintColor: widget.viewSurfaceTintColor,
viewSide: widget.viewSide,
viewShape: widget.viewShape,
viewBarPadding: widget.viewBarPadding,
viewHeaderHeight: widget.headerHeight,
viewHeaderTextStyle: widget.headerTextStyle,
viewHeaderHintStyle: widget.headerHintStyle,
dividerColor: widget.dividerColor,
viewConstraints: widget.viewConstraints,
viewPadding: widget.viewPadding,
showFullScreenView: getShowFullScreenView(),
toggleVisibility: toggleVisibility,
textDirection: Directionality.of(context),
@@ -511,11 +529,13 @@ class _SearchViewRoute extends PopupRoute<_SearchViewRoute> {
this.viewSurfaceTintColor,
this.viewSide,
this.viewShape,
this.viewBarPadding,
this.viewHeaderHeight,
this.viewHeaderTextStyle,
this.viewHeaderHintStyle,
this.dividerColor,
this.viewConstraints,
this.viewPadding,
this.textCapitalization,
required this.showFullScreenView,
required this.anchorKey,
@@ -539,11 +559,13 @@ class _SearchViewRoute extends PopupRoute<_SearchViewRoute> {
final Color? viewSurfaceTintColor;
final BorderSide? viewSide;
final OutlinedBorder? viewShape;
final EdgeInsetsGeometry? viewBarPadding;
final double? viewHeaderHeight;
final TextStyle? viewHeaderTextStyle;
final TextStyle? viewHeaderHintStyle;
final Color? dividerColor;
final BoxConstraints? viewConstraints;
final EdgeInsetsGeometry? viewPadding;
final TextCapitalization? textCapitalization;
final bool showFullScreenView;
final GlobalKey anchorKey;
@@ -705,10 +727,12 @@ class _SearchViewRoute extends PopupRoute<_SearchViewRoute> {
viewSurfaceTintColor: viewSurfaceTintColor,
viewSide: viewSide,
viewShape: viewShape,
viewBarPadding: viewBarPadding,
viewHeaderHeight: viewHeaderHeight,
viewHeaderTextStyle: viewHeaderTextStyle,
viewHeaderHintStyle: viewHeaderHintStyle,
dividerColor: dividerColor,
viewPadding: viewPadding,
showFullScreenView: showFullScreenView,
animation: curvedAnimation!,
topPadding: topPadding,
@@ -745,10 +769,12 @@ class _ViewContent extends StatefulWidget {
this.viewSurfaceTintColor,
this.viewSide,
this.viewShape,
this.viewBarPadding,
this.viewHeaderHeight,
this.viewHeaderTextStyle,
this.viewHeaderHintStyle,
this.dividerColor,
this.viewPadding,
this.textCapitalization,
required this.showFullScreenView,
required this.topPadding,
@@ -772,10 +798,12 @@ class _ViewContent extends StatefulWidget {
final Color? viewSurfaceTintColor;
final BorderSide? viewSide;
final OutlinedBorder? viewShape;
final EdgeInsetsGeometry? viewBarPadding;
final double? viewHeaderHeight;
final TextStyle? viewHeaderTextStyle;
final TextStyle? viewHeaderHintStyle;
final Color? dividerColor;
final EdgeInsetsGeometry? viewPadding;
final TextCapitalization? textCapitalization;
final bool showFullScreenView;
final double topPadding;
@@ -963,6 +991,12 @@ class _ViewContentState extends State<_ViewContent> {
?? widget.viewHeaderTextStyle
?? viewTheme.headerTextStyle
?? viewDefaults.headerHintStyle;
final EdgeInsetsGeometry? effectivePadding = widget.viewPadding
?? viewTheme.padding
?? viewDefaults.padding;
final EdgeInsetsGeometry? effectiveBarPadding = widget.viewBarPadding
?? viewTheme.barPadding
?? viewDefaults.barPadding;
final Widget viewDivider = DividerTheme(
data: dividerTheme.copyWith(color: effectiveDividerColor),
@@ -976,61 +1010,65 @@ class _ViewContentState extends State<_ViewContent> {
child: SizedBox(
width: _viewRect.width,
height: _viewRect.height,
child: Material(
clipBehavior: Clip.antiAlias,
shape: effectiveShape,
color: effectiveBackgroundColor,
surfaceTintColor: effectiveSurfaceTint,
elevation: effectiveElevation,
child: ClipRect(
child: Padding(
padding: widget.showFullScreenView ? EdgeInsets.zero : (effectivePadding ?? EdgeInsets.zero),
child: Material(
clipBehavior: Clip.antiAlias,
child: OverflowBox(
alignment: Alignment.topLeft,
maxWidth: math.min(widget.viewMaxWidth, _screenSize!.width),
minWidth: 0,
child: FadeTransition(
opacity: viewIconsFadeCurve,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: widget.topPadding),
child: SafeArea(
top: false,
bottom: false,
child: SearchBar(
autoFocus: true,
constraints: headerConstraints ?? (widget.showFullScreenView ? BoxConstraints(minHeight: _SearchViewDefaultsM3.fullScreenBarHeight) : null),
leading: widget.viewLeading ?? defaultLeading,
trailing: widget.viewTrailing ?? defaultTrailing,
hintText: widget.viewHintText,
backgroundColor: const MaterialStatePropertyAll<Color>(Colors.transparent),
overlayColor: const MaterialStatePropertyAll<Color>(Colors.transparent),
elevation: const MaterialStatePropertyAll<double>(0.0),
textStyle: MaterialStatePropertyAll<TextStyle?>(effectiveTextStyle),
hintStyle: MaterialStatePropertyAll<TextStyle?>(effectiveHintStyle),
controller: _controller,
onChanged: (String value) {
widget.viewOnChanged?.call(value);
updateSuggestions();
},
onSubmitted: widget.viewOnSubmitted,
textCapitalization: widget.textCapitalization,
textInputAction: widget.textInputAction,
keyboardType: widget.keyboardType,
shape: effectiveShape,
color: effectiveBackgroundColor,
surfaceTintColor: effectiveSurfaceTint,
elevation: effectiveElevation,
child: ClipRect(
clipBehavior: Clip.antiAlias,
child: OverflowBox(
alignment: Alignment.topLeft,
maxWidth: math.min(widget.viewMaxWidth, _screenSize!.width),
minWidth: 0,
child: FadeTransition(
opacity: viewIconsFadeCurve,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: widget.topPadding),
child: SafeArea(
top: false,
bottom: false,
child: SearchBar(
autoFocus: true,
constraints: headerConstraints ?? (widget.showFullScreenView ? BoxConstraints(minHeight: _SearchViewDefaultsM3.fullScreenBarHeight) : null),
padding: WidgetStatePropertyAll<EdgeInsetsGeometry?>(effectiveBarPadding),
leading: widget.viewLeading ?? defaultLeading,
trailing: widget.viewTrailing ?? defaultTrailing,
hintText: widget.viewHintText,
backgroundColor: const MaterialStatePropertyAll<Color>(Colors.transparent),
overlayColor: const MaterialStatePropertyAll<Color>(Colors.transparent),
elevation: const MaterialStatePropertyAll<double>(0.0),
textStyle: MaterialStatePropertyAll<TextStyle?>(effectiveTextStyle),
hintStyle: MaterialStatePropertyAll<TextStyle?>(effectiveHintStyle),
controller: _controller,
onChanged: (String value) {
widget.viewOnChanged?.call(value);
updateSuggestions();
},
onSubmitted: widget.viewOnSubmitted,
textCapitalization: widget.textCapitalization,
textInputAction: widget.textInputAction,
keyboardType: widget.keyboardType,
),
),
),
),
FadeTransition(
opacity: viewDividerFadeCurve,
child: viewDivider),
Expanded(
child: FadeTransition(
opacity: viewListFadeOnIntervalCurve,
child: viewBuilder(result),
FadeTransition(
opacity: viewDividerFadeCurve,
child: viewDivider),
Expanded(
child: FadeTransition(
opacity: viewListFadeOnIntervalCurve,
child: viewBuilder(result),
),
),
),
],
],
),
),
),
),
@@ -1054,6 +1092,7 @@ class _SearchAnchorWithSearchBar extends SearchAnchor {
MaterialStateProperty<BorderSide?>? barSide,
MaterialStateProperty<OutlinedBorder?>? barShape,
MaterialStateProperty<EdgeInsetsGeometry?>? barPadding,
super.viewBarPadding,
MaterialStateProperty<TextStyle?>? barTextStyle,
MaterialStateProperty<TextStyle?>? barHintStyle,
super.viewLeading,
@@ -1069,6 +1108,7 @@ class _SearchAnchorWithSearchBar extends SearchAnchor {
super.dividerColor,
BoxConstraints? constraints,
super.viewConstraints,
super.viewPadding,
super.isFullScreen,
super.searchController,
super.textCapitalization,
@@ -1673,6 +1713,9 @@ class _SearchViewDefaultsM3 extends SearchViewThemeData {
@override
BoxConstraints get constraints => const BoxConstraints(minWidth: 360.0, minHeight: 240.0);
@override
EdgeInsetsGeometry? get barPadding => const EdgeInsets.symmetric(horizontal: 8.0);
@override
Color? get dividerColor => _colors.outline;
}

View File

@@ -44,6 +44,8 @@ class SearchViewThemeData with Diagnosticable {
this.elevation,
this.surfaceTintColor,
this.constraints,
this.padding,
this.barPadding,
this.side,
this.shape,
this.headerHeight,
@@ -79,6 +81,12 @@ class SearchViewThemeData with Diagnosticable {
/// Overrides the value of size constraints for [SearchAnchor.viewConstraints].
final BoxConstraints? constraints;
/// Overrides the value of the padding for [SearchAnchor.viewPadding].
final EdgeInsetsGeometry? padding;
/// Overrides the value of the padding for [SearchAnchor.viewBarPadding]
final EdgeInsetsGeometry? barPadding;
/// Overrides the value of the divider color for [SearchAnchor.dividerColor].
final Color? dividerColor;
@@ -94,6 +102,8 @@ class SearchViewThemeData with Diagnosticable {
TextStyle? headerTextStyle,
TextStyle? headerHintStyle,
BoxConstraints? constraints,
EdgeInsetsGeometry? padding,
EdgeInsetsGeometry? barPadding,
Color? dividerColor,
}) {
return SearchViewThemeData(
@@ -106,6 +116,8 @@ class SearchViewThemeData with Diagnosticable {
headerTextStyle: headerTextStyle ?? this.headerTextStyle,
headerHintStyle: headerHintStyle ?? this.headerHintStyle,
constraints: constraints ?? this.constraints,
padding: padding ?? this.padding,
barPadding: barPadding ?? this.barPadding,
dividerColor: dividerColor ?? this.dividerColor,
);
}
@@ -125,6 +137,8 @@ class SearchViewThemeData with Diagnosticable {
headerTextStyle: TextStyle.lerp(a?.headerTextStyle, b?.headerTextStyle, t),
headerHintStyle: TextStyle.lerp(a?.headerTextStyle, b?.headerTextStyle, t),
constraints: BoxConstraints.lerp(a?.constraints, b?.constraints, t),
padding: EdgeInsetsGeometry.lerp(a?.padding, b?.padding, t),
barPadding: EdgeInsetsGeometry.lerp(a?.barPadding, b?.barPadding, t),
dividerColor: Color.lerp(a?.dividerColor, b?.dividerColor, t),
);
}
@@ -140,6 +154,8 @@ class SearchViewThemeData with Diagnosticable {
headerTextStyle,
headerHintStyle,
constraints,
padding,
barPadding,
dividerColor,
);
@@ -161,6 +177,8 @@ class SearchViewThemeData with Diagnosticable {
&& other.headerTextStyle == headerTextStyle
&& other.headerHintStyle == headerHintStyle
&& other.constraints == constraints
&& other.padding == padding
&& other.barPadding == barPadding
&& other.dividerColor == dividerColor;
}
@@ -176,6 +194,8 @@ class SearchViewThemeData with Diagnosticable {
properties.add(DiagnosticsProperty<TextStyle?>('headerTextStyle', headerTextStyle, defaultValue: null));
properties.add(DiagnosticsProperty<TextStyle?>('headerHintStyle', headerHintStyle, defaultValue: null));
properties.add(DiagnosticsProperty<BoxConstraints>('constraints', constraints, defaultValue: null));
properties.add(DiagnosticsProperty<EdgeInsetsGeometry?>('padding', padding, defaultValue: null));
properties.add(DiagnosticsProperty<EdgeInsetsGeometry?>('barPadding', barPadding, defaultValue: null));
properties.add(DiagnosticsProperty<Color?>('dividerColor', dividerColor, defaultValue: null));
}

View File

@@ -1642,6 +1642,56 @@ void main() {
expect(inputText.style?.color, Colors.orange);
});
testWidgets('SearchAnchor respects viewPadding property', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: Material(
child: SearchAnchor(
isFullScreen: false,
viewPadding: const EdgeInsets.all(16.0),
builder: (BuildContext context, SearchController controller) {
return IconButton(icon: const Icon(Icons.search), onPressed: () {
controller.openView();
},);
},
suggestionsBuilder: (BuildContext context, SearchController controller) {
return <Widget>[];
},
),
),
));
await tester.tap(find.widgetWithIcon(IconButton, Icons.search));
await tester.pumpAndSettle();
final Padding padding = tester.widget<Padding>(find.descendant(of: findViewContent(), matching: find.byType(Padding)).first);
expect(padding.padding, const EdgeInsets.all(16.0));
});
testWidgets('SearchAnchor ignores viewPadding property if full screen', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: Material(
child: SearchAnchor(
isFullScreen: true,
viewPadding: const EdgeInsets.all(16.0),
builder: (BuildContext context, SearchController controller) {
return IconButton(icon: const Icon(Icons.search), onPressed: () {
controller.openView();
},);
},
suggestionsBuilder: (BuildContext context, SearchController controller) {
return <Widget>[];
},
),
),
));
await tester.tap(find.widgetWithIcon(IconButton, Icons.search));
await tester.pumpAndSettle();
final Padding padding = tester.widget<Padding>(find.descendant(of: findViewContent(), matching: find.byType(Padding)).first);
expect(padding.padding, EdgeInsets.zero);
});
testWidgets('SearchAnchor respects dividerColor property', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: Material(
@@ -1696,6 +1746,33 @@ void main() {
expect(sizedBox.height, 390.0);
});
testWidgets('SearchAnchor respects viewBarPadding property', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: Material(
child: Center(
child: SearchAnchor(
viewBarPadding: const EdgeInsets.symmetric(horizontal: 16.0),
builder: (BuildContext context, SearchController controller) {
return IconButton(icon: const Icon(Icons.search), onPressed: () {
controller.openView();
},);
},
suggestionsBuilder: (BuildContext context, SearchController controller) {
return <Widget>[];
},
),
),
),
));
await tester.tap(find.widgetWithIcon(IconButton, Icons.search));
await tester.pumpAndSettle();
final Finder findSearchBar = find.descendant(of: findViewContent(), matching: find.byType(SearchBar)).first;
final Padding padding = tester.widget<Padding>(find.descendant(of: findSearchBar, matching: find.byType(Padding)).first);
expect(padding.padding, const EdgeInsets.symmetric(horizontal: 16.0));
});
testWidgets('SearchAnchor respects builder property - LTR', (WidgetTester tester) async {
Widget buildAnchor({required SearchAnchorChildBuilder builder}) {
return MaterialApp(

View File

@@ -31,6 +31,8 @@ void main() {
expect(themeData.headerHeight, null);
expect(themeData.headerTextStyle, null);
expect(themeData.headerHintStyle, null);
expect(themeData.padding, null);
expect(themeData.barPadding, null);
expect(themeData.dividerColor, null);
const SearchViewTheme theme = SearchViewTheme(data: SearchViewThemeData(), child: SizedBox());
@@ -43,6 +45,8 @@ void main() {
expect(theme.data.headerHeight, null);
expect(theme.data.headerTextStyle, null);
expect(theme.data.headerHintStyle, null);
expect(themeData.padding, null);
expect(themeData.barPadding, null);
expect(theme.data.dividerColor, null);
});
@@ -71,6 +75,8 @@ void main() {
headerTextStyle: TextStyle(fontSize: 24.0),
headerHintStyle: TextStyle(fontSize: 16.0),
constraints: BoxConstraints(minWidth: 350, minHeight: 240),
padding: EdgeInsets.only(bottom: 32.0),
barPadding: EdgeInsets.zero,
).debugFillProperties(builder);
final List<String> description = builder.properties
@@ -87,6 +93,8 @@ void main() {
expect(description[6], 'headerTextStyle: TextStyle(inherit: true, size: 24.0)');
expect(description[7], 'headerHintStyle: TextStyle(inherit: true, size: 16.0)');
expect(description[8], 'constraints: BoxConstraints(350.0<=w<=Infinity, 240.0<=h<=Infinity)');
expect(description[9], 'padding: EdgeInsets(0.0, 0.0, 0.0, 32.0)');
expect(description[10], 'barPadding: EdgeInsets.zero');
});
group('[Theme, SearchViewTheme, SearchView properties overrides]', () {