diff --git a/examples/api/lib/material/app_bar/app_bar.1.dart b/examples/api/lib/material/app_bar/app_bar.1.dart index 9ded23bf0f814..2f4eb93cba4f7 100644 --- a/examples/api/lib/material/app_bar/app_bar.1.dart +++ b/examples/api/lib/material/app_bar/app_bar.1.dart @@ -98,7 +98,7 @@ class _AppBarExampleState extends State { label: const Text('shadow color'), ), const SizedBox(width: 5), - ElevatedButton.icon( + ElevatedButton( onPressed: () { if (scrolledUnderElevation == null) { setState(() { @@ -111,8 +111,7 @@ class _AppBarExampleState extends State { }); } }, - icon: const Icon(Icons.add), - label: Text( + child: Text( 'scrolledUnderElevation: ${scrolledUnderElevation ?? 'default'}', ), ), diff --git a/packages/flutter/lib/src/material/elevated_button.dart b/packages/flutter/lib/src/material/elevated_button.dart index 94b6dfc51cc76..3b6c659408ce4 100644 --- a/packages/flutter/lib/src/material/elevated_button.dart +++ b/packages/flutter/lib/src/material/elevated_button.dart @@ -333,10 +333,10 @@ class ElevatedButton extends ButtonStyleButton { /// * hovered - 3 /// * focused or pressed - 1 /// * `padding` - /// * `textScaleFactor <= 1` - horizontal(16) - /// * `1 < textScaleFactor <= 2` - lerp(horizontal(16), horizontal(8)) - /// * `2 < textScaleFactor <= 3` - lerp(horizontal(8), horizontal(4)) - /// * `3 < textScaleFactor` - horizontal(4) + /// * `textScaleFactor <= 1` - horizontal(24) + /// * `1 < textScaleFactor <= 2` - lerp(horizontal(24), horizontal(12)) + /// * `2 < textScaleFactor <= 3` - lerp(horizontal(12), horizontal(6)) + /// * `3 < textScaleFactor` - horizontal(6) /// * `minimumSize` - Size(64, 40) /// * `fixedSize` - null /// * `maximumSize` - Size.infinite @@ -351,6 +351,10 @@ class ElevatedButton extends ButtonStyleButton { /// * `enableFeedback` - true /// * `alignment` - Alignment.center /// * `splashFactory` - Theme.splashFactory + /// + /// For the [ElevatedButton.icon] factory, the start (generally the left) value of + /// [padding] is reduced from 24 to 16. + @override ButtonStyle defaultStyleOf(BuildContext context) { final ThemeData theme = Theme.of(context); @@ -390,10 +394,12 @@ class ElevatedButton extends ButtonStyleButton { } EdgeInsetsGeometry _scaledPadding(BuildContext context) { + final bool useMaterial3 = Theme.of(context).useMaterial3; + final double padding1x = useMaterial3 ? 24.0 : 16.0; return ButtonStyleButton.scaledPadding( - const EdgeInsets.symmetric(horizontal: 16), - const EdgeInsets.symmetric(horizontal: 8), - const EdgeInsets.symmetric(horizontal: 4), + EdgeInsets.symmetric(horizontal: padding1x), + EdgeInsets.symmetric(horizontal: padding1x / 2), + EdgeInsets.symmetric(horizontal: padding1x / 2 / 2), MediaQuery.textScaleFactorOf(context), ); } @@ -496,7 +502,13 @@ class _ElevatedButtonWithIcon extends ElevatedButton { @override ButtonStyle defaultStyleOf(BuildContext context) { - final EdgeInsetsGeometry scaledPadding = ButtonStyleButton.scaledPadding( + final bool useMaterial3 = Theme.of(context).useMaterial3; + final EdgeInsetsGeometry scaledPadding = useMaterial3 ? ButtonStyleButton.scaledPadding( + const EdgeInsetsDirectional.fromSTEB(16, 0, 24, 0), + const EdgeInsetsDirectional.fromSTEB(8, 0, 12, 0), + const EdgeInsetsDirectional.fromSTEB(4, 0, 6, 0), + MediaQuery.textScaleFactorOf(context), + ) : ButtonStyleButton.scaledPadding( const EdgeInsetsDirectional.fromSTEB(12, 0, 16, 0), const EdgeInsets.symmetric(horizontal: 8), const EdgeInsetsDirectional.fromSTEB(8, 0, 4, 0), diff --git a/packages/flutter/lib/src/material/filled_button.dart b/packages/flutter/lib/src/material/filled_button.dart index 69efdde9d5825..0f3f77edef1f9 100644 --- a/packages/flutter/lib/src/material/filled_button.dart +++ b/packages/flutter/lib/src/material/filled_button.dart @@ -345,6 +345,50 @@ class FilledButton extends ButtonStyleButton { /// shape's [OutlinedBorder.side]. Typically the default value of an /// [OutlinedBorder]'s side is [BorderSide.none], so an outline is not drawn. /// + /// ## Material 3 defaults + /// + /// If [ThemeData.useMaterial3] is set to true the following defaults will + /// be used: + /// + /// * `textStyle` - Theme.textTheme.labelLarge + /// * `backgroundColor` + /// * disabled - Theme.colorScheme.onSurface(0.12) + /// * others - Theme.colorScheme.secondaryContainer + /// * `foregroundColor` + /// * disabled - Theme.colorScheme.onSurface(0.38) + /// * others - Theme.colorScheme.onSecondaryContainer + /// * `overlayColor` + /// * hovered - Theme.colorScheme.onSecondaryContainer(0.08) + /// * focused or pressed - Theme.colorScheme.onSecondaryContainer(0.12) + /// * `shadowColor` - Theme.colorScheme.shadow + /// * `surfaceTintColor` - Colors.transparent + /// * `elevation` + /// * disabled - 0 + /// * default - 1 + /// * hovered - 3 + /// * focused or pressed - 1 + /// * `padding` + /// * `textScaleFactor <= 1` - horizontal(24) + /// * `1 < textScaleFactor <= 2` - lerp(horizontal(24), horizontal(12)) + /// * `2 < textScaleFactor <= 3` - lerp(horizontal(12), horizontal(6)) + /// * `3 < textScaleFactor` - horizontal(6) + /// * `minimumSize` - Size(64, 40) + /// * `fixedSize` - null + /// * `maximumSize` - Size.infinite + /// * `side` - null + /// * `shape` - StadiumBorder() + /// * `mouseCursor` + /// * disabled - SystemMouseCursors.basic + /// * others - SystemMouseCursors.click + /// * `visualDensity` - Theme.visualDensity + /// * `tapTargetSize` - Theme.materialTapTargetSize + /// * `animationDuration` - kThemeChangeDuration + /// * `enableFeedback` - true + /// * `alignment` - Alignment.center + /// * `splashFactory` - Theme.splashFactory + /// + /// For the [FilledButton.icon] factory, the start (generally the left) value of + /// [padding] is reduced from 24 to 16. @override ButtonStyle defaultStyleOf(BuildContext context) { switch (_variant) { @@ -364,10 +408,12 @@ class FilledButton extends ButtonStyleButton { } EdgeInsetsGeometry _scaledPadding(BuildContext context) { + final bool useMaterial3 = Theme.of(context).useMaterial3; + final double padding1x = useMaterial3 ? 24.0 : 16.0; return ButtonStyleButton.scaledPadding( - const EdgeInsets.symmetric(horizontal: 16), - const EdgeInsets.symmetric(horizontal: 8), - const EdgeInsets.symmetric(horizontal: 4), + EdgeInsets.symmetric(horizontal: padding1x), + EdgeInsets.symmetric(horizontal: padding1x / 2), + EdgeInsets.symmetric(horizontal: padding1x / 2 / 2), MediaQuery.textScaleFactorOf(context), ); } @@ -467,7 +513,13 @@ class _FilledButtonWithIcon extends FilledButton { @override ButtonStyle defaultStyleOf(BuildContext context) { - final EdgeInsetsGeometry scaledPadding = ButtonStyleButton.scaledPadding( + final bool useMaterial3 = Theme.of(context).useMaterial3; + final EdgeInsetsGeometry scaledPadding = useMaterial3 ? ButtonStyleButton.scaledPadding( + const EdgeInsetsDirectional.fromSTEB(16, 0, 24, 0), + const EdgeInsetsDirectional.fromSTEB(8, 0, 12, 0), + const EdgeInsetsDirectional.fromSTEB(4, 0, 6, 0), + MediaQuery.textScaleFactorOf(context), + ) : ButtonStyleButton.scaledPadding( const EdgeInsetsDirectional.fromSTEB(12, 0, 16, 0), const EdgeInsets.symmetric(horizontal: 8), const EdgeInsetsDirectional.fromSTEB(8, 0, 4, 0), diff --git a/packages/flutter/lib/src/material/outlined_button.dart b/packages/flutter/lib/src/material/outlined_button.dart index b692cec239724..1d9633ea6de6b 100644 --- a/packages/flutter/lib/src/material/outlined_button.dart +++ b/packages/flutter/lib/src/material/outlined_button.dart @@ -287,10 +287,10 @@ class OutlinedButton extends ButtonStyleButton { /// * `surfaceTintColor` - null /// * `elevation` - 0 /// * `padding` - /// * `textScaleFactor <= 1` - horizontal(16) - /// * `1 < textScaleFactor <= 2` - lerp(horizontal(16), horizontal(8)) - /// * `2 < textScaleFactor <= 3` - lerp(horizontal(8), horizontal(4)) - /// * `3 < textScaleFactor` - horizontal(4) + /// * `textScaleFactor <= 1` - horizontal(24) + /// * `1 < textScaleFactor <= 2` - lerp(horizontal(24), horizontal(12)) + /// * `2 < textScaleFactor <= 3` - lerp(horizontal(12), horizontal(6)) + /// * `3 < textScaleFactor` - horizontal(6) /// * `minimumSize` - Size(64, 40) /// * `fixedSize` - null /// * `maximumSize` - Size.infinite @@ -307,6 +307,9 @@ class OutlinedButton extends ButtonStyleButton { /// * `enableFeedback` - true /// * `alignment` - Alignment.center /// * `splashFactory` - Theme.splashFactory + /// + /// For the [OutlinedButton.icon] factory, the start (generally the left) value of + /// [padding] is reduced from 24 to 16. @override ButtonStyle defaultStyleOf(BuildContext context) { final ThemeData theme = Theme.of(context); @@ -347,10 +350,12 @@ class OutlinedButton extends ButtonStyleButton { } EdgeInsetsGeometry _scaledPadding(BuildContext context) { + final bool useMaterial3 = Theme.of(context).useMaterial3; + final double padding1x = useMaterial3 ? 24.0 : 16.0; return ButtonStyleButton.scaledPadding( - const EdgeInsets.symmetric(horizontal: 16), - const EdgeInsets.symmetric(horizontal: 8), - const EdgeInsets.symmetric(horizontal: 4), + EdgeInsets.symmetric(horizontal: padding1x), + EdgeInsets.symmetric(horizontal: padding1x / 2), + EdgeInsets.symmetric(horizontal: padding1x / 2 / 2), MediaQuery.textScaleFactorOf(context), ); } @@ -424,6 +429,23 @@ class _OutlinedButtonWithIcon extends OutlinedButton { clipBehavior: clipBehavior ?? Clip.none, child: _OutlinedButtonWithIconChild(icon: icon, label: label), ); + + @override + ButtonStyle defaultStyleOf(BuildContext context) { + final bool useMaterial3 = Theme.of(context).useMaterial3; + if (!useMaterial3) { + return super.defaultStyleOf(context); + } + final EdgeInsetsGeometry scaledPadding = ButtonStyleButton.scaledPadding( + const EdgeInsetsDirectional.fromSTEB(16, 0, 24, 0), + const EdgeInsetsDirectional.fromSTEB(8, 0, 12, 0), + const EdgeInsetsDirectional.fromSTEB(4, 0, 6, 0), + MediaQuery.textScaleFactorOf(context), + ); + return super.defaultStyleOf(context).copyWith( + padding: MaterialStatePropertyAll(scaledPadding), + ); + } } class _OutlinedButtonWithIconChild extends StatelessWidget { diff --git a/packages/flutter/lib/src/material/text_button.dart b/packages/flutter/lib/src/material/text_button.dart index a337ac6d47a19..97b77ff69d1f6 100644 --- a/packages/flutter/lib/src/material/text_button.dart +++ b/packages/flutter/lib/src/material/text_button.dart @@ -270,7 +270,7 @@ class TextButton extends ButtonStyleButton { /// * `shadowColor` - Theme.shadowColor /// * `elevation` - 0 /// * `padding` - /// * `textScaleFactor <= 1` - all(8) + /// * `textScaleFactor <= 1` - (horizontal(12), vertical(8)) /// * `1 < textScaleFactor <= 2` - lerp(all(8), horizontal(8)) /// * `2 < textScaleFactor <= 3` - lerp(horizontal(8), horizontal(4)) /// * `3 < textScaleFactor` - horizontal(4) @@ -320,7 +320,7 @@ class TextButton extends ButtonStyleButton { /// * `surfaceTintColor` - null /// * `elevation` - 0 /// * `padding` - /// * `textScaleFactor <= 1` - all(8) + /// * `textScaleFactor <= 1` - lerp(horizontal(12), horizontal(4)) /// * `1 < textScaleFactor <= 2` - lerp(all(8), horizontal(8)) /// * `2 < textScaleFactor <= 3` - lerp(horizontal(8), horizontal(4)) /// * `3 < textScaleFactor` - horizontal(4) @@ -338,6 +338,9 @@ class TextButton extends ButtonStyleButton { /// * `enableFeedback` - true /// * `alignment` - Alignment.center /// * `splashFactory` - Theme.splashFactory + /// + /// For the [TextButton.icon] factory, the end (generally the right) value of + /// [padding] is increased from 12 to 16. /// {@endtemplate} @override ButtonStyle defaultStyleOf(BuildContext context) { @@ -378,8 +381,9 @@ class TextButton extends ButtonStyleButton { } EdgeInsetsGeometry _scaledPadding(BuildContext context) { + final bool useMaterial3 = Theme.of(context).useMaterial3; return ButtonStyleButton.scaledPadding( - const EdgeInsets.all(8), + useMaterial3 ? const EdgeInsets.symmetric(horizontal: 12, vertical: 8) : const EdgeInsets.all(8), const EdgeInsets.symmetric(horizontal: 8), const EdgeInsets.symmetric(horizontal: 4), MediaQuery.textScaleFactorOf(context), @@ -491,8 +495,9 @@ class _TextButtonWithIcon extends TextButton { @override ButtonStyle defaultStyleOf(BuildContext context) { + final bool useMaterial3 = Theme.of(context).useMaterial3; final EdgeInsetsGeometry scaledPadding = ButtonStyleButton.scaledPadding( - const EdgeInsets.all(8), + useMaterial3 ? const EdgeInsetsDirectional.fromSTEB(12, 8, 16, 8) : const EdgeInsets.all(8), const EdgeInsets.symmetric(horizontal: 4), const EdgeInsets.symmetric(horizontal: 4), MediaQuery.textScaleFactorOf(context), diff --git a/packages/flutter/test/material/elevated_button_test.dart b/packages/flutter/test/material/elevated_button_test.dart index bb0d15f0d1fe0..686fe9619b955 100644 --- a/packages/flutter/test/material/elevated_button_test.dart +++ b/packages/flutter/test/material/elevated_button_test.dart @@ -1147,6 +1147,59 @@ void main() { expect(paddingWidget.padding, const EdgeInsets.all(22)); }); + testWidgets('M3 ElevatedButton has correct padding', (WidgetTester tester) async { + final Key key = UniqueKey(); + await tester.pumpWidget( + MaterialApp( + theme: ThemeData.from(colorScheme: const ColorScheme.light(), useMaterial3: true), + home: Scaffold( + body: Center( + child: ElevatedButton( + key: key, + onPressed: () {}, + child: const Text('ElevatedButton'), + ), + ), + ), + ), + ); + + final Padding paddingWidget = tester.widget( + find.descendant( + of: find.byKey(key), + matching: find.byType(Padding), + ), + ); + expect(paddingWidget.padding, const EdgeInsets.symmetric(horizontal: 24)); + }); + + testWidgets('M3 ElevatedButton.icon has correct padding', (WidgetTester tester) async { + final Key key = UniqueKey(); + await tester.pumpWidget( + MaterialApp( + theme: ThemeData.from(colorScheme: const ColorScheme.light(), useMaterial3: true), + home: Scaffold( + body: Center( + child: ElevatedButton.icon( + key: key, + icon: const Icon(Icons.favorite), + onPressed: () {}, + label: const Text('ElevatedButton'), + ), + ), + ), + ), + ); + + final Padding paddingWidget = tester.widget( + find.descendant( + of: find.byKey(key), + matching: find.byType(Padding), + ), + ); + expect(paddingWidget.padding, const EdgeInsetsDirectional.fromSTEB(16.0, 0.0, 24.0, 0.0)); + }); + testWidgets('Elevated buttons animate elevation before color on disable', (WidgetTester tester) async { // This is a regression test for https://github.com/flutter/flutter/issues/387 diff --git a/packages/flutter/test/material/filled_button_test.dart b/packages/flutter/test/material/filled_button_test.dart index fefdb72d64f1e..235729d52b9bd 100644 --- a/packages/flutter/test/material/filled_button_test.dart +++ b/packages/flutter/test/material/filled_button_test.dart @@ -1224,6 +1224,59 @@ void main() { expect(paddingWidget.padding, const EdgeInsets.all(22)); }); + testWidgets('M3 FilledButton has correct padding', (WidgetTester tester) async { + final Key key = UniqueKey(); + await tester.pumpWidget( + MaterialApp( + theme: ThemeData.from(colorScheme: const ColorScheme.light(), useMaterial3: true), + home: Scaffold( + body: Center( + child: ElevatedButton( + key: key, + onPressed: () {}, + child: const Text('FilledButton'), + ), + ), + ), + ), + ); + + final Padding paddingWidget = tester.widget( + find.descendant( + of: find.byKey(key), + matching: find.byType(Padding), + ), + ); + expect(paddingWidget.padding, const EdgeInsets.symmetric(horizontal: 24)); + }); + + testWidgets('M3 FilledButton.icon has correct padding', (WidgetTester tester) async { + final Key key = UniqueKey(); + await tester.pumpWidget( + MaterialApp( + theme: ThemeData.from(colorScheme: const ColorScheme.light(), useMaterial3: true), + home: Scaffold( + body: Center( + child: FilledButton.icon( + key: key, + icon: const Icon(Icons.favorite), + onPressed: () {}, + label: const Text('ElevatedButton'), + ), + ), + ), + ), + ); + + final Padding paddingWidget = tester.widget( + find.descendant( + of: find.byKey(key), + matching: find.byType(Padding), + ), + ); + expect(paddingWidget.padding, const EdgeInsetsDirectional.fromSTEB(16.0, 0.0, 24.0, 0.0)); + }); + testWidgets('By default, FilledButton shape outline is defined by shape.side', (WidgetTester tester) async { const Color borderColor = Color(0xff4caf50); await tester.pumpWidget( diff --git a/packages/flutter/test/material/outlined_button_test.dart b/packages/flutter/test/material/outlined_button_test.dart index 0bfb6e5cf5361..d53af3efbc4c1 100644 --- a/packages/flutter/test/material/outlined_button_test.dart +++ b/packages/flutter/test/material/outlined_button_test.dart @@ -1388,6 +1388,59 @@ void main() { expect(paddingWidget.padding, const EdgeInsets.all(22)); }); + testWidgets('M3 OutlinedButton has correct padding', (WidgetTester tester) async { + final Key key = UniqueKey(); + await tester.pumpWidget( + MaterialApp( + theme: ThemeData.from(colorScheme: const ColorScheme.light(), useMaterial3: true), + home: Scaffold( + body: Center( + child: OutlinedButton( + key: key, + onPressed: () {}, + child: const Text('OutlinedButton'), + ), + ), + ), + ), + ); + + final Padding paddingWidget = tester.widget( + find.descendant( + of: find.byKey(key), + matching: find.byType(Padding), + ), + ); + expect(paddingWidget.padding, const EdgeInsets.symmetric(horizontal: 24)); + }); + + testWidgets('M3 OutlinedButton.icon has correct padding', (WidgetTester tester) async { + final Key key = UniqueKey(); + await tester.pumpWidget( + MaterialApp( + theme: ThemeData.from(colorScheme: const ColorScheme.light(), useMaterial3: true), + home: Scaffold( + body: Center( + child: OutlinedButton.icon( + key: key, + icon: const Icon(Icons.favorite), + onPressed: () {}, + label: const Text('OutlinedButton'), + ), + ), + ), + ), + ); + + final Padding paddingWidget = tester.widget( + find.descendant( + of: find.byKey(key), + matching: find.byType(Padding), + ), + ); + expect(paddingWidget.padding, const EdgeInsetsDirectional.fromSTEB(16.0, 0.0, 24.0, 0.0)); + }); + testWidgets('Fixed size OutlinedButtons', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( diff --git a/packages/flutter/test/material/text_button_test.dart b/packages/flutter/test/material/text_button_test.dart index 17fd1029b4910..224b4c631dd0c 100644 --- a/packages/flutter/test/material/text_button_test.dart +++ b/packages/flutter/test/material/text_button_test.dart @@ -1195,6 +1195,59 @@ void main() { expect(paddingWidget.padding, const EdgeInsets.all(22)); }); + testWidgets('M3 TextButton has correct default padding', (WidgetTester tester) async { + final Key key = UniqueKey(); + await tester.pumpWidget( + MaterialApp( + theme: ThemeData.from(colorScheme: const ColorScheme.light(), useMaterial3: true), + home: Scaffold( + body: Center( + child: TextButton( + key: key, + onPressed: () {}, + child: const Text('TextButton'), + ), + ), + ), + ), + ); + + final Padding paddingWidget = tester.widget( + find.descendant( + of: find.byKey(key), + matching: find.byType(Padding), + ), + ); + expect(paddingWidget.padding, const EdgeInsets.symmetric(horizontal: 12,vertical: 8)); + }); + + testWidgets('M3 TextButton.icon has correct default padding', (WidgetTester tester) async { + final Key key = UniqueKey(); + await tester.pumpWidget( + MaterialApp( + theme: ThemeData.from(colorScheme: const ColorScheme.light(), useMaterial3: true), + home: Scaffold( + body: Center( + child: TextButton.icon( + key: key, + onPressed: () {}, + icon: const Icon(Icons.add), + label: const Text('TextButton'), + ), + ), + ), + ), + ); + + final Padding paddingWidget = tester.widget( + find.descendant( + of: find.byKey(key), + matching: find.byType(Padding), + ), + ); + expect(paddingWidget.padding, const EdgeInsetsDirectional.fromSTEB(12, 8, 16, 8)); + }); + testWidgets('Fixed size TextButtons', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp(