Allow for customizing and theming of extended FAB content padding (#87062)

diff --git a/packages/flutter/lib/src/material/floating_action_button.dart b/packages/flutter/lib/src/material/floating_action_button.dart
index 4dc6308..9f94800 100644
--- a/packages/flutter/lib/src/material/floating_action_button.dart
+++ b/packages/flutter/lib/src/material/floating_action_button.dart
@@ -171,6 +171,7 @@
        _floatingActionButtonType = mini ? _FloatingActionButtonType.small : _FloatingActionButtonType.regular,
        _extendedLabel = null,
        extendedIconLabelSpacing = null,
+       extendedPadding = null,
        super(key: key);
 
   /// Creates a small circular floating action button.
@@ -217,6 +218,7 @@
        isExtended = false,
        _extendedLabel = null,
        extendedIconLabelSpacing = null,
+       extendedPadding = null,
        super(key: key);
 
   /// Creates a large circular floating action button.
@@ -263,6 +265,7 @@
        isExtended = false,
        _extendedLabel = null,
        extendedIconLabelSpacing = null,
+       extendedPadding = null,
        super(key: key);
 
   /// Creates a wider [StadiumBorder]-shaped floating action button with
@@ -294,6 +297,7 @@
     this.focusNode,
     this.autofocus = false,
     this.extendedIconLabelSpacing,
+    this.extendedPadding,
     Widget? icon,
     required Widget label,
     this.enableFeedback,
@@ -518,6 +522,14 @@
   /// If that is also null, the default is 8.0.
   final double? extendedIconLabelSpacing;
 
+  /// The padding for an extended [FloatingActionButton]'s content.
+  ///
+  /// If null, [FloatingActionButtonThemeData.extendedPadding] is used. If that
+  /// is also null, the default is
+  /// `EdgeInsetsDirectional.only(start: 16.0, end: 20.0)` if an icon is
+  /// provided, and `EdgeInsetsDirectional.only(start: 20.0, end: 20.0)` if not.
+  final EdgeInsetsGeometry? extendedPadding;
+
   final _FloatingActionButtonType _floatingActionButtonType;
 
   final Widget? _extendedLabel;
@@ -596,16 +608,23 @@
       case _FloatingActionButtonType.extended:
         sizeConstraints = floatingActionButtonTheme.extendedSizeConstraints ?? _kExtendedSizeConstraints;
         final double iconLabelSpacing = extendedIconLabelSpacing ?? floatingActionButtonTheme.extendedIconLabelSpacing ?? 8.0;
-        const Widget width20 = SizedBox(width: 20.0);
-        const Widget width16 = SizedBox(width: 16.0);
+        final EdgeInsetsGeometry padding = extendedPadding
+            ?? floatingActionButtonTheme.extendedPadding
+            ?? EdgeInsetsDirectional.only(start: child != null && isExtended ? 16.0 : 20.0, end: 20.0);
         resolvedChild = _ChildOverflowBox(
-          child: Row(
-              mainAxisSize: MainAxisSize.min,
-              children: child == null
-                  ? <Widget>[width20, _extendedLabel!, width20]
-                  : isExtended
-                      ? <Widget>[width16, child!, SizedBox(width: iconLabelSpacing), _extendedLabel!, width20]
-                      : <Widget>[width20, child!, width20],
+          child: Padding(
+            padding: padding,
+            child: Row(
+                mainAxisSize: MainAxisSize.min,
+                children: <Widget>[
+                  if (child != null)
+                    child!,
+                  if (child != null && isExtended)
+                    SizedBox(width: iconLabelSpacing),
+                  if (isExtended)
+                    _extendedLabel!,
+                ],
+            ),
           ),
         );
         break;
diff --git a/packages/flutter/lib/src/material/floating_action_button_theme.dart b/packages/flutter/lib/src/material/floating_action_button_theme.dart
index d020b01..8e4f6ec 100644
--- a/packages/flutter/lib/src/material/floating_action_button_theme.dart
+++ b/packages/flutter/lib/src/material/floating_action_button_theme.dart
@@ -48,6 +48,7 @@
     this.largeSizeConstraints,
     this.extendedSizeConstraints,
     this.extendedIconLabelSpacing,
+    this.extendedPadding,
   });
 
   /// Color to be used for the unselected, enabled [FloatingActionButton]'s
@@ -117,6 +118,9 @@
   /// [FloatingActionButton].
   final double? extendedIconLabelSpacing;
 
+  /// The padding for an extended [FloatingActionButton]'s content.
+  final EdgeInsetsGeometry? extendedPadding;
+
   /// Creates a copy of this object with the given fields replaced with the
   /// new values.
   FloatingActionButtonThemeData copyWith({
@@ -137,6 +141,7 @@
     BoxConstraints? largeSizeConstraints,
     BoxConstraints? extendedSizeConstraints,
     double? extendedIconLabelSpacing,
+    EdgeInsetsGeometry? extendedPadding,
   }) {
     return FloatingActionButtonThemeData(
       foregroundColor: foregroundColor ?? this.foregroundColor,
@@ -156,6 +161,7 @@
       largeSizeConstraints: largeSizeConstraints ?? this.largeSizeConstraints,
       extendedSizeConstraints: extendedSizeConstraints ?? this.extendedSizeConstraints,
       extendedIconLabelSpacing: extendedIconLabelSpacing ?? this.extendedIconLabelSpacing,
+      extendedPadding: extendedPadding ?? this.extendedPadding,
     );
   }
 
@@ -186,6 +192,7 @@
       largeSizeConstraints: BoxConstraints.lerp(a?.largeSizeConstraints, b?.largeSizeConstraints, t),
       extendedSizeConstraints: BoxConstraints.lerp(a?.extendedSizeConstraints, b?.extendedSizeConstraints, t),
       extendedIconLabelSpacing: lerpDouble(a?.extendedIconLabelSpacing, b?.extendedIconLabelSpacing, t),
+      extendedPadding: EdgeInsetsGeometry.lerp(a?.extendedPadding, b?.extendedPadding, t),
     );
   }
 
@@ -209,6 +216,7 @@
       largeSizeConstraints,
       extendedSizeConstraints,
       extendedIconLabelSpacing,
+      extendedPadding,
     );
   }
 
@@ -235,7 +243,8 @@
         && other.smallSizeConstraints == smallSizeConstraints
         && other.largeSizeConstraints == largeSizeConstraints
         && other.extendedSizeConstraints == extendedSizeConstraints
-        && other.extendedIconLabelSpacing == extendedIconLabelSpacing;
+        && other.extendedIconLabelSpacing == extendedIconLabelSpacing
+        && other.extendedPadding == extendedPadding;
   }
 
   @override
@@ -259,5 +268,6 @@
     properties.add(DiagnosticsProperty<BoxConstraints>('largeSizeConstraints', largeSizeConstraints, defaultValue: null));
     properties.add(DiagnosticsProperty<BoxConstraints>('extendedSizeConstraints', extendedSizeConstraints, defaultValue: null));
     properties.add(DoubleProperty('extendedIconLabelSpacing', extendedIconLabelSpacing, defaultValue: null));
+    properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('extendedPadding', extendedPadding, defaultValue: null));
   }
 }
diff --git a/packages/flutter/test/material/floating_action_button_test.dart b/packages/flutter/test/material/floating_action_button_test.dart
index a71521e..2989358 100644
--- a/packages/flutter/test/material/floating_action_button_test.dart
+++ b/packages/flutter/test/material/floating_action_button_test.dart
@@ -998,10 +998,11 @@
     expect(tester.getSize(find.byKey(key)), const Size(96.0, 96.0));
   });
 
-  testWidgets('FloatingActionButton.extended can customize spacing between icon and label', (WidgetTester tester) async {
+  testWidgets('FloatingActionButton.extended can customize spacing', (WidgetTester tester) async {
     const Key iconKey = Key('icon');
     const Key labelKey = Key('label');
     const double spacing = 33.0;
+    const EdgeInsetsDirectional padding = EdgeInsetsDirectional.only(start: 5.0, end: 6.0);
 
     await tester.pumpWidget(
       MaterialApp(
@@ -1010,6 +1011,7 @@
             label: const Text('', key: labelKey),
             icon: const Icon(Icons.add, key: iconKey),
             extendedIconLabelSpacing: spacing,
+            extendedPadding: padding,
             onPressed: () {},
           ),
         ),
@@ -1017,6 +1019,8 @@
     );
 
     expect(tester.getTopLeft(find.byKey(labelKey)).dx - tester.getTopRight(find.byKey(iconKey)).dx, spacing);
+    expect(tester.getTopLeft(find.byKey(iconKey)).dx - tester.getTopLeft(find.byType(FloatingActionButton)).dx, padding.start);
+    expect(tester.getTopRight(find.byType(FloatingActionButton)).dx - tester.getTopRight(find.byKey(labelKey)).dx, padding.end);
   });
 
   group('feedback', () {
diff --git a/packages/flutter/test/material/floating_action_button_theme_test.dart b/packages/flutter/test/material/floating_action_button_theme_test.dart
index 22a306f..e0c486c 100644
--- a/packages/flutter/test/material/floating_action_button_theme_test.dart
+++ b/packages/flutter/test/material/floating_action_button_theme_test.dart
@@ -180,13 +180,15 @@
     const Key iconKey = Key('icon');
     const Key labelKey = Key('label');
     const BoxConstraints constraints = BoxConstraints.tightFor(height: 100.0);
-    const double spacing = 33.0;
+    const double iconLabelSpacing = 33.0;
+    const EdgeInsetsDirectional padding = EdgeInsetsDirectional.only(start: 5.0, end: 6.0);
 
     await tester.pumpWidget(MaterialApp(
       theme: ThemeData().copyWith(
         floatingActionButtonTheme: const FloatingActionButtonThemeData(
           extendedSizeConstraints: constraints,
-          extendedIconLabelSpacing: spacing,
+          extendedIconLabelSpacing: iconLabelSpacing,
+          extendedPadding: padding,
         ),
       ),
       home: Scaffold(
@@ -199,18 +201,22 @@
     ));
 
     expect(_getRawMaterialButton(tester).constraints, constraints);
-    expect(tester.getTopLeft(find.byKey(labelKey)).dx - tester.getTopRight(find.byKey(iconKey)).dx, spacing);
+    expect(tester.getTopLeft(find.byKey(labelKey)).dx - tester.getTopRight(find.byKey(iconKey)).dx, iconLabelSpacing);
+    expect(tester.getTopLeft(find.byKey(iconKey)).dx - tester.getTopLeft(find.byType(FloatingActionButton)).dx, padding.start);
+    expect(tester.getTopRight(find.byType(FloatingActionButton)).dx - tester.getTopRight(find.byKey(labelKey)).dx, padding.end);
   });
 
   testWidgets('FloatingActionButton.extended spacing takes priority over FloatingActionButtonThemeData spacing', (WidgetTester tester) async {
     const Key iconKey = Key('icon');
     const Key labelKey = Key('label');
-    const double spacing = 33.0;
+    const double iconLabelSpacing = 33.0;
+    const EdgeInsetsDirectional padding = EdgeInsetsDirectional.only(start: 5.0, end: 6.0);
 
     await tester.pumpWidget(MaterialApp(
       theme: ThemeData().copyWith(
         floatingActionButtonTheme: const FloatingActionButtonThemeData(
           extendedIconLabelSpacing: 25.0,
+          extendedPadding: EdgeInsetsDirectional.only(start: 7.0, end: 8.0),
         ),
       ),
       home: Scaffold(
@@ -218,12 +224,15 @@
           onPressed: () { },
           label: const Text('Extended', key: labelKey),
           icon: const Icon(Icons.add, key: iconKey),
-          extendedIconLabelSpacing: spacing,
+          extendedIconLabelSpacing: iconLabelSpacing,
+          extendedPadding: padding,
         ),
       ),
     ));
 
-    expect(tester.getTopLeft(find.byKey(labelKey)).dx - tester.getTopRight(find.byKey(iconKey)).dx, spacing);
+    expect(tester.getTopLeft(find.byKey(labelKey)).dx - tester.getTopRight(find.byKey(iconKey)).dx, iconLabelSpacing);
+    expect(tester.getTopLeft(find.byKey(iconKey)).dx - tester.getTopLeft(find.byType(FloatingActionButton)).dx, padding.start);
+    expect(tester.getTopRight(find.byType(FloatingActionButton)).dx - tester.getTopRight(find.byKey(labelKey)).dx, padding.end);
   });
 
   testWidgets('default FloatingActionButton debugFillProperties', (WidgetTester tester) async {
@@ -258,6 +267,7 @@
       largeSizeConstraints: BoxConstraints.tightFor(width: 102.0, height: 102.0),
       extendedSizeConstraints: BoxConstraints(minHeight: 103.0, maxHeight: 103.0),
       extendedIconLabelSpacing: 12,
+      extendedPadding: EdgeInsetsDirectional.only(start: 7.0, end: 8.0),
     ).debugFillProperties(builder);
 
     final List<String> description = builder.properties
@@ -282,7 +292,8 @@
       'smallSizeConstraints: BoxConstraints(w=101.0, h=101.0)',
       'largeSizeConstraints: BoxConstraints(w=102.0, h=102.0)',
       'extendedSizeConstraints: BoxConstraints(0.0<=w<=Infinity, h=103.0)',
-      'extendedIconLabelSpacing: 12.0'
+      'extendedIconLabelSpacing: 12.0',
+      'extendedPadding: EdgeInsetsDirectional(7.0, 0.0, 8.0, 0.0)',
     ]);
   });
 }