Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 28 additions & 9 deletions packages/flutter/lib/src/material/floating_action_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ class FloatingActionButton extends StatelessWidget {
_floatingActionButtonType = mini ? _FloatingActionButtonType.small : _FloatingActionButtonType.regular,
_extendedLabel = null,
extendedIconLabelSpacing = null,
extendedPadding = null,
super(key: key);

/// Creates a small circular floating action button.
Expand Down Expand Up @@ -217,6 +218,7 @@ class FloatingActionButton extends StatelessWidget {
isExtended = false,
_extendedLabel = null,
extendedIconLabelSpacing = null,
extendedPadding = null,
super(key: key);

/// Creates a large circular floating action button.
Expand Down Expand Up @@ -263,6 +265,7 @@ class FloatingActionButton extends StatelessWidget {
isExtended = false,
_extendedLabel = null,
extendedIconLabelSpacing = null,
extendedPadding = null,
super(key: key);

/// Creates a wider [StadiumBorder]-shaped floating action button with
Expand Down Expand Up @@ -294,6 +297,7 @@ class FloatingActionButton extends StatelessWidget {
this.focusNode,
this.autofocus = false,
this.extendedIconLabelSpacing,
this.extendedPadding,
Widget? icon,
required Widget label,
this.enableFeedback,
Expand Down Expand Up @@ -518,6 +522,14 @@ class FloatingActionButton extends StatelessWidget {
/// 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;
Expand Down Expand Up @@ -596,16 +608,23 @@ class FloatingActionButton extends StatelessWidget {
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class FloatingActionButtonThemeData with Diagnosticable {
this.largeSizeConstraints,
this.extendedSizeConstraints,
this.extendedIconLabelSpacing,
this.extendedPadding,
});

/// Color to be used for the unselected, enabled [FloatingActionButton]'s
Expand Down Expand Up @@ -117,6 +118,9 @@ class FloatingActionButtonThemeData with Diagnosticable {
/// [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({
Expand All @@ -137,6 +141,7 @@ class FloatingActionButtonThemeData with Diagnosticable {
BoxConstraints? largeSizeConstraints,
BoxConstraints? extendedSizeConstraints,
double? extendedIconLabelSpacing,
EdgeInsetsGeometry? extendedPadding,
}) {
return FloatingActionButtonThemeData(
foregroundColor: foregroundColor ?? this.foregroundColor,
Expand All @@ -156,6 +161,7 @@ class FloatingActionButtonThemeData with Diagnosticable {
largeSizeConstraints: largeSizeConstraints ?? this.largeSizeConstraints,
extendedSizeConstraints: extendedSizeConstraints ?? this.extendedSizeConstraints,
extendedIconLabelSpacing: extendedIconLabelSpacing ?? this.extendedIconLabelSpacing,
extendedPadding: extendedPadding ?? this.extendedPadding,
);
}

Expand Down Expand Up @@ -186,6 +192,7 @@ class FloatingActionButtonThemeData with Diagnosticable {
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),
);
}

Expand All @@ -209,6 +216,7 @@ class FloatingActionButtonThemeData with Diagnosticable {
largeSizeConstraints,
extendedSizeConstraints,
extendedIconLabelSpacing,
extendedPadding,
);
}

Expand All @@ -235,7 +243,8 @@ class FloatingActionButtonThemeData with Diagnosticable {
&& other.smallSizeConstraints == smallSizeConstraints
&& other.largeSizeConstraints == largeSizeConstraints
&& other.extendedSizeConstraints == extendedSizeConstraints
&& other.extendedIconLabelSpacing == extendedIconLabelSpacing;
&& other.extendedIconLabelSpacing == extendedIconLabelSpacing
&& other.extendedPadding == extendedPadding;
}

@override
Expand All @@ -259,5 +268,6 @@ class FloatingActionButtonThemeData with Diagnosticable {
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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -998,10 +998,11 @@ void main() {
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(
Expand All @@ -1010,13 +1011,16 @@ void main() {
label: const Text('', key: labelKey),
icon: const Icon(Icons.add, key: iconKey),
extendedIconLabelSpacing: spacing,
extendedPadding: padding,
onPressed: () {},
),
),
),
);

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', () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,13 +180,15 @@ void main() {
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(
Expand All @@ -199,31 +201,38 @@ void main() {
));

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(
floatingActionButton: FloatingActionButton.extended(
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 {
Expand Down Expand Up @@ -258,6 +267,7 @@ void main() {
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
Expand All @@ -282,7 +292,8 @@ void main() {
'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)',
]);
});
}
Expand Down