mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Fix input decoration height and margin calculation. (#18696)
This commit is contained in:
parent
a7ba7b4b62
commit
daa1619905
@ -803,9 +803,6 @@ class _RenderDecoration extends RenderBox {
|
||||
+ aboveBaseline
|
||||
+ belowBaseline
|
||||
+ contentPadding.bottom;
|
||||
containerHeight = math.max(
|
||||
containerHeight,
|
||||
math.max(_boxSize(suffixIcon).height, _boxSize(prefixIcon).height));
|
||||
|
||||
if (label != null) {
|
||||
// floatingLabelHeight includes the vertical gap between the inline
|
||||
@ -814,6 +811,12 @@ class _RenderDecoration extends RenderBox {
|
||||
inputBaseline += decoration.floatingLabelHeight;
|
||||
}
|
||||
|
||||
containerHeight = math.max(
|
||||
containerHeight,
|
||||
math.max(
|
||||
_boxSize(suffixIcon).height,
|
||||
_boxSize(prefixIcon).height));
|
||||
|
||||
// Inline text within an outline border is centered within the container
|
||||
// less 2.0 dps at the top to account for the vertical space occupied
|
||||
// by the floating label.
|
||||
@ -982,8 +985,10 @@ class _RenderDecoration extends RenderBox {
|
||||
case TextDirection.rtl: {
|
||||
double start = right - _boxSize(icon).width;
|
||||
double end = left;
|
||||
if (prefixIcon != null)
|
||||
if (prefixIcon != null) {
|
||||
start += contentPadding.left;
|
||||
start -= centerLayout(prefixIcon, start - prefixIcon.size.width);
|
||||
}
|
||||
if (label != null)
|
||||
centerLayout(label, start - label.size.width);
|
||||
if (prefix != null)
|
||||
@ -992,8 +997,10 @@ class _RenderDecoration extends RenderBox {
|
||||
baselineLayout(input, start - input.size.width);
|
||||
if (hint != null)
|
||||
baselineLayout(hint, start - hint.size.width);
|
||||
if (suffixIcon != null)
|
||||
if (suffixIcon != null) {
|
||||
end -= contentPadding.left;
|
||||
end += centerLayout(suffixIcon, end);
|
||||
}
|
||||
if (suffix != null)
|
||||
end += baselineLayout(suffix, end);
|
||||
break;
|
||||
@ -1001,8 +1008,10 @@ class _RenderDecoration extends RenderBox {
|
||||
case TextDirection.ltr: {
|
||||
double start = left + _boxSize(icon).width;
|
||||
double end = right;
|
||||
if (prefixIcon != null)
|
||||
if (prefixIcon != null) {
|
||||
start -= contentPadding.left;
|
||||
start += centerLayout(prefixIcon, start);
|
||||
}
|
||||
if (label != null)
|
||||
centerLayout(label, start);
|
||||
if (prefix != null)
|
||||
@ -1011,8 +1020,10 @@ class _RenderDecoration extends RenderBox {
|
||||
baselineLayout(input, start);
|
||||
if (hint != null)
|
||||
baselineLayout(hint, start);
|
||||
if (suffixIcon != null)
|
||||
if (suffixIcon != null) {
|
||||
end += contentPadding.right;
|
||||
end -= centerLayout(suffixIcon, end - suffixIcon.size.width);
|
||||
}
|
||||
if (suffix != null)
|
||||
end -= baselineLayout(suffix, end - suffix.size.width);
|
||||
break;
|
||||
@ -1691,26 +1702,34 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
|
||||
);
|
||||
|
||||
final Widget prefixIcon = decoration.prefixIcon == null ? null :
|
||||
new ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: 48.0, minHeight: 48.0),
|
||||
child: IconTheme.merge(
|
||||
data: new IconThemeData(
|
||||
color: iconColor,
|
||||
size: iconSize,
|
||||
new Center(
|
||||
widthFactor: 1.0,
|
||||
heightFactor: 1.0,
|
||||
child: new ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: 48.0, minHeight: 48.0),
|
||||
child: IconTheme.merge(
|
||||
data: new IconThemeData(
|
||||
color: iconColor,
|
||||
size: iconSize,
|
||||
),
|
||||
child: decoration.prefixIcon,
|
||||
),
|
||||
child: decoration.prefixIcon,
|
||||
),
|
||||
);
|
||||
|
||||
final Widget suffixIcon = decoration.suffixIcon == null ? null :
|
||||
new ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: 48.0, minHeight: 48.0),
|
||||
child: IconTheme.merge(
|
||||
data: new IconThemeData(
|
||||
color: iconColor,
|
||||
size: iconSize,
|
||||
new Center(
|
||||
widthFactor: 1.0,
|
||||
heightFactor: 1.0,
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: 48.0, minHeight: 48.0),
|
||||
child: IconTheme.merge(
|
||||
data: new IconThemeData(
|
||||
color: iconColor,
|
||||
size: iconSize,
|
||||
),
|
||||
child: decoration.suffixIcon,
|
||||
),
|
||||
child: decoration.suffixIcon,
|
||||
),
|
||||
);
|
||||
|
||||
@ -1737,8 +1756,6 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
|
||||
|
||||
EdgeInsets contentPadding;
|
||||
double floatingLabelHeight;
|
||||
final double leftInset = decoration.prefixIcon == null ? 12.0 : 0.0;
|
||||
final double rightInset = decoration.suffixIcon == null ? 12.0 : 0.0;
|
||||
if (decoration.isCollapsed) {
|
||||
floatingLabelHeight = 0.0;
|
||||
contentPadding = decorationContentPadding ?? EdgeInsets.zero;
|
||||
@ -1747,8 +1764,8 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
|
||||
floatingLabelHeight = 4.0 + 0.75 * inlineLabelStyle.fontSize;
|
||||
if (decoration.filled == true) { // filled == null same as filled == false
|
||||
contentPadding = decorationContentPadding ?? (decorationIsDense
|
||||
? new EdgeInsets.fromLTRB(leftInset, 8.0, rightInset, 8.0)
|
||||
: new EdgeInsets.fromLTRB(leftInset, 12.0, rightInset, 12.0));
|
||||
? const EdgeInsets.fromLTRB(12.0, 8.0, 12.0, 8.0)
|
||||
: const EdgeInsets.fromLTRB(12.0, 12.0, 12.0, 12.0));
|
||||
} else {
|
||||
// Not left or right padding for underline borders that aren't filled
|
||||
// is a small concession to backwards compatibility. This eliminates
|
||||
@ -1760,10 +1777,9 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
|
||||
} else {
|
||||
floatingLabelHeight = 0.0;
|
||||
contentPadding = decorationContentPadding ?? (decorationIsDense
|
||||
? new EdgeInsets.fromLTRB(leftInset, 20.0, rightInset, 12.0)
|
||||
: new EdgeInsets.fromLTRB(leftInset, 24.0, rightInset, 16.0));
|
||||
? const EdgeInsets.fromLTRB(12.0, 20.0, 12.0, 12.0)
|
||||
: const EdgeInsets.fromLTRB(12.0, 24.0, 12.0, 16.0));
|
||||
}
|
||||
|
||||
return new _Decorator(
|
||||
decoration: new _Decoration(
|
||||
contentPadding: contentPadding,
|
||||
|
||||
@ -763,12 +763,11 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
// Overall height for this InputDecorator is 48dps:
|
||||
// 12 - top padding
|
||||
// 16 - input text (ahem font size 16dps)
|
||||
// 12 - bottom padding
|
||||
// 48 - prefix icon
|
||||
// 48 - suffix icon
|
||||
// Overall height for this InputDecorator is 48dps because the prefix icon's minimum size
|
||||
// is 48x48 and the rest of the elements only require 40dps:
|
||||
// 12 - top padding
|
||||
// 16 - input text (ahem font size 16dps)
|
||||
// 12 - bottom padding
|
||||
|
||||
expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 48.0));
|
||||
expect(tester.getSize(find.text('text')).height, 16.0);
|
||||
@ -786,6 +785,132 @@ void main() {
|
||||
expect(tester.getTopRight(find.text('text')).dx, lessThanOrEqualTo(tester.getTopLeft(find.byIcon(Icons.satellite)).dx));
|
||||
});
|
||||
|
||||
testWidgets('prefix/suffix icons are centered when smaller than 48 by 48', (WidgetTester tester) async {
|
||||
const Key prefixKey = const Key('prefix');
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
decoration: const InputDecoration(
|
||||
prefixIcon: const Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: const SizedBox(width: 8.0, height: 8.0, key: prefixKey),
|
||||
),
|
||||
filled: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Overall height for this InputDecorator is 48dps because the prefix icon's minimum size
|
||||
// is 48x48 and the rest of the elements only require 40dps:
|
||||
// 12 - top padding
|
||||
// 16 - input text (ahem font size 16dps)
|
||||
// 12 - bottom padding
|
||||
|
||||
expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 48.0));
|
||||
expect(tester.getSize(find.byKey(prefixKey)).height, 16.0);
|
||||
expect(tester.getTopLeft(find.byKey(prefixKey)).dy, 16.0);
|
||||
});
|
||||
|
||||
testWidgets('prefix/suffix icons increase height of decoration when larger than 48 by 48', (WidgetTester tester) async {
|
||||
const Key prefixKey = const Key('prefix');
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
decoration: const InputDecoration(
|
||||
prefixIcon: const SizedBox(width: 100.0, height: 100.0, key: prefixKey),
|
||||
filled: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Overall height for this InputDecorator is 100dps because the prefix icon's size
|
||||
// is 100x100 and the rest of the elements only require 40dps:
|
||||
// 12 - top padding
|
||||
// 16 - input text (ahem font size 16dps)
|
||||
// 12 - bottom padding
|
||||
|
||||
expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 100.0));
|
||||
expect(tester.getSize(find.byKey(prefixKey)).height, 100.0);
|
||||
expect(tester.getTopLeft(find.byKey(prefixKey)).dy, 0.0);
|
||||
});
|
||||
|
||||
|
||||
testWidgets('counter text has correct right margin - LTR, not dense', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
// isEmpty: false (default)
|
||||
// isFocused: false (default)
|
||||
decoration: const InputDecoration(
|
||||
counterText: 'test',
|
||||
filled: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Margin for text decoration is 12 when filled
|
||||
// (dx) - 12 = (text offset)x.
|
||||
expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 60.0));
|
||||
final double dx = tester.getRect(find.byType(InputDecorator)).right;
|
||||
expect(tester.getRect(find.text('test')).right, dx - 12.0);
|
||||
});
|
||||
|
||||
testWidgets('counter text has correct right margin - RTL, not dense', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
textDirection: TextDirection.rtl,
|
||||
// isEmpty: false (default)
|
||||
// isFocused: false (default)
|
||||
decoration: const InputDecoration(
|
||||
counterText: 'test',
|
||||
filled: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Margin for text decoration is 12 when filled and top left offset is (0, 0)
|
||||
// 0 + 12 = 12.
|
||||
expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 60.0));
|
||||
expect(tester.getRect(find.text('test')).left, 12.0);
|
||||
});
|
||||
|
||||
testWidgets('counter text has correct right margin - LTR, dense', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
// isEmpty: false (default)
|
||||
// isFocused: false (default)
|
||||
decoration: const InputDecoration(
|
||||
counterText: 'test',
|
||||
filled: true,
|
||||
isDense: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Margin for text decoration is 12 when filled
|
||||
// (dx) - 12 = (text offset)x.
|
||||
expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 52.0));
|
||||
final double dx = tester.getRect(find.byType(InputDecorator)).right;
|
||||
expect(tester.getRect(find.text('test')).right, dx - 12.0);
|
||||
});
|
||||
|
||||
testWidgets('counter text has correct right margin - RTL, dense', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
textDirection: TextDirection.rtl,
|
||||
// isEmpty: false (default)
|
||||
// isFocused: false (default)
|
||||
decoration: const InputDecoration(
|
||||
counterText: 'test',
|
||||
filled: true,
|
||||
isDense: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Margin for text decoration is 12 when filled and top left offset is (0, 0)
|
||||
// 0 + 12 = 12.
|
||||
expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 52.0));
|
||||
expect(tester.getRect(find.text('test')).left, 12.0);
|
||||
});
|
||||
|
||||
testWidgets('InputDecorator error/helper/counter RTL layout', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user