Fix InputDecorator default hint text style on M3 (#148944)

## Description

This PRs makes the `InputDecoration.hintText` style compliant with the M3 spec.
The hint style is not clearly specified in https://m3.material.io/components/text-fields/specs, but it is in the M3 Figma kit.
('hint' terminology came from the Material1 specification, since M2 the terminology is 'Placeholder').

See this Figma screenshot taken while focusing on the 'Placeholder' text (which corresponds to hint).

![image](https://github.com/flutter/flutter/assets/840911/58d3d5c9-0984-497a-9d47-4724dcd7b2b3)

It seems that the intention is that the 'Placeholder' colors should be the same as the 'supporting text' ones, that is why is reused 'supporting text' tokens.

## Related Issue

Fixes https://github.com/flutter/flutter/issues/148787.

## Tests

Updates several tests.
This commit is contained in:
Bruno Leroux 2024-05-31 22:56:07 +02:00 committed by GitHub
parent 2b700dda38
commit b201fbdee5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 60 additions and 55 deletions

View file

@ -34,9 +34,9 @@ class _${blockName}DefaultsM3 extends InputDecorationTheme {
@override
TextStyle? get hintStyle => MaterialStateTextStyle.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return TextStyle(color: Theme.of(context).disabledColor);
return TextStyle(color: ${componentColor('md.comp.filled-text-field.disabled.supporting-text')});
}
return TextStyle(color: Theme.of(context).hintColor);
return TextStyle(color: ${componentColor('md.comp.filled-text-field.supporting-text')});
});
@override

View file

@ -1754,8 +1754,10 @@ class InputDecorator extends StatefulWidget {
/// The style on which to base the label, hint, counter, and error styles
/// if the [decoration] does not provide explicit styles.
///
/// If null, [baseStyle] defaults to the `titleMedium` style from the
/// current [Theme], see [ThemeData.textTheme].
/// If null, [TextTheme.bodyLarge] will be used.
///
/// If null and [ThemeData.useMaterial3] is false, [TextTheme.titleMedium] will
/// be used.
///
/// The [TextStyle.textBaseline] of the [baseStyle] is used to determine
/// the baseline used for text alignment.
@ -2062,7 +2064,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
final TextStyle? style = MaterialStateProperty.resolveAs(decoration.hintStyle, materialState)
?? MaterialStateProperty.resolveAs(themeData.inputDecorationTheme.hintStyle, materialState);
return themeData.textTheme.titleMedium!
return (themeData.useMaterial3 ? themeData.textTheme.bodyLarge! : themeData.textTheme.titleMedium!)
.merge(widget.baseStyle)
.merge(defaultStyle)
.merge(style);
@ -4672,9 +4674,9 @@ class _InputDecoratorDefaultsM3 extends InputDecorationTheme {
@override
TextStyle? get hintStyle => MaterialStateTextStyle.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return TextStyle(color: Theme.of(context).disabledColor);
return TextStyle(color: _colors.onSurface.withOpacity(0.38));
}
return TextStyle(color: Theme.of(context).hintColor);
return TextStyle(color: _colors.onSurfaceVariant);
});
@override

View file

@ -2835,10 +2835,8 @@ void main() {
expect(getHintOpacity(tester), 1.0);
final ThemeData theme = Theme.of(tester.element(findDecorator()));
// TODO(bleroux): from M3 specification, it should be theme.colorScheme.onSurface.
final Color expectedColor = theme.hintColor;
// TODO(bleroux): from M3 specification, it should be textTheme.bodyLarge.
final TextStyle expectedStyle = theme.textTheme.titleMedium!.copyWith(color: expectedColor);
final Color expectedColor = theme.colorScheme.onSurfaceVariant;
final TextStyle expectedStyle = theme.textTheme.bodyLarge!.copyWith(color: expectedColor);
expect(getHintStyle(tester), expectedStyle);
});
});
@ -2901,10 +2899,8 @@ void main() {
expect(getHintOpacity(tester), 1.0);
final ThemeData theme = Theme.of(tester.element(findDecorator()));
// TODO(bleroux): from M3 specification, it should be theme.colorScheme.onSurface.withOpacity(0.38).
final Color expectedColor = theme.disabledColor;
// TODO(bleroux): from M3 specification, it should be textTheme.bodyLarge.
final TextStyle expectedStyle = theme.textTheme.titleMedium!.copyWith(color: expectedColor);
final Color expectedColor = theme.colorScheme.onSurface.withOpacity(0.38);
final TextStyle expectedStyle = theme.textTheme.bodyLarge!.copyWith(color: expectedColor);
expect(getHintStyle(tester), expectedStyle);
});
});
@ -2967,10 +2963,8 @@ void main() {
expect(getHintOpacity(tester), 1.0);
final ThemeData theme = Theme.of(tester.element(findDecorator()));
// TODO(bleroux): from M3 specification, it should be theme.colorScheme.onSurface.
final Color expectedColor = theme.hintColor;
// TODO(bleroux): from M3 specification, it should be textTheme.bodyLarge.
final TextStyle expectedStyle = theme.textTheme.titleMedium!.copyWith(color: expectedColor);
final Color expectedColor = theme.colorScheme.onSurfaceVariant;
final TextStyle expectedStyle = theme.textTheme.bodyLarge!.copyWith(color: expectedColor);
expect(getHintStyle(tester), expectedStyle);
});
});
@ -3033,10 +3027,8 @@ void main() {
expect(getHintOpacity(tester), 1.0);
final ThemeData theme = Theme.of(tester.element(findDecorator()));
// TODO(bleroux): from M3 specification, it should be theme.colorScheme.onSurface.
final Color expectedColor = theme.hintColor;
// TODO(bleroux): from M3 specification, it should be textTheme.bodyLarge.
final TextStyle expectedStyle = theme.textTheme.titleMedium!.copyWith(color: expectedColor);
final Color expectedColor = theme.colorScheme.onSurfaceVariant;
final TextStyle expectedStyle = theme.textTheme.bodyLarge!.copyWith(color: expectedColor);
expect(getHintStyle(tester), expectedStyle);
});
});
@ -3099,10 +3091,8 @@ void main() {
expect(getHintOpacity(tester), 1.0);
final ThemeData theme = Theme.of(tester.element(findDecorator()));
// TODO(bleroux): from M3 specification, it should be theme.colorScheme.onSurface.
final Color expectedColor = theme.hintColor;
// TODO(bleroux): from M3 specification, it should be textTheme.bodyLarge.
final TextStyle expectedStyle = theme.textTheme.titleMedium!.copyWith(color: expectedColor);
final Color expectedColor = theme.colorScheme.onSurfaceVariant;
final TextStyle expectedStyle = theme.textTheme.bodyLarge!.copyWith(color: expectedColor);
expect(getHintStyle(tester), expectedStyle);
});
});
@ -3309,10 +3299,8 @@ void main() {
expect(getHintOpacity(tester), 1.0);
final ThemeData theme = Theme.of(tester.element(findDecorator()));
// TODO(bleroux): from M3 specification, it should be theme.colorScheme.onSurface.
final Color expectedColor = theme.hintColor;
// TODO(bleroux): from M3 specification, it should be textTheme.bodyLarge.
final TextStyle expectedStyle = theme.textTheme.titleMedium!.copyWith(color: expectedColor);
final Color expectedColor = theme.colorScheme.onSurfaceVariant;
final TextStyle expectedStyle = theme.textTheme.bodyLarge!.copyWith(color: expectedColor);
expect(getHintStyle(tester), expectedStyle);
});
});
@ -3423,10 +3411,8 @@ void main() {
expect(getHintOpacity(tester), 1.0);
final ThemeData theme = Theme.of(tester.element(findDecorator()));
// TODO(bleroux): from M3 specification, it should be theme.colorScheme.onSurface.
final Color expectedColor = theme.hintColor;
// TODO(bleroux): from M3 specification, it should be textTheme.bodyLarge.
final TextStyle expectedStyle = theme.textTheme.titleMedium!.copyWith(color: expectedColor);
final Color expectedColor = theme.colorScheme.onSurfaceVariant;
final TextStyle expectedStyle = theme.textTheme.bodyLarge!.copyWith(color: expectedColor);
expect(getHintStyle(tester), expectedStyle);
});
});
@ -3489,10 +3475,8 @@ void main() {
expect(getHintOpacity(tester), 1.0);
final ThemeData theme = Theme.of(tester.element(findDecorator()));
// TODO(bleroux): from M3 specification, it should be theme.colorScheme.onSurface.withOpacity(0.38).
final Color expectedColor = theme.disabledColor;
// TODO(bleroux): from M3 specification, it should be textTheme.bodyLarge.
final TextStyle expectedStyle = theme.textTheme.titleMedium!.copyWith(color: expectedColor);
final Color expectedColor = theme.colorScheme.onSurface.withOpacity(0.38);
final TextStyle expectedStyle = theme.textTheme.bodyLarge!.copyWith(color: expectedColor);
expect(getHintStyle(tester), expectedStyle);
});
});
@ -3555,10 +3539,8 @@ void main() {
expect(getHintOpacity(tester), 1.0);
final ThemeData theme = Theme.of(tester.element(findDecorator()));
// TODO(bleroux): from M3 specification, it should be theme.colorScheme.onSurface.
final Color expectedColor = theme.hintColor;
// TODO(bleroux): from M3 specification, it should be textTheme.bodyLarge.
final TextStyle expectedStyle = theme.textTheme.titleMedium!.copyWith(color: expectedColor);
final Color expectedColor = theme.colorScheme.onSurfaceVariant;
final TextStyle expectedStyle = theme.textTheme.bodyLarge!.copyWith(color: expectedColor);
expect(getHintStyle(tester), expectedStyle);
});
});
@ -3621,10 +3603,8 @@ void main() {
expect(getHintOpacity(tester), 1.0);
final ThemeData theme = Theme.of(tester.element(findDecorator()));
// TODO(bleroux): from M3 specification, it should be theme.colorScheme.onSurface.
final Color expectedColor = theme.hintColor;
// TODO(bleroux): from M3 specification, it should be textTheme.bodyLarge.
final TextStyle expectedStyle = theme.textTheme.titleMedium!.copyWith(color: expectedColor);
final Color expectedColor = theme.colorScheme.onSurfaceVariant;
final TextStyle expectedStyle = theme.textTheme.bodyLarge!.copyWith(color: expectedColor);
expect(getHintStyle(tester), expectedStyle);
});
});
@ -3687,10 +3667,8 @@ void main() {
expect(getHintOpacity(tester), 1.0);
final ThemeData theme = Theme.of(tester.element(findDecorator()));
// TODO(bleroux): from M3 specification, it should be theme.colorScheme.onSurface.
final Color expectedColor = theme.hintColor;
// TODO(bleroux): from M3 specification, it should be textTheme.bodyLarge.
final TextStyle expectedStyle = theme.textTheme.titleMedium!.copyWith(color: expectedColor);
final Color expectedColor = theme.colorScheme.onSurfaceVariant;
final TextStyle expectedStyle = theme.textTheme.bodyLarge!.copyWith(color: expectedColor);
expect(getHintStyle(tester), expectedStyle);
});
});
@ -3895,10 +3873,8 @@ void main() {
expect(getHintOpacity(tester), 1.0);
final ThemeData theme = Theme.of(tester.element(findDecorator()));
// TODO(bleroux): from M3 specification, it should be theme.colorScheme.onSurface.
final Color expectedColor = theme.hintColor;
// TODO(bleroux): from M3 specification, it should be textTheme.bodyLarge.
final TextStyle expectedStyle = theme.textTheme.titleMedium!.copyWith(color: expectedColor);
final Color expectedColor = theme.colorScheme.onSurfaceVariant;
final TextStyle expectedStyle = theme.textTheme.bodyLarge!.copyWith(color: expectedColor);
expect(getHintStyle(tester), expectedStyle);
});
});

View file

@ -5409,11 +5409,38 @@ void main() {
});
testWidgets('TextField with default hintStyle', (WidgetTester tester) async {
final TextStyle style = TextStyle(
color: Colors.pink[500],
fontSize: 10.0,
);
final ThemeData themeData = ThemeData();
await tester.pumpWidget(
overlay(
child: Theme(
data: themeData,
child: TextField(
decoration: const InputDecoration(
hintText: 'Placeholder',
),
style: style,
),
),
),
);
final Text hintText = tester.widget(find.text('Placeholder'));
expect(hintText.style!.color, themeData.colorScheme.onSurfaceVariant);
expect(hintText.style!.fontSize, style.fontSize);
});
testWidgets('Material2 - TextField with default hintStyle', (WidgetTester tester) async {
final TextStyle style = TextStyle(
color: Colors.pink[500],
fontSize: 10.0,
);
final ThemeData themeData = ThemeData(
useMaterial3: false,
hintColor: Colors.blue[500],
);