diff --git a/packages/flutter/lib/src/material/dropdown_menu.dart b/packages/flutter/lib/src/material/dropdown_menu.dart index 1d8c0f0e9ca..5d9d5984cc5 100644 --- a/packages/flutter/lib/src/material/dropdown_menu.dart +++ b/packages/flutter/lib/src/material/dropdown_menu.dart @@ -48,7 +48,7 @@ const double _kMinimumWidth = 112.0; const double _kDefaultHorizontalPadding = 12.0; -const double _kLeadingIconToInputPadding = 4.0; +const double _kInputStartGap = 4.0; /// Defines a [DropdownMenu] menu button that represents one item view in the menu. /// @@ -641,12 +641,7 @@ class _DropdownMenuState extends State> { return; } setState(() { - final double? leadingWidgetWidth = getWidth(_leadingKey); - if (leadingWidgetWidth != null) { - leadingPadding = leadingWidgetWidth + _kLeadingIconToInputPadding; - } else { - leadingPadding = leadingWidgetWidth; - } + leadingPadding = getWidth(_leadingKey); }); }, debugLabel: 'DropdownMenu.refreshLeadingPadding'); } @@ -718,7 +713,9 @@ class _DropdownMenuState extends State> { int? focusedIndex, bool enableScrollToHighlight = true, bool excludeSemantics = false, + bool? useMaterial3, }) { + final double effectiveInputStartGap = useMaterial3 ?? false ? _kInputStartGap : 0.0; final List result = []; for (int i = 0; i < filteredEntries.length; i++) { final DropdownMenuEntry entry = filteredEntries[i]; @@ -735,14 +732,9 @@ class _DropdownMenuState extends State> { : _kDefaultHorizontalPadding; ButtonStyle effectiveStyle = entry.style ?? - switch (textDirection) { - TextDirection.rtl => MenuItemButton.styleFrom( - padding: EdgeInsets.only(left: _kDefaultHorizontalPadding, right: padding), - ), - TextDirection.ltr => MenuItemButton.styleFrom( - padding: EdgeInsets.only(left: padding, right: _kDefaultHorizontalPadding), - ), - }; + MenuItemButton.styleFrom( + padding: EdgeInsetsDirectional.only(start: padding, end: _kDefaultHorizontalPadding), + ); final ButtonStyle? themeStyle = MenuButtonTheme.of(context).style; @@ -797,7 +789,8 @@ class _DropdownMenuState extends State> { Widget label = entry.labelWidget ?? Text(entry.label); if (widget.width != null) { - final double horizontalPadding = padding + _kDefaultHorizontalPadding; + final double horizontalPadding = + padding + _kDefaultHorizontalPadding + effectiveInputStartGap; label = ConstrainedBox( constraints: BoxConstraints(maxWidth: widget.width! - horizontalPadding), child: label, @@ -809,13 +802,7 @@ class _DropdownMenuState extends State> { child: MenuItemButton( key: enableScrollToHighlight ? buttonItemKeys[i] : null, style: effectiveStyle, - leadingIcon: - entry.leadingIcon != null - ? Padding( - padding: const EdgeInsetsDirectional.only(end: _kLeadingIconToInputPadding), - child: entry.leadingIcon, - ) - : null, + leadingIcon: entry.leadingIcon, trailingIcon: entry.trailingIcon, closeOnActivate: widget.closeBehavior == DropdownMenuCloseBehavior.all, onPressed: @@ -834,7 +821,15 @@ class _DropdownMenuState extends State> { } : null, requestFocusOnHover: false, - child: label, + // MenuItemButton implementation is based on M3 spec for menu which specifies a + // horizontal padding of 12 pixels. + // In the context of DropdownMenu the M3 spec specifies that the menu item and the text + // field content should be aligned. The text field has a horizontal padding of 16 pixels. + // To conform with the 16 pixels padding, a 4 pixels padding is added in front of the item label. + child: Padding( + padding: EdgeInsetsDirectional.only(start: effectiveInputStartGap), + child: label, + ), ), ); result.add(menuItemButton); @@ -924,6 +919,7 @@ class _DropdownMenuState extends State> { @override Widget build(BuildContext context) { + final bool useMaterial3 = Theme.of(context).useMaterial3; final TextDirection textDirection = Directionality.of(context); _initialMenu ??= _buildButtons( widget.dropdownMenuEntries, @@ -931,6 +927,7 @@ class _DropdownMenuState extends State> { enableScrollToHighlight: false, // The _initialMenu is invisible, we should not add semantics nodes to it excludeSemantics: true, + useMaterial3: useMaterial3, ); final DropdownMenuThemeData theme = DropdownMenuTheme.of(context); final DropdownMenuThemeData defaults = _DropdownMenuDefaultsM3(context); @@ -963,6 +960,7 @@ class _DropdownMenuState extends State> { filteredEntries, textDirection, focusedIndex: currentHighlight, + useMaterial3: useMaterial3, ); final TextStyle? effectiveTextStyle = widget.textStyle ?? theme.textStyle ?? defaults.textStyle; diff --git a/packages/flutter/lib/src/material/input_decorator.dart b/packages/flutter/lib/src/material/input_decorator.dart index 24bf5036c6c..bcf68c1edc9 100644 --- a/packages/flutter/lib/src/material/input_decorator.dart +++ b/packages/flutter/lib/src/material/input_decorator.dart @@ -38,6 +38,13 @@ const Duration _kTransitionDuration = Duration(milliseconds: 167); const Curve _kTransitionCurve = Curves.fastOutSlowIn; const double _kFinalLabelScale = 0.75; +// From the M3 spec, horizontal padding is 12 pixels for the prefix icon and +// 16 pixels for the input content. +// InputDecorator default padding is set to 12 pixels because 16 pixels will move +// the prefix icon too far. +// An extra padding should be added for the input content to comply with the 16 pixels padding. +const double _kInputExtraPadding = 4.0; + typedef _SubtextSize = ({double ascent, double bottomHeight, double subtextHeight}); typedef _ChildBaselineGetter = double Function(RenderBox child, BoxConstraints constraints); @@ -565,6 +572,7 @@ class _Decoration { required this.isDense, required this.isEmpty, required this.visualDensity, + required this.inputGap, required this.maintainHintSize, this.icon, this.input, @@ -590,6 +598,7 @@ class _Decoration { final bool? isDense; final bool isEmpty; final VisualDensity visualDensity; + final double inputGap; final bool maintainHintSize; final Widget? icon; final Widget? input; @@ -623,6 +632,7 @@ class _Decoration { other.isDense == isDense && other.isEmpty == isEmpty && other.visualDensity == visualDensity && + other.inputGap == inputGap && other.maintainHintSize == maintainHintSize && other.icon == icon && other.input == input && @@ -649,6 +659,7 @@ class _Decoration { isDense, isEmpty, visualDensity, + inputGap, maintainHintSize, icon, input, @@ -657,8 +668,7 @@ class _Decoration { prefix, suffix, prefixIcon, - suffixIcon, - Object.hash(helperError, counter, container), + Object.hash(suffixIcon, helperError, counter, container), ); } @@ -967,10 +977,14 @@ class _RenderDecoration extends RenderBox start: iconWidth + prefixSize.width + - (prefixIcon == null ? contentPadding.start : prefixIconSize.width + prefixToInputGap), + (prefixIcon == null + ? contentPadding.start + decoration.inputGap + : prefixIconSize.width + prefixToInputGap), end: suffixSize.width + - (suffixIcon == null ? contentPadding.end : suffixIconSize.width + inputToSuffixGap), + (suffixIcon == null + ? contentPadding.end + decoration.inputGap + : suffixIconSize.width + inputToSuffixGap), ); final double inputWidth = math.max( @@ -987,7 +1001,11 @@ class _RenderDecoration extends RenderBox final double labelWidth = math.max( 0.0, constraints.maxWidth - - (iconWidth + contentPadding.horizontal + prefixIconSize.width + suffixIconSpace), + (decoration.inputGap * 2 + + iconWidth + + contentPadding.horizontal + + prefixIconSize.width + + suffixIconSpace), ); // Increase the available width for the label when it is scaled down. @@ -1168,13 +1186,13 @@ class _RenderDecoration extends RenderBox ? math.max(_minWidth(input, height), _minWidth(hint, height)) : _minWidth(input, height); return _minWidth(icon, height) + - (prefixIcon != null ? prefixToInputGap : contentPadding.start) + + (prefixIcon != null ? prefixToInputGap : contentPadding.start + decoration.inputGap) + _minWidth(prefixIcon, height) + _minWidth(prefix, height) + contentWidth + _minWidth(suffix, height) + _minWidth(suffixIcon, height) + - (suffixIcon != null ? inputToSuffixGap : contentPadding.end); + (suffixIcon != null ? inputToSuffixGap : contentPadding.end + decoration.inputGap); } @override @@ -1184,13 +1202,13 @@ class _RenderDecoration extends RenderBox ? math.max(_maxWidth(input, height), _maxWidth(hint, height)) : _maxWidth(input, height); return _maxWidth(icon, height) + - (prefixIcon != null ? prefixToInputGap : contentPadding.start) + + (prefixIcon != null ? prefixToInputGap : contentPadding.start + decoration.inputGap) + _maxWidth(prefixIcon, height) + _maxWidth(prefix, height) + contentWidth + _maxWidth(suffix, height) + _maxWidth(suffixIcon, height) + - (suffixIcon != null ? inputToSuffixGap : contentPadding.end); + (suffixIcon != null ? inputToSuffixGap : contentPadding.end + decoration.inputGap); } double _lineHeight(double width, List boxes) { @@ -1375,10 +1393,13 @@ class _RenderDecoration extends RenderBox case TextDirection.ltr: start = contentPadding.start + _boxSize(icon).width; end = overallWidth - contentPadding.end; - _boxParentData(helperError).offset = Offset(start, subtextBaseline - helperErrorBaseline); + _boxParentData(helperError).offset = Offset( + start + decoration.inputGap, + subtextBaseline - helperErrorBaseline, + ); if (counter != null) { _boxParentData(counter).offset = Offset( - end - counter.size.width, + end - counter.size.width - decoration.inputGap, subtextBaseline - counterBaseline, ); } @@ -1386,11 +1407,14 @@ class _RenderDecoration extends RenderBox start = overallWidth - contentPadding.start - _boxSize(icon).width; end = contentPadding.end; _boxParentData(helperError).offset = Offset( - start - helperError.size.width, + start - helperError.size.width - decoration.inputGap, subtextBaseline - helperErrorBaseline, ); if (counter != null) { - _boxParentData(counter).offset = Offset(end, subtextBaseline - counterBaseline); + _boxParentData(counter).offset = Offset( + end + decoration.inputGap, + subtextBaseline - counterBaseline, + ); } } @@ -1410,6 +1434,8 @@ class _RenderDecoration extends RenderBox start += contentPadding.start; start -= centerLayout(prefixIcon!, start - prefixIcon!.size.width); start -= prefixToInputGap; + } else { + start -= decoration.inputGap; } if (label != null) { if (decoration.alignLabelWithHint) { @@ -1431,6 +1457,8 @@ class _RenderDecoration extends RenderBox end -= contentPadding.end; end += centerLayout(suffixIcon!, end); end += inputToSuffixGap; + } else { + end += decoration.inputGap; } if (suffix != null) { end += baselineLayout(suffix!, end); @@ -1443,6 +1471,8 @@ class _RenderDecoration extends RenderBox start -= contentPadding.start; start += centerLayout(prefixIcon!, start); start += prefixToInputGap; + } else { + start += decoration.inputGap; } if (label != null) { if (decoration.alignLabelWithHint) { @@ -1464,6 +1494,8 @@ class _RenderDecoration extends RenderBox end += contentPadding.end; end -= centerLayout(suffixIcon!, end - suffixIcon!.size.width); end -= inputToSuffixGap; + } else { + end -= decoration.inputGap; } if (suffix != null) { end -= baselineLayout(suffix!, end - suffix!.size.width); @@ -2258,10 +2290,9 @@ class _InputDecoratorState extends State with TickerProviderStat Widget build(BuildContext context) { final ThemeData themeData = Theme.of(context); final VisualDensity visualDensity = decoration.visualDensity ?? themeData.visualDensity; + final bool useMaterial3 = Theme.of(context).useMaterial3; final InputDecorationTheme defaults = - themeData.useMaterial3 - ? _InputDecoratorDefaultsM3(context) - : _InputDecoratorDefaultsM2(context); + useMaterial3 ? _InputDecoratorDefaultsM3(context) : _InputDecoratorDefaultsM2(context); final InputDecorationTheme inputDecorationTheme = themeData.inputDecorationTheme; final IconButtonThemeData iconButtonTheme = IconButtonTheme.of(context); @@ -2551,7 +2582,7 @@ class _InputDecoratorState extends State with TickerProviderStat if (decoration.filled ?? false) { contentPadding = decorationContentPadding ?? - (Theme.of(context).useMaterial3 + (useMaterial3 ? decorationIsDense ? const EdgeInsetsDirectional.fromSTEB(12.0, 4.0, 12.0, 4.0) : const EdgeInsetsDirectional.fromSTEB(12.0, 8.0, 12.0, 8.0) @@ -2564,7 +2595,7 @@ class _InputDecoratorState extends State with TickerProviderStat // the most noticeable layout change introduced by #13734. contentPadding = decorationContentPadding ?? - (Theme.of(context).useMaterial3 + (useMaterial3 ? decorationIsDense ? const EdgeInsetsDirectional.fromSTEB(0.0, 4.0, 0.0, 4.0) : const EdgeInsetsDirectional.fromSTEB(0.0, 8.0, 0.0, 8.0) @@ -2576,7 +2607,7 @@ class _InputDecoratorState extends State with TickerProviderStat floatingLabelHeight = 0.0; contentPadding = decorationContentPadding ?? - (Theme.of(context).useMaterial3 + (useMaterial3 ? decorationIsDense ? const EdgeInsetsDirectional.fromSTEB(12.0, 16.0, 12.0, 8.0) : const EdgeInsetsDirectional.fromSTEB(12.0, 20.0, 12.0, 12.0) @@ -2585,10 +2616,20 @@ class _InputDecoratorState extends State with TickerProviderStat : const EdgeInsetsDirectional.fromSTEB(12.0, 24.0, 12.0, 16.0)); } + double inputGap = 0.0; + if (useMaterial3) { + if (border is OutlineInputBorder) { + inputGap = border.gapPadding; + } else { + inputGap = border.isOutline || (decoration.filled ?? false) ? _kInputExtraPadding : 0.0; + } + } + final _Decorator decorator = _Decorator( decoration: _Decoration( contentPadding: contentPadding, isCollapsed: decoration.isCollapsed ?? themeData.inputDecorationTheme.isCollapsed, + inputGap: inputGap, floatingLabelHeight: floatingLabelHeight, floatingLabelAlignment: decoration.floatingLabelAlignment!, floatingLabelProgress: _floatingLabelAnimation.value, diff --git a/packages/flutter/test/material/dropdown_menu_test.dart b/packages/flutter/test/material/dropdown_menu_test.dart index 073230aeaf0..bdc0c6d2fdb 100644 --- a/packages/flutter/test/material/dropdown_menu_test.dart +++ b/packages/flutter/test/material/dropdown_menu_test.dart @@ -624,7 +624,7 @@ void main() { final Finder textField = find.byType(TextField); final double anchorWidth = tester.getSize(textField).width; - expect(anchorWidth, closeTo(180.5, 0.1)); + expect(anchorWidth, closeTo(184.5, 0.1)); await tester.tap(find.byType(DropdownMenu)); await tester.pumpAndSettle(); @@ -634,7 +634,7 @@ void main() { .ancestor(of: find.byType(SingleChildScrollView), matching: find.byType(Material)) .first; final double menuWidth = tester.getSize(menuMaterial).width; - expect(menuWidth, closeTo(180.5, 0.1)); + expect(menuWidth, closeTo(184.5, 0.1)); // The text field should have same width as the menu // when the width property is not null. @@ -741,10 +741,14 @@ void main() { final double width = tester.getSize(find.byType(DropdownMenu)).width; const double menuEntryPadding = 24.0; // See _kDefaultHorizontalPadding. + const double decorationStartGap = 4.0; // See _kInputStartGap. const double leadingWidth = 16.0; const double trailingWidth = 56.0; - expect(width, entryLabelWidth + leadingWidth + trailingWidth + menuEntryPadding); + expect( + width, + entryLabelWidth + leadingWidth + trailingWidth + menuEntryPadding + decorationStartGap, + ); }); testWidgets('The width is determined by the label when it is longer than menu entries', ( @@ -994,7 +998,6 @@ void main() { .ancestor(of: find.byType(SingleChildScrollView), matching: find.byType(Padding)) .first; final Size menuViewSize = tester.getSize(menuView); - expect(menuViewSize.width, closeTo(180.6, 0.1)); expect(menuViewSize.height, equals(304.0)); // 304 = 288 + vertical padding(2 * 8) // Constrains the menu height. @@ -1011,7 +1014,6 @@ void main() { .first; final Size updatedMenuSize = tester.getSize(updatedMenu); - expect(updatedMenuSize.width, closeTo(180.6, 0.1)); expect(updatedMenuSize.height, equals(100.0)); }, ); diff --git a/packages/flutter/test/material/input_decorator_test.dart b/packages/flutter/test/material/input_decorator_test.dart index 301149a0296..6b2ff3824b5 100644 --- a/packages/flutter/test/material/input_decorator_test.dart +++ b/packages/flutter/test/material/input_decorator_test.dart @@ -2286,6 +2286,37 @@ void main() { group('Material3 - InputDecoration label', () { group('for filled text field', () { + testWidgets('label and input horizontal positions are M3 compliant in LTR', ( + WidgetTester tester, + ) async { + await tester.pumpWidget( + buildInputDecorator( + decoration: const InputDecoration(filled: true, labelText: labelText), + ), + ); + + expect(getDecoratorRect(tester).size, const Size(800.0, 56.0)); + const double labelAndInputStart = 12.0 + 4.0; // Content left padding + default input gap. + expect(getLabelRect(tester).left, labelAndInputStart); + expect(getInputRect(tester).left, labelAndInputStart); + }); + + testWidgets('label and input horizontal positions are M3 compliant in RTL', ( + WidgetTester tester, + ) async { + await tester.pumpWidget( + buildInputDecorator( + decoration: const InputDecoration(filled: true, labelText: labelText), + textDirection: TextDirection.rtl, + ), + ); + + expect(getDecoratorRect(tester).size, const Size(800.0, 56.0)); + const double labelAndInputStart = 12.0 + 4.0; // Content left padding + default input gap. + expect(getLabelRect(tester).right, 800.0 - labelAndInputStart); + expect(getInputRect(tester).right, 800.0 - labelAndInputStart); + }); + group('when field is enabled', () { testWidgets('label text has correct style', (WidgetTester tester) async { await tester.pumpWidget( @@ -2434,6 +2465,76 @@ void main() { }); group('for outlined text field', () { + testWidgets('label and input horizontal positions are M3 compliant in LTR', ( + WidgetTester tester, + ) async { + await tester.pumpWidget( + buildInputDecorator( + decoration: const InputDecoration(border: OutlineInputBorder(), labelText: labelText), + ), + ); + + expect(getDecoratorRect(tester).size, const Size(800.0, 56.0)); + const double labelAndInputStart = 12.0 + 4.0; // Content left padding + default input gap. + expect(getLabelRect(tester).left, labelAndInputStart); + expect(getInputRect(tester).left, labelAndInputStart); + }); + + testWidgets('label and input horizontal positions are M3 compliant in RTL', ( + WidgetTester tester, + ) async { + await tester.pumpWidget( + buildInputDecorator( + decoration: const InputDecoration(border: OutlineInputBorder(), labelText: labelText), + textDirection: TextDirection.rtl, + ), + ); + + expect(getDecoratorRect(tester).size, const Size(800.0, 56.0)); + const double labelAndInputStart = 12.0 + 4.0; // Content left padding + default input gap. + expect(getLabelRect(tester).right, 800 - labelAndInputStart); + expect(getInputRect(tester).right, 800 - labelAndInputStart); + }); + + testWidgets('label and input horizontal positions can be adjusted in LTR', ( + WidgetTester tester, + ) async { + const double customGap = 6.0; + await tester.pumpWidget( + buildInputDecorator( + decoration: const InputDecoration( + border: OutlineInputBorder(gapPadding: customGap), + labelText: labelText, + ), + ), + ); + + expect(getDecoratorRect(tester).size, const Size(800.0, 56.0)); + const double labelAndInputStart = 12.0 + customGap; // Content left padding + input gap. + expect(getLabelRect(tester).left, labelAndInputStart); + expect(getInputRect(tester).left, labelAndInputStart); + }); + + testWidgets('label and input horizontal positions can be adjusted in RTL', ( + WidgetTester tester, + ) async { + const double customGap = 6.0; + await tester.pumpWidget( + buildInputDecorator( + decoration: const InputDecoration( + border: OutlineInputBorder(gapPadding: customGap), + labelText: labelText, + ), + textDirection: TextDirection.rtl, + ), + ); + + expect(getDecoratorRect(tester).size, const Size(800.0, 56.0)); + const double labelAndInputStart = 12.0 + customGap; // Content left padding + input gap. + expect(getLabelRect(tester).right, 800.0 - labelAndInputStart); + expect(getInputRect(tester).right, 800.0 - labelAndInputStart); + }); + group('when field is enabled', () { testWidgets('label text has correct style', (WidgetTester tester) async { await tester.pumpWidget( @@ -2602,6 +2703,35 @@ void main() { }); }); + testWidgets( + 'Label and input for non-filled and non-outlined text field have no horizontal padding in LTR', + (WidgetTester tester) async { + await tester.pumpWidget( + buildInputDecorator(decoration: const InputDecoration(labelText: labelText)), + ); + + expect(getDecoratorRect(tester).size, const Size(800.0, 56.0)); + expect(getLabelRect(tester).left, 0.0); + expect(getInputRect(tester).left, 0.0); + }, + ); + + testWidgets( + 'Label and input for non-filled and non-outlined text field have no horizontal padding in RTL', + (WidgetTester tester) async { + await tester.pumpWidget( + buildInputDecorator( + decoration: const InputDecoration(labelText: labelText), + textDirection: TextDirection.rtl, + ), + ); + + expect(getDecoratorRect(tester).size, const Size(800.0, 56.0)); + expect(getLabelRect(tester).right, 800.0); + expect(getInputRect(tester).right, 800.0); + }, + ); + testWidgets('floatingLabelStyle overrides default style', (WidgetTester tester) async { const TextStyle floatingLabelStyle = TextStyle(color: Colors.indigo, fontSize: 16.0); @@ -2881,43 +3011,43 @@ void main() { await pumpDecorator(focused: false); await tester.pump(kTransitionDuration); const Size labelSize = Size(82.5, 16); - expect(getLabelRect(tester).topLeft, equals(const Offset(12, 20))); + expect(getLabelRect(tester).topLeft, equals(const Offset(16, 20))); expect(getLabelRect(tester).size, equals(labelSize)); await pumpDecorator(focused: false, empty: false); await tester.pump(kTransitionDuration); - expect(getLabelRect(tester).topLeft, equals(const Offset(12, -5.5))); + expect(getLabelRect(tester).topLeft, equals(const Offset(16, -5.5))); expect(getLabelRect(tester).size, equals(labelSize * 0.75)); await pumpDecorator(focused: true); await tester.pump(kTransitionDuration); - expect(getLabelRect(tester).topLeft, equals(const Offset(12, -5.5))); + expect(getLabelRect(tester).topLeft, equals(const Offset(16, -5.5))); expect(getLabelRect(tester).size, equals(labelSize * 0.75)); await pumpDecorator(focused: true, empty: false); await tester.pump(kTransitionDuration); - expect(getLabelRect(tester).topLeft, equals(const Offset(12, -5.5))); + expect(getLabelRect(tester).topLeft, equals(const Offset(16, -5.5))); expect(getLabelRect(tester).size, equals(labelSize * 0.75)); await pumpDecorator(focused: false, enabled: false); await tester.pump(kTransitionDuration); - expect(getLabelRect(tester).topLeft, equals(const Offset(12, 20))); + expect(getLabelRect(tester).topLeft, equals(const Offset(16, 20))); expect(getLabelRect(tester).size, equals(labelSize)); await pumpDecorator(focused: false, empty: false, enabled: false); await tester.pump(kTransitionDuration); - expect(getLabelRect(tester).topLeft, equals(const Offset(12, -5.5))); + expect(getLabelRect(tester).topLeft, equals(const Offset(16, -5.5))); expect(getLabelRect(tester).size, equals(labelSize * 0.75)); // Focused and disabled happens with NavigationMode.directional. await pumpDecorator(focused: true, enabled: false); await tester.pump(kTransitionDuration); - expect(getLabelRect(tester).topLeft, equals(const Offset(12, 20))); + expect(getLabelRect(tester).topLeft, equals(const Offset(16, 20))); expect(getLabelRect(tester).size, equals(labelSize)); await pumpDecorator(focused: true, empty: false, enabled: false); await tester.pump(kTransitionDuration); - expect(getLabelRect(tester).topLeft, equals(const Offset(12, -5.5))); + expect(getLabelRect(tester).topLeft, equals(const Offset(16, -5.5))); expect(getLabelRect(tester).size, equals(labelSize * 0.75)); }); @@ -5128,9 +5258,8 @@ void main() { const double fullHeight = containerHeight + helperGap + helperHeight; // 76.0 const double errorHeight = helperHeight; const double hintHeight = inputHeight; - // TODO(bleroux): consider changing this padding because, from the M3 specification, it should be 16. - const double helperStartPadding = 12.0; - const double counterEndPadding = 12.0; + const double helperStartPadding = 16.0; + const double counterEndPadding = 16.0; group('for filled text field', () { group('when field is enabled', () { diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart index dca2948b192..11040fad341 100644 --- a/packages/flutter/test/material/text_field_test.dart +++ b/packages/flutter/test/material/text_field_test.dart @@ -5529,12 +5529,12 @@ void main() { ), ); final double iconRight = tester.getTopRight(find.byType(Icon)).dx; - // Per https://material.io/go/design-text-fields#text-fields-layout - // There's a 16 dps gap between the right edge of the icon and the text field's - // container, and the 12dps more padding between the left edge of the container - // and the left edge of the input and label. - expect(iconRight + 28.0, equals(tester.getTopLeft(find.text('label')).dx)); - expect(iconRight + 28.0, equals(tester.getTopLeft(find.byType(EditableText)).dx)); + // There's a 16 pixels gap between the right edge of the icon and the text field's + // container, and, per https://material.io/go/design-text-fields#text-fields-layout, + // 16 pixels more padding between the left edge of the container and the left edge + // of the input and label. + expect(iconRight + 16.0 + 16.0, equals(tester.getTopLeft(find.text('label')).dx)); + expect(iconRight + 16.0 + 16.0, equals(tester.getTopLeft(find.byType(EditableText)).dx)); }); testWidgets('Collapsed hint text placement', (WidgetTester tester) async {