diff --git a/packages/material_ui/lib/src/button_theme.dart b/packages/material_ui/lib/src/button_theme.dart index f59f15c59b2..a466885edf0 100644 --- a/packages/material_ui/lib/src/button_theme.dart +++ b/packages/material_ui/lib/src/button_theme.dart @@ -396,7 +396,10 @@ class ButtonThemeData with Diagnosticable { /// If [MaterialButton.textColor] is a [WidgetStateProperty], it will be /// used as the `disabledTextColor`. It will be resolved in the [WidgetState.disabled] state. Color getDisabledTextColor(MaterialButton button) { - return button.textColor ?? button.disabledTextColor ?? colorScheme!.onSurface.withOpacity(0.38); + if (button.textColor is WidgetStateProperty) { + return button.textColor!; + } + return button.disabledTextColor ?? button.textColor ?? colorScheme!.onSurface.withOpacity(0.38); } /// The [button]'s background color when [MaterialButton.onPressed] is null diff --git a/packages/material_ui/pending_changelogs/change_2026_09_22_1790099179515.yaml b/packages/material_ui/pending_changelogs/change_2026_09_22_1790099179515.yaml new file mode 100644 index 00000000000..a017cc0b4b7 --- /dev/null +++ b/packages/material_ui/pending_changelogs/change_2026_09_22_1790099179515.yaml @@ -0,0 +1,3 @@ +changelog: | + - Fixes MaterialButton using textColor instead of disabledTextColor when disabled. +version: patch diff --git a/packages/material_ui/test/material_button_test.dart b/packages/material_ui/test/material_button_test.dart index 8ed9b0f8c3c..b56dc986399 100644 --- a/packages/material_ui/test/material_button_test.dart +++ b/packages/material_ui/test/material_button_test.dart @@ -275,6 +275,78 @@ void main() { expect(material.color, const Color(0xff00ff00)); }); + testWidgets('Disabled MaterialButton prefers disabledTextColor over textColor', ( + WidgetTester tester, + ) async { + // Regression test for https://github.com/flutter/flutter/issues/127449. + + final Finder rawButtonMaterial = find.descendant( + of: find.byType(MaterialButton), + matching: find.byType(Material), + ); + + await tester.pumpWidget( + const Directionality( + textDirection: TextDirection.ltr, + child: MaterialButton( + onPressed: null, + textColor: Colors.blue, + disabledTextColor: Colors.red, + child: Text('button'), + ), + ), + ); + + final Material material = tester.widget(rawButtonMaterial); + expect(material.textStyle!.color, Colors.red); + }); + + testWidgets( + 'Disabled MaterialButton resolves stateful textColor when disabledTextColor is null', + (WidgetTester tester) async { + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: MaterialButton( + onPressed: null, + textColor: WidgetStateColor.resolveWith((Set states) { + return states.contains(WidgetState.disabled) ? Colors.grey : Colors.blue; + }), + child: const Text('button'), + ), + ), + ); + + final Material material = tester.widget( + find.descendant(of: find.byType(MaterialButton), matching: find.byType(Material)), + ); + expect(material.textStyle!.color, Colors.grey); + }, + ); + + testWidgets('Disabled MaterialButton prefers stateful textColor over disabledTextColor', ( + WidgetTester tester, + ) async { + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: MaterialButton( + onPressed: null, + textColor: WidgetStateColor.resolveWith((Set states) { + return states.contains(WidgetState.disabled) ? Colors.grey : Colors.blue; + }), + disabledTextColor: Colors.red, + child: const Text('button'), + ), + ), + ); + + final Material material = tester.widget( + find.descendant(of: find.byType(MaterialButton), matching: find.byType(Material)), + ); + expect(material.textStyle!.color, Colors.grey); + }); + testWidgets( 'Default MaterialButton meets a11y contrast guidelines', (WidgetTester tester) async {