Unverified Commit 8866ac65 authored by Rami's avatar Rami Committed by GitHub

[Material You] Introduce large FAB size and allow for FAB size theming (#86441)

parent df3fe587
...@@ -25,9 +25,13 @@ const BoxConstraints _kMiniSizeConstraints = BoxConstraints.tightFor( ...@@ -25,9 +25,13 @@ const BoxConstraints _kMiniSizeConstraints = BoxConstraints.tightFor(
height: 40.0, height: 40.0,
); );
const BoxConstraints _kExtendedSizeConstraints = BoxConstraints( const BoxConstraints _kLargeSizeConstraints = BoxConstraints.tightFor(
minHeight: 48.0, width: 96.0,
maxHeight: 48.0, height: 96.0,
);
const BoxConstraints _kExtendedSizeConstraints = BoxConstraints.tightFor(
height: 48.0,
); );
class _DefaultHeroTag { class _DefaultHeroTag {
...@@ -36,6 +40,13 @@ class _DefaultHeroTag { ...@@ -36,6 +40,13 @@ class _DefaultHeroTag {
String toString() => '<default FloatingActionButton tag>'; String toString() => '<default FloatingActionButton tag>';
} }
enum _FloatingActionButtonType {
regular,
small,
large,
extended,
}
/// A material design floating action button. /// A material design floating action button.
/// ///
/// A floating action button is a circular icon button that hovers over content /// A floating action button is a circular icon button that hovers over content
...@@ -157,7 +168,101 @@ class FloatingActionButton extends StatelessWidget { ...@@ -157,7 +168,101 @@ class FloatingActionButton extends StatelessWidget {
assert(clipBehavior != null), assert(clipBehavior != null),
assert(isExtended != null), assert(isExtended != null),
assert(autofocus != null), assert(autofocus != null),
_sizeConstraints = mini ? _kMiniSizeConstraints : _kSizeConstraints, _floatingActionButtonType = mini ? _FloatingActionButtonType.small : _FloatingActionButtonType.regular,
_extendedLabel = null,
extendedIconLabelSpacing = null,
super(key: key);
/// Creates a small circular floating action button.
///
/// This constructor overrides the default size constraints of the floating
/// action button.
///
/// The [clipBehavior] and [autofocus] arguments must not be null.
/// Additionally, [elevation], [focusElevation], [hoverElevation],
/// [highlightElevation], and [disabledElevation] (if specified) must be
/// non-negative.
const FloatingActionButton.small({
Key? key,
this.child,
this.tooltip,
this.foregroundColor,
this.backgroundColor,
this.focusColor,
this.hoverColor,
this.splashColor,
this.heroTag = const _DefaultHeroTag(),
this.elevation,
this.focusElevation,
this.hoverElevation,
this.highlightElevation,
this.disabledElevation,
required this.onPressed,
this.mouseCursor,
this.shape,
this.clipBehavior = Clip.none,
this.focusNode,
this.autofocus = false,
this.materialTapTargetSize,
this.enableFeedback,
}) : assert(elevation == null || elevation >= 0.0),
assert(focusElevation == null || focusElevation >= 0.0),
assert(hoverElevation == null || hoverElevation >= 0.0),
assert(highlightElevation == null || highlightElevation >= 0.0),
assert(disabledElevation == null || disabledElevation >= 0.0),
assert(clipBehavior != null),
assert(autofocus != null),
_floatingActionButtonType = _FloatingActionButtonType.small,
mini = true,
isExtended = false,
_extendedLabel = null,
extendedIconLabelSpacing = null,
super(key: key);
/// Creates a large circular floating action button.
///
/// This constructor overrides the default size constraints of the floating
/// action button.
///
/// The [clipBehavior] and [autofocus] arguments must not be null.
/// Additionally, [elevation], [focusElevation], [hoverElevation],
/// [highlightElevation], and [disabledElevation] (if specified) must be
/// non-negative.
const FloatingActionButton.large({
Key? key,
this.child,
this.tooltip,
this.foregroundColor,
this.backgroundColor,
this.focusColor,
this.hoverColor,
this.splashColor,
this.heroTag = const _DefaultHeroTag(),
this.elevation,
this.focusElevation,
this.hoverElevation,
this.highlightElevation,
this.disabledElevation,
required this.onPressed,
this.mouseCursor,
this.shape,
this.clipBehavior = Clip.none,
this.focusNode,
this.autofocus = false,
this.materialTapTargetSize,
this.enableFeedback,
}) : assert(elevation == null || elevation >= 0.0),
assert(focusElevation == null || focusElevation >= 0.0),
assert(hoverElevation == null || hoverElevation >= 0.0),
assert(highlightElevation == null || highlightElevation >= 0.0),
assert(disabledElevation == null || disabledElevation >= 0.0),
assert(clipBehavior != null),
assert(autofocus != null),
_floatingActionButtonType = _FloatingActionButtonType.large,
mini = false,
isExtended = false,
_extendedLabel = null,
extendedIconLabelSpacing = null,
super(key: key); super(key: key);
/// Creates a wider [StadiumBorder]-shaped floating action button with /// Creates a wider [StadiumBorder]-shaped floating action button with
...@@ -166,7 +271,7 @@ class FloatingActionButton extends StatelessWidget { ...@@ -166,7 +271,7 @@ class FloatingActionButton extends StatelessWidget {
/// The [label], [autofocus], and [clipBehavior] arguments must not be null. /// The [label], [autofocus], and [clipBehavior] arguments must not be null.
/// Additionally, [elevation], [highlightElevation], and [disabledElevation] /// Additionally, [elevation], [highlightElevation], and [disabledElevation]
/// (if specified) must be non-negative. /// (if specified) must be non-negative.
FloatingActionButton.extended({ const FloatingActionButton.extended({
Key? key, Key? key,
this.tooltip, this.tooltip,
this.foregroundColor, this.foregroundColor,
...@@ -188,6 +293,7 @@ class FloatingActionButton extends StatelessWidget { ...@@ -188,6 +293,7 @@ class FloatingActionButton extends StatelessWidget {
this.clipBehavior = Clip.none, this.clipBehavior = Clip.none,
this.focusNode, this.focusNode,
this.autofocus = false, this.autofocus = false,
this.extendedIconLabelSpacing,
Widget? icon, Widget? icon,
required Widget label, required Widget label,
this.enableFeedback, this.enableFeedback,
...@@ -199,30 +305,10 @@ class FloatingActionButton extends StatelessWidget { ...@@ -199,30 +305,10 @@ class FloatingActionButton extends StatelessWidget {
assert(isExtended != null), assert(isExtended != null),
assert(clipBehavior != null), assert(clipBehavior != null),
assert(autofocus != null), assert(autofocus != null),
_sizeConstraints = _kExtendedSizeConstraints,
mini = false, mini = false,
child = _ChildOverflowBox( _floatingActionButtonType = _FloatingActionButtonType.extended,
child: Row( child = icon,
mainAxisSize: MainAxisSize.min, _extendedLabel = label,
children: icon == null
? <Widget>[
const SizedBox(width: 20.0),
label,
const SizedBox(width: 20.0),
]
: !isExtended ? <Widget>[
const SizedBox(width: 20.0),
icon,
const SizedBox(width: 20.0),
] : <Widget>[
const SizedBox(width: 16.0),
icon,
const SizedBox(width: 8.0),
label,
const SizedBox(width: 20.0),
],
),
),
super(key: key); super(key: key);
/// The widget below this widget in the tree. /// The widget below this widget in the tree.
...@@ -424,7 +510,17 @@ class FloatingActionButton extends StatelessWidget { ...@@ -424,7 +510,17 @@ class FloatingActionButton extends StatelessWidget {
/// * [Feedback] for providing platform-specific feedback to certain actions. /// * [Feedback] for providing platform-specific feedback to certain actions.
final bool? enableFeedback; final bool? enableFeedback;
final BoxConstraints _sizeConstraints;
/// The spacing between the icon and the label for an extended
/// [FloatingActionButton].
///
/// If null, [FloatingActionButtonThemeData.extendedIconLabelSpacing] is used.
/// If that is also null, the default is 8.0.
final double? extendedIconLabelSpacing;
final _FloatingActionButtonType _floatingActionButtonType;
final Widget? _extendedLabel;
static const double _defaultElevation = 6; static const double _defaultElevation = 6;
static const double _defaultFocusElevation = 6; static const double _defaultFocusElevation = 6;
...@@ -480,6 +576,41 @@ class FloatingActionButton extends StatelessWidget { ...@@ -480,6 +576,41 @@ class FloatingActionButton extends StatelessWidget {
?? floatingActionButtonTheme.shape ?? floatingActionButtonTheme.shape
?? (isExtended ? _defaultExtendedShape : _defaultShape); ?? (isExtended ? _defaultExtendedShape : _defaultShape);
BoxConstraints sizeConstraints;
Widget? resolvedChild = child;
switch(_floatingActionButtonType) {
case _FloatingActionButtonType.regular:
sizeConstraints = floatingActionButtonTheme.sizeConstraints ?? _kSizeConstraints;
break;
case _FloatingActionButtonType.small:
sizeConstraints = floatingActionButtonTheme.smallSizeConstraints ?? _kMiniSizeConstraints;
break;
case _FloatingActionButtonType.large:
sizeConstraints = floatingActionButtonTheme.largeSizeConstraints ?? _kLargeSizeConstraints;
// The large FAB uses a larger icon.
resolvedChild = child != null ? IconTheme.merge(
data: const IconThemeData(size: 36.0),
child: child!,
) : child;
break;
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);
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],
),
);
break;
}
Widget result = RawMaterialButton( Widget result = RawMaterialButton(
onPressed: onPressed, onPressed: onPressed,
mouseCursor: mouseCursor, mouseCursor: mouseCursor,
...@@ -488,7 +619,7 @@ class FloatingActionButton extends StatelessWidget { ...@@ -488,7 +619,7 @@ class FloatingActionButton extends StatelessWidget {
hoverElevation: hoverElevation, hoverElevation: hoverElevation,
highlightElevation: highlightElevation, highlightElevation: highlightElevation,
disabledElevation: disabledElevation, disabledElevation: disabledElevation,
constraints: _sizeConstraints, constraints: sizeConstraints,
materialTapTargetSize: materialTapTargetSize, materialTapTargetSize: materialTapTargetSize,
fillColor: backgroundColor, fillColor: backgroundColor,
focusColor: focusColor, focusColor: focusColor,
...@@ -500,7 +631,7 @@ class FloatingActionButton extends StatelessWidget { ...@@ -500,7 +631,7 @@ class FloatingActionButton extends StatelessWidget {
focusNode: focusNode, focusNode: focusNode,
autofocus: autofocus, autofocus: autofocus,
enableFeedback: enableFeedback, enableFeedback: enableFeedback,
child: child, child: resolvedChild,
); );
if (tooltip != null) { if (tooltip != null) {
......
...@@ -43,6 +43,11 @@ class FloatingActionButtonThemeData with Diagnosticable { ...@@ -43,6 +43,11 @@ class FloatingActionButtonThemeData with Diagnosticable {
this.highlightElevation, this.highlightElevation,
this.shape, this.shape,
this.enableFeedback, this.enableFeedback,
this.sizeConstraints,
this.smallSizeConstraints,
this.largeSizeConstraints,
this.extendedSizeConstraints,
this.extendedIconLabelSpacing,
}); });
/// Color to be used for the unselected, enabled [FloatingActionButton]'s /// Color to be used for the unselected, enabled [FloatingActionButton]'s
...@@ -96,6 +101,22 @@ class FloatingActionButtonThemeData with Diagnosticable { ...@@ -96,6 +101,22 @@ class FloatingActionButtonThemeData with Diagnosticable {
/// ignored. /// ignored.
final bool? enableFeedback; final bool? enableFeedback;
/// Overrides the default size constraints for the [FloatingActionButton].
final BoxConstraints? sizeConstraints;
/// Overrides the default size constraints for [FloatingActionButton.small].
final BoxConstraints? smallSizeConstraints;
/// Overrides the default size constraints for [FloatingActionButton.large].
final BoxConstraints? largeSizeConstraints;
/// Overrides the default size constraints for [FloatingActionButton.extended].
final BoxConstraints? extendedSizeConstraints;
/// The spacing between the icon and the label for an extended
/// [FloatingActionButton].
final double? extendedIconLabelSpacing;
/// Creates a copy of this object with the given fields replaced with the /// Creates a copy of this object with the given fields replaced with the
/// new values. /// new values.
FloatingActionButtonThemeData copyWith({ FloatingActionButtonThemeData copyWith({
...@@ -111,6 +132,11 @@ class FloatingActionButtonThemeData with Diagnosticable { ...@@ -111,6 +132,11 @@ class FloatingActionButtonThemeData with Diagnosticable {
double? highlightElevation, double? highlightElevation,
ShapeBorder? shape, ShapeBorder? shape,
bool? enableFeedback, bool? enableFeedback,
BoxConstraints? sizeConstraints,
BoxConstraints? smallSizeConstraints,
BoxConstraints? largeSizeConstraints,
BoxConstraints? extendedSizeConstraints,
double? extendedIconLabelSpacing,
}) { }) {
return FloatingActionButtonThemeData( return FloatingActionButtonThemeData(
foregroundColor: foregroundColor ?? this.foregroundColor, foregroundColor: foregroundColor ?? this.foregroundColor,
...@@ -125,6 +151,11 @@ class FloatingActionButtonThemeData with Diagnosticable { ...@@ -125,6 +151,11 @@ class FloatingActionButtonThemeData with Diagnosticable {
highlightElevation: highlightElevation ?? this.highlightElevation, highlightElevation: highlightElevation ?? this.highlightElevation,
shape: shape ?? this.shape, shape: shape ?? this.shape,
enableFeedback: enableFeedback ?? this.enableFeedback, enableFeedback: enableFeedback ?? this.enableFeedback,
sizeConstraints: sizeConstraints ?? this.sizeConstraints,
smallSizeConstraints: smallSizeConstraints ?? this.smallSizeConstraints,
largeSizeConstraints: largeSizeConstraints ?? this.largeSizeConstraints,
extendedSizeConstraints: extendedSizeConstraints ?? this.extendedSizeConstraints,
extendedIconLabelSpacing: extendedIconLabelSpacing ?? this.extendedIconLabelSpacing,
); );
} }
...@@ -150,6 +181,11 @@ class FloatingActionButtonThemeData with Diagnosticable { ...@@ -150,6 +181,11 @@ class FloatingActionButtonThemeData with Diagnosticable {
highlightElevation: lerpDouble(a?.highlightElevation, b?.highlightElevation, t), highlightElevation: lerpDouble(a?.highlightElevation, b?.highlightElevation, t),
shape: ShapeBorder.lerp(a?.shape, b?.shape, t), shape: ShapeBorder.lerp(a?.shape, b?.shape, t),
enableFeedback: t < 0.5 ? a?.enableFeedback : b?.enableFeedback, enableFeedback: t < 0.5 ? a?.enableFeedback : b?.enableFeedback,
sizeConstraints: BoxConstraints.lerp(a?.sizeConstraints, b?.sizeConstraints, t),
smallSizeConstraints: BoxConstraints.lerp(a?.smallSizeConstraints, b?.smallSizeConstraints, t),
largeSizeConstraints: BoxConstraints.lerp(a?.largeSizeConstraints, b?.largeSizeConstraints, t),
extendedSizeConstraints: BoxConstraints.lerp(a?.extendedSizeConstraints, b?.extendedSizeConstraints, t),
extendedIconLabelSpacing: lerpDouble(a?.extendedIconLabelSpacing, b?.extendedIconLabelSpacing, t),
); );
} }
...@@ -168,6 +204,11 @@ class FloatingActionButtonThemeData with Diagnosticable { ...@@ -168,6 +204,11 @@ class FloatingActionButtonThemeData with Diagnosticable {
highlightElevation, highlightElevation,
shape, shape,
enableFeedback, enableFeedback,
sizeConstraints,
smallSizeConstraints,
largeSizeConstraints,
extendedSizeConstraints,
extendedIconLabelSpacing,
); );
} }
...@@ -189,25 +230,34 @@ class FloatingActionButtonThemeData with Diagnosticable { ...@@ -189,25 +230,34 @@ class FloatingActionButtonThemeData with Diagnosticable {
&& other.disabledElevation == disabledElevation && other.disabledElevation == disabledElevation
&& other.highlightElevation == highlightElevation && other.highlightElevation == highlightElevation
&& other.shape == shape && other.shape == shape
&& other.enableFeedback == enableFeedback; && other.enableFeedback == enableFeedback
&& other.sizeConstraints == sizeConstraints
&& other.smallSizeConstraints == smallSizeConstraints
&& other.largeSizeConstraints == largeSizeConstraints
&& other.extendedSizeConstraints == extendedSizeConstraints
&& other.extendedIconLabelSpacing == extendedIconLabelSpacing;
} }
@override @override
void debugFillProperties(DiagnosticPropertiesBuilder properties) { void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties); super.debugFillProperties(properties);
const FloatingActionButtonThemeData defaultData = FloatingActionButtonThemeData();
properties.add(ColorProperty('foregroundColor', foregroundColor, defaultValue: null));
properties.add(ColorProperty('foregroundColor', foregroundColor, defaultValue: defaultData.foregroundColor)); properties.add(ColorProperty('backgroundColor', backgroundColor, defaultValue: null));
properties.add(ColorProperty('backgroundColor', backgroundColor, defaultValue: defaultData.backgroundColor)); properties.add(ColorProperty('focusColor', focusColor, defaultValue: null));
properties.add(ColorProperty('focusColor', focusColor, defaultValue: defaultData.focusColor)); properties.add(ColorProperty('hoverColor', hoverColor, defaultValue: null));
properties.add(ColorProperty('hoverColor', hoverColor, defaultValue: defaultData.hoverColor)); properties.add(ColorProperty('splashColor', splashColor, defaultValue: null));
properties.add(ColorProperty('splashColor', splashColor, defaultValue: defaultData.splashColor)); properties.add(DoubleProperty('elevation', elevation, defaultValue: null));
properties.add(DoubleProperty('elevation', elevation, defaultValue: defaultData.elevation)); properties.add(DoubleProperty('focusElevation', focusElevation, defaultValue: null));
properties.add(DoubleProperty('focusElevation', focusElevation, defaultValue: defaultData.focusElevation)); properties.add(DoubleProperty('hoverElevation', hoverElevation, defaultValue: null));
properties.add(DoubleProperty('hoverElevation', hoverElevation, defaultValue: defaultData.hoverElevation)); properties.add(DoubleProperty('disabledElevation', disabledElevation, defaultValue: null));
properties.add(DoubleProperty('disabledElevation', disabledElevation, defaultValue: defaultData.disabledElevation)); properties.add(DoubleProperty('highlightElevation', highlightElevation, defaultValue: null));
properties.add(DoubleProperty('highlightElevation', highlightElevation, defaultValue: defaultData.highlightElevation)); properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null));
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: defaultData.shape)); properties.add(DiagnosticsProperty<bool>('enableFeedback', enableFeedback, defaultValue: null));
properties.add(DiagnosticsProperty<bool>('enableFeedback', enableFeedback, defaultValue: defaultData.enableFeedback)); properties.add(DiagnosticsProperty<BoxConstraints>('sizeConstraints', sizeConstraints, defaultValue: null));
properties.add(DiagnosticsProperty<BoxConstraints>('smallSizeConstraints', smallSizeConstraints, defaultValue: null));
properties.add(DiagnosticsProperty<BoxConstraints>('largeSizeConstraints', largeSizeConstraints, defaultValue: null));
properties.add(DiagnosticsProperty<BoxConstraints>('extendedSizeConstraints', extendedSizeConstraints, defaultValue: null));
properties.add(DoubleProperty('extendedIconLabelSpacing', extendedIconLabelSpacing, defaultValue: null));
} }
} }
...@@ -421,14 +421,14 @@ void main() { ...@@ -421,14 +421,14 @@ void main() {
expect(getRawMaterialButtonWidget().shape, const CircleBorder()); expect(getRawMaterialButtonWidget().shape, const CircleBorder());
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( const MaterialApp(
home: Scaffold( home: Scaffold(
floatingActionButton: FloatingActionButton.extended( floatingActionButton: FloatingActionButton.extended(
label: const SizedBox( label: SizedBox(
width: 100.0, width: 100.0,
child: Text('label'), child: Text('label'),
), ),
icon: const Icon(Icons.android), icon: Icon(Icons.android),
onPressed: null, onPressed: null,
), ),
), ),
...@@ -475,10 +475,10 @@ void main() { ...@@ -475,10 +475,10 @@ void main() {
} }
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( const MaterialApp(
home: Scaffold( home: Scaffold(
floatingActionButton: FloatingActionButton.extended( floatingActionButton: FloatingActionButton.extended(
label: const SizedBox( label: SizedBox(
width: 100.0, width: 100.0,
child: Text('label'), child: Text('label'),
), ),
...@@ -965,6 +965,60 @@ void main() { ...@@ -965,6 +965,60 @@ void main() {
expect(find.byKey(labelKey), findsNothing); expect(find.byKey(labelKey), findsNothing);
}); });
testWidgets('FloatingActionButton.small configures correct size', (WidgetTester tester) async {
final Key key = UniqueKey();
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
floatingActionButton: FloatingActionButton.small(
key: key,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
onPressed: null,
),
),
),
);
expect(tester.getSize(find.byKey(key)), const Size(40.0, 40.0));
});
testWidgets('FloatingActionButton.large configures correct size', (WidgetTester tester) async {
final Key key = UniqueKey();
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
floatingActionButton: FloatingActionButton.large(
key: key,
onPressed: null,
),
),
),
);
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 {
const Key iconKey = Key('icon');
const Key labelKey = Key('label');
const double spacing = 33.0;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
floatingActionButton: FloatingActionButton.extended(
label: const Text('', key: labelKey),
icon: const Icon(Icons.add, key: iconKey),
extendedIconLabelSpacing: spacing,
onPressed: () {},
),
),
),
);
expect(tester.getTopLeft(find.byKey(labelKey)).dx - tester.getTopRight(find.byKey(iconKey)).dx, spacing);
});
group('feedback', () { group('feedback', () {
late FeedbackTester feedback; late FeedbackTester feedback;
......
...@@ -32,6 +32,7 @@ void main() { ...@@ -32,6 +32,7 @@ void main() {
expect(_getRawMaterialButton(tester).highlightElevation, 12); expect(_getRawMaterialButton(tester).highlightElevation, 12);
expect(_getRawMaterialButton(tester).shape, const CircleBorder()); expect(_getRawMaterialButton(tester).shape, const CircleBorder());
expect(_getRawMaterialButton(tester).splashColor, ThemeData().splashColor); expect(_getRawMaterialButton(tester).splashColor, ThemeData().splashColor);
expect(_getRawMaterialButton(tester).constraints, const BoxConstraints.tightFor(width: 56.0, height: 56.0));
}); });
testWidgets('FloatingActionButtonThemeData values are used when no FloatingActionButton properties are specified', (WidgetTester tester) async { testWidgets('FloatingActionButtonThemeData values are used when no FloatingActionButton properties are specified', (WidgetTester tester) async {
...@@ -42,6 +43,7 @@ void main() { ...@@ -42,6 +43,7 @@ void main() {
const double disabledElevation = 1; const double disabledElevation = 1;
const double highlightElevation = 13; const double highlightElevation = 13;
const ShapeBorder shape = StadiumBorder(); const ShapeBorder shape = StadiumBorder();
const BoxConstraints constraints = BoxConstraints.tightFor(width: 100.0, height: 100.0);
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
theme: ThemeData().copyWith( theme: ThemeData().copyWith(
...@@ -53,6 +55,7 @@ void main() { ...@@ -53,6 +55,7 @@ void main() {
disabledElevation: disabledElevation, disabledElevation: disabledElevation,
highlightElevation: highlightElevation, highlightElevation: highlightElevation,
shape: shape, shape: shape,
sizeConstraints: constraints,
), ),
), ),
home: Scaffold( home: Scaffold(
...@@ -70,6 +73,7 @@ void main() { ...@@ -70,6 +73,7 @@ void main() {
expect(_getRawMaterialButton(tester).highlightElevation, highlightElevation); expect(_getRawMaterialButton(tester).highlightElevation, highlightElevation);
expect(_getRawMaterialButton(tester).shape, shape); expect(_getRawMaterialButton(tester).shape, shape);
expect(_getRawMaterialButton(tester).splashColor, splashColor); expect(_getRawMaterialButton(tester).splashColor, splashColor);
expect(_getRawMaterialButton(tester).constraints, constraints);
}); });
testWidgets('FloatingActionButton values take priority over FloatingActionButtonThemeData values when both properties are specified', (WidgetTester tester) async { testWidgets('FloatingActionButton values take priority over FloatingActionButtonThemeData values when both properties are specified', (WidgetTester tester) async {
...@@ -132,6 +136,96 @@ void main() { ...@@ -132,6 +136,96 @@ void main() {
expect(_getRawMaterialButton(tester).shape, customShape); expect(_getRawMaterialButton(tester).shape, customShape);
}); });
testWidgets('FloatingActionButton.small uses custom constraints when specified in the theme', (WidgetTester tester) async {
const BoxConstraints constraints = BoxConstraints.tightFor(width: 100.0, height: 100.0);
await tester.pumpWidget(MaterialApp(
theme: ThemeData().copyWith(
floatingActionButtonTheme: const FloatingActionButtonThemeData(
smallSizeConstraints: constraints,
),
),
home: Scaffold(
floatingActionButton: FloatingActionButton.small(
onPressed: () { },
child: const Icon(Icons.add),
),
),
));
expect(_getRawMaterialButton(tester).constraints, constraints);
});
testWidgets('FloatingActionButton.large uses custom constraints when specified in the theme', (WidgetTester tester) async {
const BoxConstraints constraints = BoxConstraints.tightFor(width: 100.0, height: 100.0);
await tester.pumpWidget(MaterialApp(
theme: ThemeData().copyWith(
floatingActionButtonTheme: const FloatingActionButtonThemeData(
largeSizeConstraints: constraints,
),
),
home: Scaffold(
floatingActionButton: FloatingActionButton.large(
onPressed: () { },
child: const Icon(Icons.add),
),
),
));
expect(_getRawMaterialButton(tester).constraints, constraints);
});
testWidgets('FloatingActionButton.extended uses custom constraints and spacing when specified in the theme', (WidgetTester tester) async {
const Key iconKey = Key('icon');
const Key labelKey = Key('label');
const BoxConstraints constraints = BoxConstraints.tightFor(height: 100.0);
const double spacing = 33.0;
await tester.pumpWidget(MaterialApp(
theme: ThemeData().copyWith(
floatingActionButtonTheme: const FloatingActionButtonThemeData(
extendedSizeConstraints: constraints,
extendedIconLabelSpacing: spacing,
),
),
home: Scaffold(
floatingActionButton: FloatingActionButton.extended(
onPressed: () { },
label: const Text('Extended', key: labelKey),
icon: const Icon(Icons.add, key: iconKey),
),
),
));
expect(_getRawMaterialButton(tester).constraints, constraints);
expect(tester.getTopLeft(find.byKey(labelKey)).dx - tester.getTopRight(find.byKey(iconKey)).dx, spacing);
});
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;
await tester.pumpWidget(MaterialApp(
theme: ThemeData().copyWith(
floatingActionButtonTheme: const FloatingActionButtonThemeData(
extendedIconLabelSpacing: 25.0,
),
),
home: Scaffold(
floatingActionButton: FloatingActionButton.extended(
onPressed: () { },
label: const Text('Extended', key: labelKey),
icon: const Icon(Icons.add, key: iconKey),
extendedIconLabelSpacing: spacing,
),
),
));
expect(tester.getTopLeft(find.byKey(labelKey)).dx - tester.getTopRight(find.byKey(iconKey)).dx, spacing);
});
testWidgets('default FloatingActionButton debugFillProperties', (WidgetTester tester) async { testWidgets('default FloatingActionButton debugFillProperties', (WidgetTester tester) async {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
const FloatingActionButtonThemeData ().debugFillProperties(builder); const FloatingActionButtonThemeData ().debugFillProperties(builder);
...@@ -147,12 +241,23 @@ void main() { ...@@ -147,12 +241,23 @@ void main() {
testWidgets('Material implements debugFillProperties', (WidgetTester tester) async { testWidgets('Material implements debugFillProperties', (WidgetTester tester) async {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
const FloatingActionButtonThemeData( const FloatingActionButtonThemeData(
backgroundColor: Color(0xCAFECAFE),
foregroundColor: Color(0xFEEDFEED), foregroundColor: Color(0xFEEDFEED),
backgroundColor: Color(0xCAFECAFE),
focusColor: Color(0xFEEDFEE1),
hoverColor: Color(0xFEEDFEE2),
splashColor: Color(0xFEEDFEE3),
elevation: 23, elevation: 23,
focusElevation: 9,
hoverElevation: 10,
disabledElevation: 11, disabledElevation: 11,
highlightElevation: 43, highlightElevation: 43,
shape: BeveledRectangleBorder(), shape: BeveledRectangleBorder(),
enableFeedback: true,
sizeConstraints: BoxConstraints.tightFor(width: 100.0, height: 100.0),
smallSizeConstraints: BoxConstraints.tightFor(width: 101.0, height: 101.0),
largeSizeConstraints: BoxConstraints.tightFor(width: 102.0, height: 102.0),
extendedSizeConstraints: BoxConstraints(minHeight: 103.0, maxHeight: 103.0),
extendedIconLabelSpacing: 12,
).debugFillProperties(builder); ).debugFillProperties(builder);
final List<String> description = builder.properties final List<String> description = builder.properties
...@@ -163,10 +268,21 @@ void main() { ...@@ -163,10 +268,21 @@ void main() {
expect(description, <String>[ expect(description, <String>[
'foregroundColor: Color(0xfeedfeed)', 'foregroundColor: Color(0xfeedfeed)',
'backgroundColor: Color(0xcafecafe)', 'backgroundColor: Color(0xcafecafe)',
'focusColor: Color(0xfeedfee1)',
'hoverColor: Color(0xfeedfee2)',
'splashColor: Color(0xfeedfee3)',
'elevation: 23.0', 'elevation: 23.0',
'focusElevation: 9.0',
'hoverElevation: 10.0',
'disabledElevation: 11.0', 'disabledElevation: 11.0',
'highlightElevation: 43.0', 'highlightElevation: 43.0',
'shape: BeveledRectangleBorder(BorderSide(Color(0xff000000), 0.0, BorderStyle.none), BorderRadius.zero)', 'shape: BeveledRectangleBorder(BorderSide(Color(0xff000000), 0.0, BorderStyle.none), BorderRadius.zero)',
'enableFeedback: true',
'sizeConstraints: BoxConstraints(w=100.0, h=100.0)',
'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'
]); ]);
}); });
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment