Fix missing icon props in button styleFrom
methods (#154821)
Fixes [Add missing icon props in button `styleFrom` methods.](https://github.com/flutter/flutter/issues/154798) ### Description Add missing icon propers in the following widgets: - `ElevatedButton.styleFrom` (missing `iconSize`) - `FilledButton.styleFrom` (missing `iconSize`) - `OutlinedButton.styleFrom` (missing `iconSize`) - `TextButton.styleFrom` (missing `iconSize`) - `MenuItemButton.styleFrom` (missing `iconSize` and `disabledIconColor`) - `SubmenuButton.styleFrom` (missing `iconSize` and `disabledIconColor`) - `SegmentedButton.styleFrom` (missing `iconSize`, `iconColor`, and `disabledIconColor`) ### Code sample <details> <summary>expand to view the code sample</summary> ```dart import 'package:flutter/material.dart'; enum Calendar { day, week, month, year } void main() => runApp(const MyApp()); class MyApp extends StatefulWidget { const MyApp({super.key}); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { Calendar calendarView = Calendar.week; bool isEnabled = true; @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: Scaffold( body: Center( child: Column( spacing: 10.0, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ ElevatedButton.icon( style: ElevatedButton.styleFrom( iconSize: 30, iconColor: Colors.red, disabledIconColor: Colors.red.withValues(alpha: 0.5), ), onPressed: isEnabled ? () {} : null, icon: const Icon(Icons.add), label: const Text('ElevatedButton'), ), FilledButton.icon( style: ElevatedButton.styleFrom( iconSize: 30, iconColor: Colors.red, disabledIconColor: Colors.red.withValues(alpha: 0.5), ), onPressed: isEnabled ? () {} : null, icon: const Icon(Icons.add), label: const Text('FilledButton'), ), FilledButton.tonalIcon( style: ElevatedButton.styleFrom( iconSize: 30, iconColor: Colors.red, disabledIconColor: Colors.red.withValues(alpha: 0.5), ), onPressed: isEnabled ? () {} : null, icon: const Icon(Icons.add), label: const Text('Add'), ), OutlinedButton.icon( style: ElevatedButton.styleFrom( iconSize: 30, iconColor: Colors.red, disabledIconColor: Colors.red.withValues(alpha: 0.5), ), onPressed: isEnabled ? () {} : null, icon: const Icon(Icons.add), label: const Text('OutlinedButton'), ), TextButton.icon( style: ElevatedButton.styleFrom( iconSize: 30, iconColor: Colors.red, disabledIconColor: Colors.red.withValues(alpha: 0.5), ), onPressed: isEnabled ? () {} : null, icon: const Icon(Icons.add), label: const Text('TextButton'), ), SizedBox( width: 200, child: MenuItemButton( style: MenuItemButton.styleFrom( iconSize: 30, iconColor: Colors.red, disabledIconColor: Colors.red.withValues(alpha: 0.5), ), trailingIcon: const Icon(Icons.arrow_forward_ios), onPressed: isEnabled ? () {} : null, child: const Text('MenuItemButton'), ), ), SizedBox( width: 200, child: SubmenuButton( style: SubmenuButton.styleFrom( iconSize: 30, iconColor: Colors.red, disabledIconColor: Colors.red.withValues(alpha: 0.5), ), trailingIcon: const Icon(Icons.arrow_forward_ios), menuChildren: <Widget>[ if (isEnabled) const Text('Item'), ], child: const Text('SubmenuButton'), ), ), SegmentedButton<Calendar>( style: SegmentedButton.styleFrom( iconColor: Colors.red, iconSize: 30, disabledIconColor: Colors.red.withValues(alpha: 0.5), ), segments: const <ButtonSegment<Calendar>>[ ButtonSegment<Calendar>( value: Calendar.day, label: Text('Day'), icon: Icon(Icons.calendar_view_day)), ButtonSegment<Calendar>( value: Calendar.week, label: Text('Week'), icon: Icon(Icons.calendar_view_week)), ButtonSegment<Calendar>( value: Calendar.month, label: Text('Month'), icon: Icon(Icons.calendar_view_month)), ButtonSegment<Calendar>( value: Calendar.year, label: Text('Year'), icon: Icon(Icons.calendar_today)), ], selected: <Calendar>{calendarView}, onSelectionChanged: isEnabled ? (Set<Calendar> newSelection) {} : null, ) ], ), ), floatingActionButton: FloatingActionButton.extended( onPressed: () { setState(() { isEnabled = !isEnabled; }); }, label: Text(isEnabled ? 'Enabled' : 'Disabled'), ), ), ); } } ``` </details> ### Preview (Customized using icon props in `styleFrom` methods) <img width="838" alt="Screenshot 2024-09-09 at 16 27 19" src="https://github.com/user-attachments/assets/551d328b-307f-4f63-b0e8-1358a12877f9">
This commit is contained in:
parent
d0a9e3be94
commit
d5e843eca3
@ -156,9 +156,11 @@ class ElevatedButton extends ButtonStyleButton {
|
||||
/// used to create a [WidgetStateProperty] [ButtonStyle.backgroundColor].
|
||||
///
|
||||
/// Similarly, the [enabledMouseCursor] and [disabledMouseCursor]
|
||||
/// parameters are used to construct [ButtonStyle.mouseCursor] and
|
||||
/// [iconColor], [disabledIconColor] are used to construct
|
||||
/// [ButtonStyle.iconColor].
|
||||
/// parameters are used to construct [ButtonStyle.mouseCursor].
|
||||
///
|
||||
/// The [iconColor], [disabledIconColor] are used to construct
|
||||
/// [ButtonStyle.iconColor] and [iconSize] is used to construct
|
||||
/// [ButtonStyle.iconSize].
|
||||
///
|
||||
/// The button's elevations are defined relative to the [elevation]
|
||||
/// parameter. The disabled elevation is the same as the parameter
|
||||
@ -207,6 +209,7 @@ class ElevatedButton extends ButtonStyleButton {
|
||||
Color? shadowColor,
|
||||
Color? surfaceTintColor,
|
||||
Color? iconColor,
|
||||
double? iconSize,
|
||||
Color? disabledIconColor,
|
||||
Color? overlayColor,
|
||||
double? elevation,
|
||||
@ -261,6 +264,7 @@ class ElevatedButton extends ButtonStyleButton {
|
||||
shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
|
||||
surfaceTintColor: ButtonStyleButton.allOrNull<Color>(surfaceTintColor),
|
||||
iconColor: ButtonStyleButton.defaultColor(iconColor, disabledIconColor),
|
||||
iconSize: ButtonStyleButton.allOrNull<double>(iconSize),
|
||||
elevation: elevationValue,
|
||||
padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
|
||||
minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
|
||||
|
@ -229,6 +229,10 @@ class FilledButton extends ButtonStyleButton {
|
||||
/// Similarly, the [enabledMouseCursor] and [disabledMouseCursor]
|
||||
/// parameters are used to construct [ButtonStyle.mouseCursor].
|
||||
///
|
||||
/// The [iconColor], [disabledIconColor] are used to construct
|
||||
/// [ButtonStyle.iconColor] and [iconSize] is used to construct
|
||||
/// [ButtonStyle.iconSize].
|
||||
///
|
||||
/// The button's elevations are defined relative to the [elevation]
|
||||
/// parameter. The disabled elevation is the same as the parameter
|
||||
/// value, [elevation] + 2 is used when the button is hovered
|
||||
@ -270,6 +274,7 @@ class FilledButton extends ButtonStyleButton {
|
||||
Color? shadowColor,
|
||||
Color? surfaceTintColor,
|
||||
Color? iconColor,
|
||||
double? iconSize,
|
||||
Color? disabledIconColor,
|
||||
Color? overlayColor,
|
||||
double? elevation,
|
||||
@ -311,6 +316,7 @@ class FilledButton extends ButtonStyleButton {
|
||||
shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
|
||||
surfaceTintColor: ButtonStyleButton.allOrNull<Color>(surfaceTintColor),
|
||||
iconColor: ButtonStyleButton.defaultColor(iconColor, disabledIconColor),
|
||||
iconSize: ButtonStyleButton.allOrNull<double>(iconSize),
|
||||
elevation: ButtonStyleButton.allOrNull(elevation),
|
||||
padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
|
||||
minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
|
||||
|
@ -996,6 +996,13 @@ class MenuItemButton extends StatefulWidget {
|
||||
/// [disabledBackgroundColor] to specify the button's disabled icon and fill
|
||||
/// color.
|
||||
///
|
||||
/// Similarly, the [enabledMouseCursor] and [disabledMouseCursor]
|
||||
/// parameters are used to construct [ButtonStyle.mouseCursor].
|
||||
///
|
||||
/// The [iconColor], [disabledIconColor] are used to construct
|
||||
/// [ButtonStyle.iconColor] and [iconSize] is used to construct
|
||||
/// [ButtonStyle.iconSize].
|
||||
///
|
||||
/// All of the other parameters are either used directly or used to create a
|
||||
/// [WidgetStateProperty] with a single value for all states.
|
||||
///
|
||||
@ -1025,6 +1032,8 @@ class MenuItemButton extends StatefulWidget {
|
||||
Color? shadowColor,
|
||||
Color? surfaceTintColor,
|
||||
Color? iconColor,
|
||||
double? iconSize,
|
||||
Color? disabledIconColor,
|
||||
TextStyle? textStyle,
|
||||
Color? overlayColor,
|
||||
double? elevation,
|
||||
@ -1051,6 +1060,8 @@ class MenuItemButton extends StatefulWidget {
|
||||
shadowColor: shadowColor,
|
||||
surfaceTintColor: surfaceTintColor,
|
||||
iconColor: iconColor,
|
||||
iconSize: iconSize,
|
||||
disabledIconColor: disabledIconColor,
|
||||
textStyle: textStyle,
|
||||
overlayColor: overlayColor,
|
||||
elevation: elevation,
|
||||
@ -1777,6 +1788,13 @@ class SubmenuButton extends StatefulWidget {
|
||||
/// [disabledBackgroundColor] to specify the button's disabled icon and fill
|
||||
/// color.
|
||||
///
|
||||
/// Similarly, the [enabledMouseCursor] and [disabledMouseCursor]
|
||||
/// parameters are used to construct [ButtonStyle.mouseCursor].
|
||||
///
|
||||
/// The [iconColor], [disabledIconColor] are used to construct
|
||||
/// [ButtonStyle.iconColor] and [iconSize] is used to construct
|
||||
/// [ButtonStyle.iconSize].
|
||||
///
|
||||
/// All of the other parameters are either used directly or used to create a
|
||||
/// [WidgetStateProperty] with a single value for all states.
|
||||
///
|
||||
@ -1804,6 +1822,8 @@ class SubmenuButton extends StatefulWidget {
|
||||
Color? shadowColor,
|
||||
Color? surfaceTintColor,
|
||||
Color? iconColor,
|
||||
double? iconSize,
|
||||
Color? disabledIconColor,
|
||||
TextStyle? textStyle,
|
||||
Color? overlayColor,
|
||||
double? elevation,
|
||||
@ -1830,6 +1850,8 @@ class SubmenuButton extends StatefulWidget {
|
||||
shadowColor: shadowColor,
|
||||
surfaceTintColor: surfaceTintColor,
|
||||
iconColor: iconColor,
|
||||
disabledIconColor: disabledIconColor,
|
||||
iconSize: iconSize,
|
||||
textStyle: textStyle,
|
||||
overlayColor: overlayColor,
|
||||
elevation: elevation,
|
||||
|
@ -155,9 +155,11 @@ class OutlinedButton extends ButtonStyleButton {
|
||||
/// used to create a [WidgetStateProperty] [ButtonStyle.backgroundColor].
|
||||
///
|
||||
/// Similarly, the [enabledMouseCursor] and [disabledMouseCursor]
|
||||
/// parameters are used to construct [ButtonStyle.mouseCursor] and
|
||||
/// [iconColor], [disabledIconColor] are used to construct
|
||||
/// [ButtonStyle.iconColor].
|
||||
/// parameters are used to construct [ButtonStyle.mouseCursor].
|
||||
///
|
||||
/// The [iconColor], [disabledIconColor] are used to construct
|
||||
/// [ButtonStyle.iconColor] and [iconSize] is used to construct
|
||||
/// [ButtonStyle.iconSize].
|
||||
///
|
||||
/// If [overlayColor] is specified and its value is [Colors.transparent]
|
||||
/// then the pressed/focused/hovered highlights are effectively defeated.
|
||||
@ -194,6 +196,7 @@ class OutlinedButton extends ButtonStyleButton {
|
||||
Color? shadowColor,
|
||||
Color? surfaceTintColor,
|
||||
Color? iconColor,
|
||||
double? iconSize,
|
||||
Color? disabledIconColor,
|
||||
Color? overlayColor,
|
||||
double? elevation,
|
||||
@ -239,6 +242,7 @@ class OutlinedButton extends ButtonStyleButton {
|
||||
shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
|
||||
surfaceTintColor: ButtonStyleButton.allOrNull<Color>(surfaceTintColor),
|
||||
iconColor: ButtonStyleButton.defaultColor(iconColor, disabledIconColor),
|
||||
iconSize: ButtonStyleButton.allOrNull<double>(iconSize),
|
||||
elevation: ButtonStyleButton.allOrNull<double>(elevation),
|
||||
padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
|
||||
minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
|
||||
|
@ -232,6 +232,10 @@ class SegmentedButton<T> extends StatefulWidget {
|
||||
/// Similarly, the [enabledMouseCursor] and [disabledMouseCursor]
|
||||
/// parameters are used to construct [ButtonStyle.mouseCursor].
|
||||
///
|
||||
/// The [iconColor], [disabledIconColor] are used to construct
|
||||
/// [ButtonStyle.iconColor] and [iconSize] is used to construct
|
||||
/// [ButtonStyle.iconSize].
|
||||
///
|
||||
/// All of the other parameters are either used directly or used to
|
||||
/// create a [WidgetStateProperty] with a single value for all
|
||||
/// states.
|
||||
@ -282,6 +286,9 @@ class SegmentedButton<T> extends StatefulWidget {
|
||||
Color? disabledBackgroundColor,
|
||||
Color? shadowColor,
|
||||
Color? surfaceTintColor,
|
||||
Color? iconColor,
|
||||
double? iconSize,
|
||||
Color? disabledIconColor,
|
||||
Color? overlayColor,
|
||||
double? elevation,
|
||||
TextStyle? textStyle,
|
||||
@ -311,6 +318,9 @@ class SegmentedButton<T> extends StatefulWidget {
|
||||
textStyle: textStyle,
|
||||
shadowColor: shadowColor,
|
||||
surfaceTintColor: surfaceTintColor,
|
||||
iconColor: iconColor,
|
||||
iconSize: iconSize,
|
||||
disabledIconColor: disabledIconColor,
|
||||
elevation: elevation,
|
||||
padding: padding,
|
||||
minimumSize: minimumSize,
|
||||
|
@ -163,9 +163,11 @@ class TextButton extends ButtonStyleButton {
|
||||
/// used to create a [WidgetStateProperty] [ButtonStyle.backgroundColor].
|
||||
///
|
||||
/// Similarly, the [enabledMouseCursor] and [disabledMouseCursor]
|
||||
/// parameters are used to construct [ButtonStyle.mouseCursor] and
|
||||
/// [iconColor], [disabledIconColor] are used to construct
|
||||
/// [ButtonStyle.iconColor].
|
||||
/// parameters are used to construct [ButtonStyle.mouseCursor].
|
||||
///
|
||||
/// The [iconColor], [disabledIconColor] are used to construct
|
||||
/// [ButtonStyle.iconColor] and [iconSize] is used to construct
|
||||
/// [ButtonStyle.iconSize].
|
||||
///
|
||||
/// If [overlayColor] is specified and its value is [Colors.transparent]
|
||||
/// then the pressed/focused/hovered highlights are effectively defeated.
|
||||
@ -201,6 +203,7 @@ class TextButton extends ButtonStyleButton {
|
||||
Color? shadowColor,
|
||||
Color? surfaceTintColor,
|
||||
Color? iconColor,
|
||||
double? iconSize,
|
||||
Color? disabledIconColor,
|
||||
Color? overlayColor,
|
||||
double? elevation,
|
||||
@ -250,6 +253,7 @@ class TextButton extends ButtonStyleButton {
|
||||
shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
|
||||
surfaceTintColor: ButtonStyleButton.allOrNull<Color>(surfaceTintColor),
|
||||
iconColor: iconColorProp,
|
||||
iconSize: ButtonStyleButton.allOrNull<double>(iconSize),
|
||||
elevation: ButtonStyleButton.allOrNull<double>(elevation),
|
||||
padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
|
||||
minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
|
||||
|
@ -10,6 +10,13 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import '../widgets/semantics_tester.dart';
|
||||
|
||||
void main() {
|
||||
TextStyle iconStyle(WidgetTester tester, IconData icon) {
|
||||
final RichText iconRichText = tester.widget<RichText>(
|
||||
find.descendant(of: find.byIcon(icon), matching: find.byType(RichText)),
|
||||
);
|
||||
return iconRichText.text.style!;
|
||||
}
|
||||
|
||||
testWidgets('ElevatedButton, ElevatedButton.icon defaults', (WidgetTester tester) async {
|
||||
const ColorScheme colorScheme = ColorScheme.light();
|
||||
final ThemeData theme = ThemeData.from(colorScheme: colorScheme);
|
||||
@ -537,14 +544,13 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
Color iconColor() => _iconStyle(tester, Icons.add).color!;
|
||||
// Default, not disabled.
|
||||
expect(iconColor(), equals(defaultColor));
|
||||
expect(iconStyle(tester, Icons.add).color, equals(defaultColor));
|
||||
|
||||
// Focused.
|
||||
focusNode.requestFocus();
|
||||
await tester.pumpAndSettle();
|
||||
expect(iconColor(), focusedColor);
|
||||
expect(iconStyle(tester, Icons.add).color, focusedColor);
|
||||
|
||||
// Hovered.
|
||||
final Offset center = tester.getCenter(find.byKey(buttonKey));
|
||||
@ -554,13 +560,13 @@ void main() {
|
||||
await gesture.addPointer();
|
||||
await gesture.moveTo(center);
|
||||
await tester.pumpAndSettle();
|
||||
expect(iconColor(), hoverColor);
|
||||
expect(iconStyle(tester, Icons.add).color, hoverColor);
|
||||
|
||||
// Highlighted (pressed).
|
||||
await gesture.down(center);
|
||||
await tester.pump(); // Start the splash and highlight animations.
|
||||
await tester.pump(const Duration(milliseconds: 800)); // Wait for splash and highlight to be well under way.
|
||||
expect(iconColor(), pressedColor);
|
||||
expect(iconStyle(tester, Icons.add).color, pressedColor);
|
||||
|
||||
focusNode.dispose();
|
||||
});
|
||||
@ -2400,11 +2406,36 @@ void main() {
|
||||
// The icon is aligned to the left of the button.
|
||||
expect(buttonTopLeft.dx, iconTopLeft.dx - 24.0); // 24.0 - padding between icon and button edge.
|
||||
});
|
||||
}
|
||||
|
||||
TextStyle _iconStyle(WidgetTester tester, IconData icon) {
|
||||
final RichText iconRichText = tester.widget<RichText>(
|
||||
find.descendant(of: find.byIcon(icon), matching: find.byType(RichText)),
|
||||
);
|
||||
return iconRichText.text.style!;
|
||||
// Regression test for https://github.com/flutter/flutter/issues/154798.
|
||||
testWidgets('ElevatedButton.styleFrom can customize the button icon', (WidgetTester tester) async {
|
||||
const Color iconColor = Color(0xFFF000FF);
|
||||
const double iconSize = 32.0;
|
||||
const Color disabledIconColor = Color(0xFFFFF000);
|
||||
Widget buildButton({ bool enabled = true }) {
|
||||
return MaterialApp(
|
||||
home: Material(
|
||||
child: ElevatedButton.icon(
|
||||
style: ElevatedButton.styleFrom(
|
||||
iconColor: iconColor,
|
||||
iconSize: iconSize,
|
||||
disabledIconColor: disabledIconColor,
|
||||
),
|
||||
onPressed: enabled ? () {} : null,
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('Button'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Test enabled button.
|
||||
await tester.pumpWidget(buildButton());
|
||||
expect(tester.getSize(find.byIcon(Icons.add)), const Size(iconSize, iconSize));
|
||||
expect(iconStyle(tester, Icons.add).color, iconColor);
|
||||
|
||||
// Test disabled button.
|
||||
await tester.pumpWidget(buildButton(enabled: false));
|
||||
expect(iconStyle(tester, Icons.add).color, disabledIconColor);
|
||||
});
|
||||
}
|
||||
|
@ -10,6 +10,13 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import '../widgets/semantics_tester.dart';
|
||||
|
||||
void main() {
|
||||
TextStyle iconStyle(WidgetTester tester, IconData icon) {
|
||||
final RichText iconRichText = tester.widget<RichText>(
|
||||
find.descendant(of: find.byIcon(icon), matching: find.byType(RichText)),
|
||||
);
|
||||
return iconRichText.text.style!;
|
||||
}
|
||||
|
||||
testWidgets('FilledButton, FilledButton.icon defaults', (WidgetTester tester) async {
|
||||
const ColorScheme colorScheme = ColorScheme.light();
|
||||
final ThemeData theme = ThemeData.from(useMaterial3: false, colorScheme: colorScheme);
|
||||
@ -729,14 +736,13 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
Color iconColor() => _iconStyle(tester, Icons.add).color!;
|
||||
// Default, not disabled.
|
||||
expect(iconColor(), equals(defaultColor));
|
||||
expect(iconStyle(tester, Icons.add).color, equals(defaultColor));
|
||||
|
||||
// Focused.
|
||||
focusNode.requestFocus();
|
||||
await tester.pumpAndSettle();
|
||||
expect(iconColor(), focusedColor);
|
||||
expect(iconStyle(tester, Icons.add).color, focusedColor);
|
||||
|
||||
// Hovered.
|
||||
final Offset center = tester.getCenter(find.byKey(buttonKey));
|
||||
@ -746,13 +752,13 @@ void main() {
|
||||
await gesture.addPointer();
|
||||
await gesture.moveTo(center);
|
||||
await tester.pumpAndSettle();
|
||||
expect(iconColor(), hoverColor);
|
||||
expect(iconStyle(tester, Icons.add).color, hoverColor);
|
||||
|
||||
// Highlighted (pressed).
|
||||
await gesture.down(center);
|
||||
await tester.pump(); // Start the splash and highlight animations.
|
||||
await tester.pump(const Duration(milliseconds: 800)); // Wait for splash and highlight to be well under way.
|
||||
expect(iconColor(), pressedColor);
|
||||
expect(iconStyle(tester, Icons.add).color, pressedColor);
|
||||
focusNode.dispose();
|
||||
});
|
||||
|
||||
@ -2625,11 +2631,38 @@ void main() {
|
||||
// The icon is aligned to the left of the button.
|
||||
expect(buttonTopLeft.dx, iconTopLeft.dx - 24.0); // 24.0 - padding between icon and button edge.
|
||||
});
|
||||
}
|
||||
|
||||
TextStyle _iconStyle(WidgetTester tester, IconData icon) {
|
||||
final RichText iconRichText = tester.widget<RichText>(
|
||||
find.descendant(of: find.byIcon(icon), matching: find.byType(RichText)),
|
||||
);
|
||||
return iconRichText.text.style!;
|
||||
// Regression test for https://github.com/flutter/flutter/issues/154798.
|
||||
testWidgets('FilledButton.styleFrom can customize the button icon', (WidgetTester tester) async {
|
||||
const Color iconColor = Color(0xFFF000FF);
|
||||
const double iconSize = 32.0;
|
||||
const Color disabledIconColor = Color(0xFFFFF000);
|
||||
Widget buildButton({ bool enabled = true }) {
|
||||
return MaterialApp(
|
||||
home: Material(
|
||||
child: Center(
|
||||
child: FilledButton.icon(
|
||||
style: FilledButton.styleFrom(
|
||||
iconColor: iconColor,
|
||||
iconSize: iconSize,
|
||||
disabledIconColor: disabledIconColor,
|
||||
),
|
||||
onPressed: enabled ? () {} : null,
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('Button'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Test enabled button.
|
||||
await tester.pumpWidget(buildButton());
|
||||
expect(tester.getSize(find.byIcon(Icons.add)), const Size(iconSize, iconSize));
|
||||
expect(iconStyle(tester, Icons.add).color, iconColor);
|
||||
|
||||
// Test disabled button.
|
||||
await tester.pumpWidget(buildButton(enabled: false));
|
||||
expect(iconStyle(tester, Icons.add).color, disabledIconColor);
|
||||
});
|
||||
}
|
||||
|
@ -172,6 +172,13 @@ void main() {
|
||||
return tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
|
||||
}
|
||||
|
||||
TextStyle iconStyle(WidgetTester tester, IconData icon) {
|
||||
final RichText iconRichText = tester.widget<RichText>(
|
||||
find.descendant(of: find.byIcon(icon), matching: find.byType(RichText)),
|
||||
);
|
||||
return iconRichText.text.style!;
|
||||
}
|
||||
|
||||
testWidgets('Menu responds to density changes', (WidgetTester tester) async {
|
||||
Widget buildMenu({VisualDensity? visualDensity = VisualDensity.standard}) {
|
||||
return MaterialApp(
|
||||
@ -4508,6 +4515,77 @@ void main() {
|
||||
expect(state.target, isNull);
|
||||
}, skip: kIsWeb // [intended] ForceGC does not work in web and in release mode. See https://api.flutter.dev/flutter/package-leak_tracker_leak_tracker/forceGC.html
|
||||
);
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/154798.
|
||||
testWidgets('MenuItemButton.styleFrom can customize the button icon', (WidgetTester tester) async {
|
||||
const Color iconColor = Color(0xFFF000FF);
|
||||
const double iconSize = 32.0;
|
||||
const Color disabledIconColor = Color(0xFFFFF000);
|
||||
Widget buildButton({ bool enabled = true }) {
|
||||
return MaterialApp(
|
||||
home: Material(
|
||||
child: Center(
|
||||
child: MenuItemButton(
|
||||
style: MenuItemButton.styleFrom(
|
||||
iconColor: iconColor,
|
||||
iconSize: iconSize,
|
||||
disabledIconColor: disabledIconColor,
|
||||
),
|
||||
onPressed: enabled ? () {} : null,
|
||||
trailingIcon: const Icon(Icons.add),
|
||||
child: const Text('Button'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Test enabled button.
|
||||
await tester.pumpWidget(buildButton());
|
||||
expect(tester.getSize(find.byIcon(Icons.add)), const Size(iconSize, iconSize));
|
||||
expect(iconStyle(tester, Icons.add).color, iconColor);
|
||||
|
||||
// Test disabled button.
|
||||
await tester.pumpWidget(buildButton(enabled: false));
|
||||
expect(iconStyle(tester, Icons.add).color, disabledIconColor);
|
||||
});
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/154798.
|
||||
testWidgets('SubmenuButton.styleFrom can customize the button icon', (WidgetTester tester) async {
|
||||
const Color iconColor = Color(0xFFF000FF);
|
||||
const double iconSize = 32.0;
|
||||
const Color disabledIconColor = Color(0xFFFFF000);
|
||||
Widget buildButton({ bool enabled = true }) {
|
||||
return MaterialApp(
|
||||
home: Material(
|
||||
child: Center(
|
||||
child: SubmenuButton(
|
||||
style: SubmenuButton.styleFrom(
|
||||
iconColor: iconColor,
|
||||
iconSize: iconSize,
|
||||
disabledIconColor: disabledIconColor,
|
||||
),
|
||||
trailingIcon: const Icon(Icons.add),
|
||||
menuChildren: <Widget>[
|
||||
if (enabled)
|
||||
const Text('Item'),
|
||||
],
|
||||
child: const Text('SubmenuButton'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Test enabled button.
|
||||
await tester.pumpWidget(buildButton());
|
||||
expect(tester.getSize(find.byIcon(Icons.add)), const Size(iconSize, iconSize));
|
||||
expect(iconStyle(tester, Icons.add).color, iconColor);
|
||||
|
||||
// Test disabled button.
|
||||
await tester.pumpWidget(buildButton(enabled: false));
|
||||
expect(iconStyle(tester, Icons.add).color, disabledIconColor);
|
||||
});
|
||||
}
|
||||
|
||||
List<Widget> createTestMenus({
|
||||
|
@ -10,6 +10,13 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import '../widgets/semantics_tester.dart';
|
||||
|
||||
void main() {
|
||||
TextStyle iconStyle(WidgetTester tester, IconData icon) {
|
||||
final RichText iconRichText = tester.widget<RichText>(
|
||||
find.descendant(of: find.byIcon(icon), matching: find.byType(RichText)),
|
||||
);
|
||||
return iconRichText.text.style!;
|
||||
}
|
||||
|
||||
testWidgets('OutlinedButton, OutlinedButton.icon defaults', (WidgetTester tester) async {
|
||||
const ColorScheme colorScheme = ColorScheme.light();
|
||||
final ThemeData theme = ThemeData.from(colorScheme: colorScheme);
|
||||
@ -720,14 +727,13 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
Color iconColor() => _iconStyle(tester, Icons.add).color!;
|
||||
// Default, not disabled.
|
||||
expect(iconColor(), equals(defaultColor));
|
||||
expect(iconStyle(tester, Icons.add).color, equals(defaultColor));
|
||||
|
||||
// Focused.
|
||||
focusNode.requestFocus();
|
||||
await tester.pumpAndSettle();
|
||||
expect(iconColor(), focusedColor);
|
||||
expect(iconStyle(tester, Icons.add).color, focusedColor);
|
||||
|
||||
// Hovered.
|
||||
final Offset center = tester.getCenter(find.byKey(buttonKey));
|
||||
@ -737,13 +743,13 @@ void main() {
|
||||
await gesture.addPointer();
|
||||
await gesture.moveTo(center);
|
||||
await tester.pumpAndSettle();
|
||||
expect(iconColor(), hoverColor);
|
||||
expect(iconStyle(tester, Icons.add).color, hoverColor);
|
||||
|
||||
// Highlighted (pressed).
|
||||
await gesture.down(center);
|
||||
await tester.pump(); // Start the splash and highlight animations.
|
||||
await tester.pump(const Duration(milliseconds: 800)); // Wait for splash and highlight to be well under way.
|
||||
expect(iconColor(), pressedColor);
|
||||
expect(iconStyle(tester, Icons.add).color, pressedColor);
|
||||
|
||||
focusNode.dispose();
|
||||
});
|
||||
@ -2630,7 +2636,7 @@ void main() {
|
||||
focusNode.dispose();
|
||||
});
|
||||
|
||||
testWidgets('disabled and hovered OutlinedButton.icon responds to mouse-exit', (WidgetTester tester) async {
|
||||
testWidgets('Disabled and hovered OutlinedButton.icon responds to mouse-exit', (WidgetTester tester) async {
|
||||
int onHoverCount = 0;
|
||||
late bool hover;
|
||||
const Key key = Key('OutlinedButton.icon');
|
||||
@ -2694,7 +2700,7 @@ void main() {
|
||||
expect(hover, false);
|
||||
});
|
||||
|
||||
testWidgets('Can set OutlinedButton.icon focus and Can set unFocus.', (WidgetTester tester) async {
|
||||
testWidgets('OutlinedButton.icon can be focused/unfocused', (WidgetTester tester) async {
|
||||
final FocusNode node = FocusNode(debugLabel: 'OutlinedButton.icon Focus');
|
||||
bool gotFocus = false;
|
||||
await tester.pumpWidget(
|
||||
@ -2721,7 +2727,7 @@ void main() {
|
||||
node.dispose();
|
||||
});
|
||||
|
||||
testWidgets('When OutlinedButton.icon disable, Can not set OutlinedButton.icon focus.', (WidgetTester tester) async {
|
||||
testWidgets('Disabled OutlinedButton.icon cannot receive focus', (WidgetTester tester) async {
|
||||
final FocusNode node = FocusNode(debugLabel: 'OutlinedButton.icon Focus');
|
||||
bool gotFocus = false;
|
||||
await tester.pumpWidget(
|
||||
@ -2743,11 +2749,38 @@ void main() {
|
||||
expect(node.hasFocus, isFalse);
|
||||
node.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
TextStyle _iconStyle(WidgetTester tester, IconData icon) {
|
||||
final RichText iconRichText = tester.widget<RichText>(
|
||||
find.descendant(of: find.byIcon(icon), matching: find.byType(RichText)),
|
||||
);
|
||||
return iconRichText.text.style!;
|
||||
// Regression test for https://github.com/flutter/flutter/issues/154798.
|
||||
testWidgets('OutlinedButton.styleFrom can customize the button icon', (WidgetTester tester) async {
|
||||
const Color iconColor = Color(0xFFF000FF);
|
||||
const double iconSize = 32.0;
|
||||
const Color disabledIconColor = Color(0xFFFFF000);
|
||||
Widget buildButton({ bool enabled = true }) {
|
||||
return MaterialApp(
|
||||
home: Material(
|
||||
child: Center(
|
||||
child: OutlinedButton.icon(
|
||||
style: OutlinedButton.styleFrom(
|
||||
iconColor: iconColor,
|
||||
iconSize: iconSize,
|
||||
disabledIconColor: disabledIconColor,
|
||||
),
|
||||
onPressed: enabled ? () {} : null,
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('Button'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Test enabled button.
|
||||
await tester.pumpWidget(buildButton());
|
||||
expect(tester.getSize(find.byIcon(Icons.add)), const Size(iconSize, iconSize));
|
||||
expect(iconStyle(tester, Icons.add).color, iconColor);
|
||||
|
||||
// Test disabled button.
|
||||
await tester.pumpWidget(buildButton(enabled: false));
|
||||
expect(iconStyle(tester, Icons.add).color, disabledIconColor);
|
||||
});
|
||||
}
|
||||
|
@ -16,18 +16,25 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../widgets/semantics_tester.dart';
|
||||
|
||||
Widget boilerplate({required Widget child}) {
|
||||
return Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Center(child: child),
|
||||
);
|
||||
}
|
||||
|
||||
void main() {
|
||||
RenderObject getOverlayColor(WidgetTester tester) {
|
||||
return tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
|
||||
}
|
||||
|
||||
Widget boilerplate({required Widget child}) {
|
||||
return Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Center(child: child),
|
||||
);
|
||||
}
|
||||
|
||||
TextStyle iconStyle(WidgetTester tester, IconData icon) {
|
||||
final RichText iconRichText = tester.widget<RichText>(
|
||||
find.descendant(of: find.byIcon(icon), matching: find.byType(RichText)),
|
||||
);
|
||||
return iconRichText.text.style!;
|
||||
}
|
||||
|
||||
testWidgets('SegmentsButton when compositing does not crash', (WidgetTester tester) async {
|
||||
// Regression test for https://github.com/flutter/flutter/issues/135747
|
||||
// If the render object holds on to a stale canvas reference, this will
|
||||
@ -1204,6 +1211,52 @@ void main() {
|
||||
matchesGoldenFile('segmented_button_test_vertical.png'),
|
||||
);
|
||||
});
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/154798.
|
||||
testWidgets('SegmentedButton.styleFrom can customize the button icon', (WidgetTester tester) async {
|
||||
const Color iconColor = Color(0xFFF000FF);
|
||||
const double iconSize = 32.0;
|
||||
const Color disabledIconColor = Color(0xFFFFF000);
|
||||
Widget buildButton({ bool enabled = true }) {
|
||||
return MaterialApp(
|
||||
home: Material(
|
||||
child: Center(
|
||||
child: SegmentedButton<int>(
|
||||
style: SegmentedButton.styleFrom(
|
||||
iconColor: iconColor,
|
||||
iconSize: iconSize,
|
||||
disabledIconColor: disabledIconColor,
|
||||
),
|
||||
segments: const <ButtonSegment<int>>[
|
||||
ButtonSegment<int>(
|
||||
value: 0,
|
||||
label: Text('Add'),
|
||||
icon: Icon(Icons.add),
|
||||
),
|
||||
ButtonSegment<int>(
|
||||
value: 1,
|
||||
label: Text('Subtract'),
|
||||
icon: Icon(Icons.remove),
|
||||
),
|
||||
],
|
||||
showSelectedIcon: false,
|
||||
onSelectionChanged: enabled ? (Set<int> selected) {} : null,
|
||||
selected: const <int>{0},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Test enabled button.
|
||||
await tester.pumpWidget(buildButton());
|
||||
expect(tester.getSize(find.byIcon(Icons.add)), const Size(iconSize, iconSize));
|
||||
expect(iconStyle(tester, Icons.add).color, iconColor);
|
||||
|
||||
// Test disabled button.
|
||||
await tester.pumpWidget(buildButton(enabled: false));
|
||||
expect(iconStyle(tester, Icons.add).color, disabledIconColor);
|
||||
});
|
||||
}
|
||||
|
||||
Set<MaterialState> enabled = const <MaterialState>{};
|
||||
|
@ -10,6 +10,13 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import '../widgets/semantics_tester.dart';
|
||||
|
||||
void main() {
|
||||
TextStyle iconStyle(WidgetTester tester, IconData icon) {
|
||||
final RichText iconRichText = tester.widget<RichText>(
|
||||
find.descendant(of: find.byIcon(icon), matching: find.byType(RichText)),
|
||||
);
|
||||
return iconRichText.text.style!;
|
||||
}
|
||||
|
||||
testWidgets('TextButton, TextButton.icon defaults', (WidgetTester tester) async {
|
||||
const ColorScheme colorScheme = ColorScheme.light();
|
||||
final ThemeData theme = ThemeData.from(colorScheme: colorScheme);
|
||||
@ -584,14 +591,13 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
Color? iconColor() => _iconStyle(tester, Icons.add)?.color;
|
||||
// Default, not disabled.
|
||||
expect(iconColor(), equals(defaultColor));
|
||||
expect(iconStyle(tester, Icons.add).color, equals(defaultColor));
|
||||
|
||||
// Focused.
|
||||
focusNode.requestFocus();
|
||||
await tester.pumpAndSettle();
|
||||
expect(iconColor(), focusedColor);
|
||||
expect(iconStyle(tester, Icons.add).color, focusedColor);
|
||||
|
||||
// Hovered.
|
||||
final Offset center = tester.getCenter(find.byKey(buttonKey));
|
||||
@ -601,13 +607,13 @@ void main() {
|
||||
await gesture.addPointer();
|
||||
await gesture.moveTo(center);
|
||||
await tester.pumpAndSettle();
|
||||
expect(iconColor(), hoverColor);
|
||||
expect(iconStyle(tester, Icons.add).color, hoverColor);
|
||||
|
||||
// Highlighted (pressed).
|
||||
await gesture.down(center);
|
||||
await tester.pump(); // Start the splash and highlight animations.
|
||||
await tester.pump(const Duration(milliseconds: 800)); // Wait for splash and highlight to be well under way.
|
||||
expect(iconColor(), pressedColor);
|
||||
expect(iconStyle(tester, Icons.add).color, pressedColor);
|
||||
|
||||
focusNode.dispose();
|
||||
});
|
||||
@ -2028,8 +2034,7 @@ void main() {
|
||||
Material material = tester.widget<Material>(buttonMaterial);
|
||||
expect(material.textStyle!.color, colorScheme.primary);
|
||||
|
||||
Color? iconColor() => _iconStyle(tester, Icons.add)?.color;
|
||||
expect(iconColor(), equals(Colors.red));
|
||||
expect(iconStyle(tester, Icons.add).color, equals(Colors.red));
|
||||
|
||||
// disabled button
|
||||
await tester.pumpWidget(
|
||||
@ -2054,7 +2059,7 @@ void main() {
|
||||
|
||||
material = tester.widget<Material>(buttonMaterial);
|
||||
expect(material.textStyle!.color, colorScheme.onSurface.withOpacity(0.38));
|
||||
expect(iconColor(), equals(Colors.blue));
|
||||
expect(iconStyle(tester, Icons.add).color, equals(Colors.blue));
|
||||
});
|
||||
|
||||
testWidgets("TextButton.styleFrom doesn't throw exception on passing only one cursor", (WidgetTester tester) async {
|
||||
@ -2457,11 +2462,38 @@ void main() {
|
||||
expect(overlayColor(), paints..rect(color: theme.colorScheme.primary.withOpacity(0.08)));
|
||||
expect(hasBeenHovered, isTrue);
|
||||
});
|
||||
}
|
||||
|
||||
TextStyle? _iconStyle(WidgetTester tester, IconData icon) {
|
||||
final RichText iconRichText = tester.widget<RichText>(
|
||||
find.descendant(of: find.byIcon(icon), matching: find.byType(RichText)),
|
||||
);
|
||||
return iconRichText.text.style;
|
||||
// Regression test for https://github.com/flutter/flutter/issues/154798.
|
||||
testWidgets('TextButton.styleFrom can customize the button icon', (WidgetTester tester) async {
|
||||
const Color iconColor = Color(0xFFF000FF);
|
||||
const double iconSize = 32.0;
|
||||
const Color disabledIconColor = Color(0xFFFFF000);
|
||||
Widget buildButton({ bool enabled = true }) {
|
||||
return MaterialApp(
|
||||
home: Material(
|
||||
child: Center(
|
||||
child: TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
iconColor: iconColor,
|
||||
iconSize: iconSize,
|
||||
disabledIconColor: disabledIconColor,
|
||||
),
|
||||
onPressed: enabled ? () {} : null,
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('Button'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Test enabled button.
|
||||
await tester.pumpWidget(buildButton());
|
||||
expect(tester.getSize(find.byIcon(Icons.add)), const Size(iconSize, iconSize));
|
||||
expect(iconStyle(tester, Icons.add).color, iconColor);
|
||||
|
||||
// Test disabled button.
|
||||
await tester.pumpWidget(buildButton(enabled: false));
|
||||
expect(iconStyle(tester, Icons.add).color, disabledIconColor);
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user