Fix InputDecorator suffix and prefix IconButtons ignore IconButtonTheme
(#145473)
fixes [DropdownMenu TrailingIcon can't be styled through providing an IconButtonTheme](https://github.com/flutter/flutter/issues/145081) ### Code sample <details> <summary>expand to view the code sample</summary> ```dart import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: Scaffold( body: Center( child: IconButtonTheme( data: IconButtonThemeData( style: IconButton.styleFrom( foregroundColor: const Color(0xffff0000), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10.0), ), ), ), child: SizedBox( width: 300, child: Column( mainAxisSize: MainAxisSize.min, children: [ const Text('IconButton'), IconButton(onPressed: () {}, icon: const Icon(Icons.search)), const Text('TextField'), TextField( decoration: InputDecoration( prefixIcon: IconButton( onPressed: () {}, icon: const Icon(Icons.search), ), suffixIcon: IconButton( onPressed: () {}, icon: const Icon(Icons.search), ), ), ), ], ), ), ), ), ), ); } } ``` </details> | Before | After | | --------------- | --------------- | | <img src="https://github.com/flutter/flutter/assets/48603081/69b5966b-c95d-4934-b867-3262d1377f70" /> | <img src="https://github.com/flutter/flutter/assets/48603081/0064db2b-0379-4424-a5bf-39bdc5441fe8" /> |
This commit is contained in:
parent
1d89ae3f65
commit
7c72a089e1
@ -2055,15 +2055,24 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
|
||||
?? MaterialStateProperty.resolveAs(defaults.iconColor!, materialState);
|
||||
}
|
||||
|
||||
Color _getPrefixIconColor(ThemeData themeData, InputDecorationTheme defaults) {
|
||||
Color _getPrefixIconColor(
|
||||
InputDecorationTheme inputDecorationTheme,
|
||||
IconButtonThemeData iconButtonTheme,
|
||||
InputDecorationTheme defaults) {
|
||||
return MaterialStateProperty.resolveAs(decoration.prefixIconColor, materialState)
|
||||
?? MaterialStateProperty.resolveAs(themeData.inputDecorationTheme.prefixIconColor, materialState)
|
||||
?? MaterialStateProperty.resolveAs(inputDecorationTheme.prefixIconColor, materialState)
|
||||
?? iconButtonTheme.style?.foregroundColor?.resolve(materialState)
|
||||
?? MaterialStateProperty.resolveAs(defaults.prefixIconColor!, materialState);
|
||||
}
|
||||
|
||||
Color _getSuffixIconColor(ThemeData themeData, InputDecorationTheme defaults) {
|
||||
Color _getSuffixIconColor(
|
||||
InputDecorationTheme inputDecorationTheme,
|
||||
IconButtonThemeData iconButtonTheme,
|
||||
InputDecorationTheme defaults,
|
||||
) {
|
||||
return MaterialStateProperty.resolveAs(decoration.suffixIconColor, materialState)
|
||||
?? MaterialStateProperty.resolveAs(themeData.inputDecorationTheme.suffixIconColor, materialState)
|
||||
?? MaterialStateProperty.resolveAs(inputDecorationTheme.suffixIconColor, materialState)
|
||||
?? iconButtonTheme.style?.foregroundColor?.resolve(materialState)
|
||||
?? MaterialStateProperty.resolveAs(defaults.suffixIconColor!, materialState);
|
||||
}
|
||||
|
||||
@ -2189,6 +2198,8 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
|
||||
final ThemeData themeData = Theme.of(context);
|
||||
final InputDecorationTheme defaults =
|
||||
Theme.of(context).useMaterial3 ? _InputDecoratorDefaultsM3(context) : _InputDecoratorDefaultsM2(context);
|
||||
final InputDecorationTheme inputDecorationTheme = themeData.inputDecorationTheme;
|
||||
final IconButtonThemeData iconButtonTheme = IconButtonTheme.of(context);
|
||||
|
||||
final TextStyle labelStyle = _getInlineLabelStyle(themeData, defaults);
|
||||
final TextBaseline textBaseline = labelStyle.textBaseline!;
|
||||
@ -2320,15 +2331,15 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
|
||||
),
|
||||
child: IconTheme.merge(
|
||||
data: IconThemeData(
|
||||
color: _getPrefixIconColor(themeData, defaults),
|
||||
color: _getPrefixIconColor(inputDecorationTheme, iconButtonTheme, defaults),
|
||||
size: iconSize,
|
||||
),
|
||||
child: IconButtonTheme(
|
||||
data: IconButtonThemeData(
|
||||
style: IconButton.styleFrom(
|
||||
foregroundColor: _getPrefixIconColor(themeData, defaults),
|
||||
foregroundColor: _getPrefixIconColor(inputDecorationTheme, iconButtonTheme, defaults),
|
||||
iconSize: iconSize,
|
||||
),
|
||||
).merge(iconButtonTheme.style),
|
||||
),
|
||||
child: Semantics(
|
||||
child: decoration.prefixIcon,
|
||||
@ -2355,15 +2366,15 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
|
||||
),
|
||||
child: IconTheme.merge(
|
||||
data: IconThemeData(
|
||||
color: _getSuffixIconColor(themeData, defaults),
|
||||
color: _getSuffixIconColor(inputDecorationTheme, iconButtonTheme, defaults),
|
||||
size: iconSize,
|
||||
),
|
||||
child: IconButtonTheme(
|
||||
data: IconButtonThemeData(
|
||||
style: IconButton.styleFrom(
|
||||
foregroundColor: _getSuffixIconColor(themeData, defaults),
|
||||
foregroundColor: _getSuffixIconColor(inputDecorationTheme, iconButtonTheme, defaults),
|
||||
iconSize: iconSize,
|
||||
),
|
||||
).merge(iconButtonTheme.style),
|
||||
),
|
||||
child: Semantics(
|
||||
child: decoration.suffixIcon,
|
||||
|
@ -44,6 +44,7 @@ Widget buildInputDecorator({
|
||||
InputDecoration decoration = const InputDecoration(),
|
||||
ThemeData? theme,
|
||||
InputDecorationTheme? inputDecorationTheme,
|
||||
IconButtonThemeData? iconButtonTheme,
|
||||
TextDirection textDirection = TextDirection.ltr,
|
||||
bool expands = false,
|
||||
bool isEmpty = false,
|
||||
@ -81,6 +82,7 @@ Widget buildInputDecorator({
|
||||
return Theme(
|
||||
data: (theme ?? Theme.of(context)).copyWith(
|
||||
inputDecorationTheme: inputDecorationTheme,
|
||||
iconButtonTheme: iconButtonTheme,
|
||||
visualDensity: visualDensity,
|
||||
),
|
||||
child: Align(
|
||||
@ -4965,6 +4967,148 @@ void main() {
|
||||
expect(merged.constraints, overrideTheme.constraints);
|
||||
});
|
||||
|
||||
testWidgets('Prefix and suffix IconButtons inherit IconButtonTheme', (WidgetTester tester) async {
|
||||
const IconData prefixIcon = Icons.person;
|
||||
const IconData suffixIcon = Icons.search;
|
||||
const Color backgroundColor = Color(0xffff0000);
|
||||
const Color foregroundColor = Color(0xff00ff00);
|
||||
final OutlinedBorder shape =RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
);
|
||||
final ButtonStyle iconButtonStyle = IconButton.styleFrom(
|
||||
backgroundColor: backgroundColor,
|
||||
foregroundColor: foregroundColor,
|
||||
shape: shape,
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
IconButtonTheme(
|
||||
data: IconButtonThemeData(style: iconButtonStyle),
|
||||
child: buildInputDecorator(
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(prefixIcon),
|
||||
),
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(suffixIcon),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final Finder prefixIconMaterial = find.descendant(
|
||||
of: find.widgetWithIcon(IconButton, prefixIcon),
|
||||
matching: find.byType(Material),
|
||||
);
|
||||
Material material = tester.widget<Material>(prefixIconMaterial);
|
||||
expect(material.color, backgroundColor);
|
||||
expect(material.shape, iconButtonStyle.shape?.resolve(<WidgetState>{}));
|
||||
final Finder suffixIconMaterial = find.descendant(
|
||||
of: find.widgetWithIcon(IconButton, suffixIcon),
|
||||
matching: find.byType(Material),
|
||||
);
|
||||
material = tester.widget<Material>(suffixIconMaterial);
|
||||
expect(material.color, backgroundColor);
|
||||
expect(material.shape, shape);
|
||||
|
||||
expect(getIconStyle(tester, prefixIcon)?.color, foregroundColor);
|
||||
expect(getIconStyle(tester, suffixIcon)?.color, foregroundColor);
|
||||
});
|
||||
|
||||
testWidgets('Prefix IconButton color respects IconButtonTheme foreground color states', (WidgetTester tester) async {
|
||||
const IconData prefixIcon = Icons.person;
|
||||
const Color iconErrorColor = Color(0xffff0000);
|
||||
const Color iconColor = Color(0xff00ff00);
|
||||
final ButtonStyle iconButtonStyle = ButtonStyle(
|
||||
foregroundColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.error)) {
|
||||
return iconErrorColor;
|
||||
}
|
||||
return iconColor;
|
||||
}),
|
||||
);
|
||||
|
||||
// Test the prefix IconButton color when there is an error text.
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
iconButtonTheme: IconButtonThemeData(style: iconButtonStyle),
|
||||
decoration: InputDecoration(
|
||||
errorText: 'error',
|
||||
prefixIcon: IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(prefixIcon),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(getIconStyle(tester, prefixIcon)?.color, iconErrorColor);
|
||||
|
||||
// Test the prefix IconButton color when there is no error text.
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
iconButtonTheme: IconButtonThemeData(style: iconButtonStyle),
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(prefixIcon),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
|
||||
expect(getIconStyle(tester, prefixIcon)?.color, iconColor);
|
||||
});
|
||||
|
||||
testWidgets('Suffix IconButton color respects IconButtonTheme foreground color states', (WidgetTester tester) async {
|
||||
const IconData suffixIcon = Icons.search;
|
||||
const Color iconErrorColor = Color(0xffff0000);
|
||||
const Color iconColor = Color(0xff00ff00);
|
||||
final ButtonStyle iconButtonStyle = ButtonStyle(
|
||||
foregroundColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.error)) {
|
||||
return iconErrorColor;
|
||||
}
|
||||
return iconColor;
|
||||
}),
|
||||
);
|
||||
|
||||
// Test the prefix IconButton color when there is an error text.
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
iconButtonTheme: IconButtonThemeData(style: iconButtonStyle),
|
||||
decoration: InputDecoration(
|
||||
errorText: 'error',
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(suffixIcon),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(getIconStyle(tester, suffixIcon)?.color, iconErrorColor);
|
||||
|
||||
// Test the prefix IconButton color when there is no error text.
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
iconButtonTheme: IconButtonThemeData(style: iconButtonStyle),
|
||||
decoration: InputDecoration(
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(suffixIcon),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
|
||||
expect(getIconStyle(tester, suffixIcon)?.color, iconColor);
|
||||
});
|
||||
|
||||
group('Material2', () {
|
||||
// These tests are only relevant for Material 2. Once Material 2
|
||||
|
Loading…
x
Reference in New Issue
Block a user