mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Migrate FloatingActionButton to Material 3 (#94486)
This commit is contained in:
parent
20ff180ae8
commit
2ab2ca2bc6
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"version": "v0.72",
|
"version": "v0.74",
|
||||||
"date": "2021-12-16 00:27:25.239571",
|
"date": "2022-01-06",
|
||||||
"md.sys.color.light.on-tertiary": "md.ref.palette.tertiary100",
|
"md.sys.color.light.on-tertiary": "md.ref.palette.tertiary100",
|
||||||
"md.sys.color.light.on-secondary-container": "md.ref.palette.secondary10",
|
"md.sys.color.light.on-secondary-container": "md.ref.palette.secondary10",
|
||||||
"md.sys.color.light.on-secondary": "md.ref.palette.secondary100",
|
"md.sys.color.light.on-secondary": "md.ref.palette.secondary100",
|
||||||
|
@ -9,31 +9,14 @@ import 'package:flutter/rendering.dart';
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
import 'button.dart';
|
import 'button.dart';
|
||||||
|
import 'color_scheme.dart';
|
||||||
import 'floating_action_button_theme.dart';
|
import 'floating_action_button_theme.dart';
|
||||||
import 'scaffold.dart';
|
import 'scaffold.dart';
|
||||||
|
import 'text_theme.dart';
|
||||||
import 'theme.dart';
|
import 'theme.dart';
|
||||||
import 'theme_data.dart';
|
import 'theme_data.dart';
|
||||||
import 'tooltip.dart';
|
import 'tooltip.dart';
|
||||||
|
|
||||||
const BoxConstraints _kSizeConstraints = BoxConstraints.tightFor(
|
|
||||||
width: 56.0,
|
|
||||||
height: 56.0,
|
|
||||||
);
|
|
||||||
|
|
||||||
const BoxConstraints _kMiniSizeConstraints = BoxConstraints.tightFor(
|
|
||||||
width: 40.0,
|
|
||||||
height: 40.0,
|
|
||||||
);
|
|
||||||
|
|
||||||
const BoxConstraints _kLargeSizeConstraints = BoxConstraints.tightFor(
|
|
||||||
width: 96.0,
|
|
||||||
height: 96.0,
|
|
||||||
);
|
|
||||||
|
|
||||||
const BoxConstraints _kExtendedSizeConstraints = BoxConstraints.tightFor(
|
|
||||||
height: 48.0,
|
|
||||||
);
|
|
||||||
|
|
||||||
class _DefaultHeroTag {
|
class _DefaultHeroTag {
|
||||||
const _DefaultHeroTag();
|
const _DefaultHeroTag();
|
||||||
@override
|
@override
|
||||||
@ -508,82 +491,80 @@ class FloatingActionButton extends StatelessWidget {
|
|||||||
|
|
||||||
final Widget? _extendedLabel;
|
final Widget? _extendedLabel;
|
||||||
|
|
||||||
static const double _defaultElevation = 6;
|
|
||||||
static const double _defaultFocusElevation = 6;
|
|
||||||
static const double _defaultHoverElevation = 8;
|
|
||||||
static const double _defaultHighlightElevation = 12;
|
|
||||||
static const ShapeBorder _defaultShape = CircleBorder();
|
|
||||||
static const ShapeBorder _defaultExtendedShape = StadiumBorder();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ThemeData theme = Theme.of(context);
|
final ThemeData theme = Theme.of(context);
|
||||||
final FloatingActionButtonThemeData floatingActionButtonTheme = theme.floatingActionButtonTheme;
|
final FloatingActionButtonThemeData floatingActionButtonTheme = theme.floatingActionButtonTheme;
|
||||||
|
final FloatingActionButtonThemeData defaults = theme.useMaterial3
|
||||||
|
? _M3Defaults(context, _floatingActionButtonType, child != null)
|
||||||
|
: _M2Defaults(context, _floatingActionButtonType, child != null);
|
||||||
|
|
||||||
final Color foregroundColor = this.foregroundColor
|
final Color foregroundColor = this.foregroundColor
|
||||||
?? floatingActionButtonTheme.foregroundColor
|
?? floatingActionButtonTheme.foregroundColor
|
||||||
?? theme.colorScheme.onSecondary;
|
?? defaults.foregroundColor!;
|
||||||
final Color backgroundColor = this.backgroundColor
|
final Color backgroundColor = this.backgroundColor
|
||||||
?? floatingActionButtonTheme.backgroundColor
|
?? floatingActionButtonTheme.backgroundColor
|
||||||
?? theme.colorScheme.secondary;
|
?? defaults.backgroundColor!;
|
||||||
final Color focusColor = this.focusColor
|
final Color focusColor = this.focusColor
|
||||||
?? floatingActionButtonTheme.focusColor
|
?? floatingActionButtonTheme.focusColor
|
||||||
?? theme.focusColor;
|
?? defaults.focusColor!;
|
||||||
final Color hoverColor = this.hoverColor
|
final Color hoverColor = this.hoverColor
|
||||||
?? floatingActionButtonTheme.hoverColor
|
?? floatingActionButtonTheme.hoverColor
|
||||||
?? theme.hoverColor;
|
?? defaults.hoverColor!;
|
||||||
final Color splashColor = this.splashColor
|
final Color splashColor = this.splashColor
|
||||||
?? floatingActionButtonTheme.splashColor
|
?? floatingActionButtonTheme.splashColor
|
||||||
?? theme.splashColor;
|
?? defaults.splashColor!;
|
||||||
final double elevation = this.elevation
|
final double elevation = this.elevation
|
||||||
?? floatingActionButtonTheme.elevation
|
?? floatingActionButtonTheme.elevation
|
||||||
?? _defaultElevation;
|
?? defaults.elevation!;
|
||||||
final double focusElevation = this.focusElevation
|
final double focusElevation = this.focusElevation
|
||||||
?? floatingActionButtonTheme.focusElevation
|
?? floatingActionButtonTheme.focusElevation
|
||||||
?? _defaultFocusElevation;
|
?? defaults.focusElevation!;
|
||||||
final double hoverElevation = this.hoverElevation
|
final double hoverElevation = this.hoverElevation
|
||||||
?? floatingActionButtonTheme.hoverElevation
|
?? floatingActionButtonTheme.hoverElevation
|
||||||
?? _defaultHoverElevation;
|
?? defaults.hoverElevation!;
|
||||||
final double disabledElevation = this.disabledElevation
|
final double disabledElevation = this.disabledElevation
|
||||||
?? floatingActionButtonTheme.disabledElevation
|
?? floatingActionButtonTheme.disabledElevation
|
||||||
|
?? defaults.disabledElevation
|
||||||
?? elevation;
|
?? elevation;
|
||||||
final double highlightElevation = this.highlightElevation
|
final double highlightElevation = this.highlightElevation
|
||||||
?? floatingActionButtonTheme.highlightElevation
|
?? floatingActionButtonTheme.highlightElevation
|
||||||
?? _defaultHighlightElevation;
|
?? defaults.highlightElevation!;
|
||||||
final MaterialTapTargetSize materialTapTargetSize = this.materialTapTargetSize
|
final MaterialTapTargetSize materialTapTargetSize = this.materialTapTargetSize
|
||||||
?? theme.materialTapTargetSize;
|
?? theme.materialTapTargetSize;
|
||||||
final bool enableFeedback = this.enableFeedback
|
final bool enableFeedback = this.enableFeedback
|
||||||
?? floatingActionButtonTheme.enableFeedback ?? true;
|
?? floatingActionButtonTheme.enableFeedback
|
||||||
|
?? defaults.enableFeedback!;
|
||||||
|
final double iconSize = floatingActionButtonTheme.iconSize
|
||||||
|
?? defaults.iconSize!;
|
||||||
final TextStyle extendedTextStyle = (this.extendedTextStyle
|
final TextStyle extendedTextStyle = (this.extendedTextStyle
|
||||||
?? floatingActionButtonTheme.extendedTextStyle
|
?? floatingActionButtonTheme.extendedTextStyle
|
||||||
?? theme.textTheme.button!.copyWith(letterSpacing: 1.2)).copyWith(color: foregroundColor);
|
?? defaults.extendedTextStyle!).copyWith(color: foregroundColor);
|
||||||
final ShapeBorder shape = this.shape
|
final ShapeBorder shape = this.shape
|
||||||
?? floatingActionButtonTheme.shape
|
?? floatingActionButtonTheme.shape
|
||||||
?? (isExtended ? _defaultExtendedShape : _defaultShape);
|
?? defaults.shape!;
|
||||||
|
|
||||||
BoxConstraints sizeConstraints;
|
BoxConstraints sizeConstraints;
|
||||||
Widget? resolvedChild = child;
|
Widget? resolvedChild = child != null ? IconTheme.merge(
|
||||||
|
data: IconThemeData(size: iconSize),
|
||||||
|
child: child!,
|
||||||
|
) : child;
|
||||||
switch(_floatingActionButtonType) {
|
switch(_floatingActionButtonType) {
|
||||||
case _FloatingActionButtonType.regular:
|
case _FloatingActionButtonType.regular:
|
||||||
sizeConstraints = floatingActionButtonTheme.sizeConstraints ?? _kSizeConstraints;
|
sizeConstraints = floatingActionButtonTheme.sizeConstraints ?? defaults.sizeConstraints!;
|
||||||
break;
|
break;
|
||||||
case _FloatingActionButtonType.small:
|
case _FloatingActionButtonType.small:
|
||||||
sizeConstraints = floatingActionButtonTheme.smallSizeConstraints ?? _kMiniSizeConstraints;
|
sizeConstraints = floatingActionButtonTheme.smallSizeConstraints ?? defaults.smallSizeConstraints!;
|
||||||
break;
|
break;
|
||||||
case _FloatingActionButtonType.large:
|
case _FloatingActionButtonType.large:
|
||||||
sizeConstraints = floatingActionButtonTheme.largeSizeConstraints ?? _kLargeSizeConstraints;
|
sizeConstraints = floatingActionButtonTheme.largeSizeConstraints ?? defaults.largeSizeConstraints!;
|
||||||
// The large FAB uses a larger icon.
|
|
||||||
resolvedChild = child != null ? IconTheme.merge(
|
|
||||||
data: const IconThemeData(size: 36.0),
|
|
||||||
child: child!,
|
|
||||||
) : child;
|
|
||||||
break;
|
break;
|
||||||
case _FloatingActionButtonType.extended:
|
case _FloatingActionButtonType.extended:
|
||||||
sizeConstraints = floatingActionButtonTheme.extendedSizeConstraints ?? _kExtendedSizeConstraints;
|
sizeConstraints = floatingActionButtonTheme.extendedSizeConstraints ?? defaults.extendedSizeConstraints!;
|
||||||
final double iconLabelSpacing = extendedIconLabelSpacing ?? floatingActionButtonTheme.extendedIconLabelSpacing ?? 8.0;
|
final double iconLabelSpacing = extendedIconLabelSpacing ?? floatingActionButtonTheme.extendedIconLabelSpacing ?? 8.0;
|
||||||
final EdgeInsetsGeometry padding = extendedPadding
|
final EdgeInsetsGeometry padding = extendedPadding
|
||||||
?? floatingActionButtonTheme.extendedPadding
|
?? floatingActionButtonTheme.extendedPadding
|
||||||
?? EdgeInsetsDirectional.only(start: child != null && isExtended ? 16.0 : 20.0, end: 20.0);
|
?? defaults.extendedPadding!;
|
||||||
resolvedChild = _ChildOverflowBox(
|
resolvedChild = _ChildOverflowBox(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: padding,
|
padding: padding,
|
||||||
@ -730,3 +711,148 @@ class _RenderChildOverflowBox extends RenderAligningShiftedBox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate a FloatingActionButtonThemeData that represents
|
||||||
|
// the M2 default values. This was generated by hand from the
|
||||||
|
// previous hand coded defaults for M2. It uses get method overrides
|
||||||
|
// instead of properties to avoid computing values that we may not
|
||||||
|
// need upfront.
|
||||||
|
class _M2Defaults extends FloatingActionButtonThemeData {
|
||||||
|
_M2Defaults(BuildContext context, this.type, this.hasChild)
|
||||||
|
: _theme = Theme.of(context),
|
||||||
|
_colors = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
|
final _FloatingActionButtonType type;
|
||||||
|
final bool hasChild;
|
||||||
|
final ThemeData _theme;
|
||||||
|
final ColorScheme _colors;
|
||||||
|
|
||||||
|
bool get _isExtended => type == _FloatingActionButtonType.extended;
|
||||||
|
bool get _isLarge => type == _FloatingActionButtonType.large;
|
||||||
|
|
||||||
|
@override Color? get foregroundColor => _colors.onSecondary;
|
||||||
|
@override Color? get backgroundColor => _colors.secondary;
|
||||||
|
@override Color? get focusColor => _theme.focusColor;
|
||||||
|
@override Color? get hoverColor => _theme.hoverColor;
|
||||||
|
@override Color? get splashColor => _theme.splashColor;
|
||||||
|
@override double? get elevation => 6;
|
||||||
|
@override double? get focusElevation => 6;
|
||||||
|
@override double? get hoverElevation => 8;
|
||||||
|
@override double? get highlightElevation => 12;
|
||||||
|
@override ShapeBorder? get shape => _isExtended ? const StadiumBorder() : const CircleBorder();
|
||||||
|
@override bool? get enableFeedback => true;
|
||||||
|
@override double? get iconSize => _isLarge ? 36.0 : 24.0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
BoxConstraints? get sizeConstraints => const BoxConstraints.tightFor(
|
||||||
|
width: 56.0,
|
||||||
|
height: 56.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
BoxConstraints? get smallSizeConstraints => const BoxConstraints.tightFor(
|
||||||
|
width: 40.0,
|
||||||
|
height: 40.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
BoxConstraints? get largeSizeConstraints => const BoxConstraints.tightFor(
|
||||||
|
width: 96.0,
|
||||||
|
height: 96.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
BoxConstraints? get extendedSizeConstraints => const BoxConstraints.tightFor(
|
||||||
|
height: 48.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override double? get extendedIconLabelSpacing => 8.0;
|
||||||
|
@override EdgeInsetsGeometry? get extendedPadding => EdgeInsetsDirectional.only(start: hasChild && _isExtended ? 16.0 : 20.0, end: 20.0);
|
||||||
|
@override TextStyle? get extendedTextStyle => _theme.textTheme.button!.copyWith(letterSpacing: 1.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BEGIN GENERATED TOKEN PROPERTIES
|
||||||
|
|
||||||
|
// Generated code to the end of this file. Do not edit by hand.
|
||||||
|
// These defaults are generated from the Material Design Token
|
||||||
|
// database by the script dev/tools/gen_defaults/bin/gen_defaults.dart.
|
||||||
|
|
||||||
|
// Generated version v0.74, 2022-01-06
|
||||||
|
class _M3Defaults extends FloatingActionButtonThemeData {
|
||||||
|
_M3Defaults(this.context, this.type, this.hasChild)
|
||||||
|
: _colors = Theme.of(context).colorScheme,
|
||||||
|
_textTheme = Theme.of(context).textTheme;
|
||||||
|
|
||||||
|
final BuildContext context;
|
||||||
|
final _FloatingActionButtonType type;
|
||||||
|
final bool hasChild;
|
||||||
|
final ColorScheme _colors;
|
||||||
|
final TextTheme _textTheme;
|
||||||
|
|
||||||
|
bool get _isExtended => type == _FloatingActionButtonType.extended;
|
||||||
|
|
||||||
|
@override Color? get foregroundColor => _colors.onPrimaryContainer;
|
||||||
|
@override Color? get backgroundColor => _colors.primaryContainer;
|
||||||
|
@override Color? get splashColor => _colors.onPrimaryContainer.withOpacity(0.12);
|
||||||
|
@override double get elevation => 6.0;
|
||||||
|
@override Color? get focusColor => _colors.onPrimaryContainer.withOpacity(0.12);
|
||||||
|
@override double get focusElevation => 6.0;
|
||||||
|
@override Color? get hoverColor => _colors.onPrimaryContainer.withOpacity(0.08);
|
||||||
|
@override double get hoverElevation => 8.0;
|
||||||
|
@override double get highlightElevation => 6.0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ShapeBorder? get shape {
|
||||||
|
switch (type) {
|
||||||
|
case _FloatingActionButtonType.regular:
|
||||||
|
return const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16.0)));
|
||||||
|
case _FloatingActionButtonType.small:
|
||||||
|
return const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12.0)));
|
||||||
|
case _FloatingActionButtonType.large:
|
||||||
|
return const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(28.0)));
|
||||||
|
case _FloatingActionButtonType.extended:
|
||||||
|
return const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16.0)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override bool? get enableFeedback => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
double? get iconSize {
|
||||||
|
switch (type) {
|
||||||
|
case _FloatingActionButtonType.regular: return 24.0;
|
||||||
|
case _FloatingActionButtonType.small: return 24.0;
|
||||||
|
case _FloatingActionButtonType.large: return 36.0;
|
||||||
|
case _FloatingActionButtonType.extended: return 24.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
BoxConstraints? get sizeConstraints => const BoxConstraints.tightFor(
|
||||||
|
width: 56.0,
|
||||||
|
height: 56.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
BoxConstraints? get smallSizeConstraints => const BoxConstraints.tightFor(
|
||||||
|
width: 40.0,
|
||||||
|
height: 40.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
BoxConstraints? get largeSizeConstraints => const BoxConstraints.tightFor(
|
||||||
|
width: 96.0,
|
||||||
|
height: 96.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
BoxConstraints? get extendedSizeConstraints => const BoxConstraints.tightFor(
|
||||||
|
height: 56.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override double? get extendedIconLabelSpacing => 8.0;
|
||||||
|
@override EdgeInsetsGeometry? get extendedPadding => EdgeInsetsDirectional.only(start: hasChild && _isExtended ? 16.0 : 20.0, end: 20.0);
|
||||||
|
@override TextStyle? get extendedTextStyle => _textTheme.labelLarge;
|
||||||
|
}
|
||||||
|
|
||||||
|
// END GENERATED TOKEN PROPERTIES
|
||||||
|
@ -43,6 +43,7 @@ class FloatingActionButtonThemeData with Diagnosticable {
|
|||||||
this.highlightElevation,
|
this.highlightElevation,
|
||||||
this.shape,
|
this.shape,
|
||||||
this.enableFeedback,
|
this.enableFeedback,
|
||||||
|
this.iconSize,
|
||||||
this.sizeConstraints,
|
this.sizeConstraints,
|
||||||
this.smallSizeConstraints,
|
this.smallSizeConstraints,
|
||||||
this.largeSizeConstraints,
|
this.largeSizeConstraints,
|
||||||
@ -103,6 +104,9 @@ class FloatingActionButtonThemeData with Diagnosticable {
|
|||||||
/// ignored.
|
/// ignored.
|
||||||
final bool? enableFeedback;
|
final bool? enableFeedback;
|
||||||
|
|
||||||
|
/// Overrides the default icon size for the [FloatingActionButton];
|
||||||
|
final double? iconSize;
|
||||||
|
|
||||||
/// Overrides the default size constraints for the [FloatingActionButton].
|
/// Overrides the default size constraints for the [FloatingActionButton].
|
||||||
final BoxConstraints? sizeConstraints;
|
final BoxConstraints? sizeConstraints;
|
||||||
|
|
||||||
@ -140,6 +144,7 @@ class FloatingActionButtonThemeData with Diagnosticable {
|
|||||||
double? highlightElevation,
|
double? highlightElevation,
|
||||||
ShapeBorder? shape,
|
ShapeBorder? shape,
|
||||||
bool? enableFeedback,
|
bool? enableFeedback,
|
||||||
|
double? iconSize,
|
||||||
BoxConstraints? sizeConstraints,
|
BoxConstraints? sizeConstraints,
|
||||||
BoxConstraints? smallSizeConstraints,
|
BoxConstraints? smallSizeConstraints,
|
||||||
BoxConstraints? largeSizeConstraints,
|
BoxConstraints? largeSizeConstraints,
|
||||||
@ -161,6 +166,7 @@ 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,
|
||||||
|
iconSize: iconSize ?? this.iconSize,
|
||||||
sizeConstraints: sizeConstraints ?? this.sizeConstraints,
|
sizeConstraints: sizeConstraints ?? this.sizeConstraints,
|
||||||
smallSizeConstraints: smallSizeConstraints ?? this.smallSizeConstraints,
|
smallSizeConstraints: smallSizeConstraints ?? this.smallSizeConstraints,
|
||||||
largeSizeConstraints: largeSizeConstraints ?? this.largeSizeConstraints,
|
largeSizeConstraints: largeSizeConstraints ?? this.largeSizeConstraints,
|
||||||
@ -193,6 +199,7 @@ 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,
|
||||||
|
iconSize: lerpDouble(a?.iconSize, b?.iconSize, t),
|
||||||
sizeConstraints: BoxConstraints.lerp(a?.sizeConstraints, b?.sizeConstraints, t),
|
sizeConstraints: BoxConstraints.lerp(a?.sizeConstraints, b?.sizeConstraints, t),
|
||||||
smallSizeConstraints: BoxConstraints.lerp(a?.smallSizeConstraints, b?.smallSizeConstraints, t),
|
smallSizeConstraints: BoxConstraints.lerp(a?.smallSizeConstraints, b?.smallSizeConstraints, t),
|
||||||
largeSizeConstraints: BoxConstraints.lerp(a?.largeSizeConstraints, b?.largeSizeConstraints, t),
|
largeSizeConstraints: BoxConstraints.lerp(a?.largeSizeConstraints, b?.largeSizeConstraints, t),
|
||||||
@ -218,6 +225,7 @@ class FloatingActionButtonThemeData with Diagnosticable {
|
|||||||
highlightElevation,
|
highlightElevation,
|
||||||
shape,
|
shape,
|
||||||
enableFeedback,
|
enableFeedback,
|
||||||
|
iconSize,
|
||||||
sizeConstraints,
|
sizeConstraints,
|
||||||
smallSizeConstraints,
|
smallSizeConstraints,
|
||||||
largeSizeConstraints,
|
largeSizeConstraints,
|
||||||
@ -247,6 +255,7 @@ class FloatingActionButtonThemeData with Diagnosticable {
|
|||||||
&& other.highlightElevation == highlightElevation
|
&& other.highlightElevation == highlightElevation
|
||||||
&& other.shape == shape
|
&& other.shape == shape
|
||||||
&& other.enableFeedback == enableFeedback
|
&& other.enableFeedback == enableFeedback
|
||||||
|
&& other.iconSize == iconSize
|
||||||
&& other.sizeConstraints == sizeConstraints
|
&& other.sizeConstraints == sizeConstraints
|
||||||
&& other.smallSizeConstraints == smallSizeConstraints
|
&& other.smallSizeConstraints == smallSizeConstraints
|
||||||
&& other.largeSizeConstraints == largeSizeConstraints
|
&& other.largeSizeConstraints == largeSizeConstraints
|
||||||
@ -272,6 +281,7 @@ class FloatingActionButtonThemeData with Diagnosticable {
|
|||||||
properties.add(DoubleProperty('highlightElevation', highlightElevation, defaultValue: null));
|
properties.add(DoubleProperty('highlightElevation', highlightElevation, defaultValue: null));
|
||||||
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null));
|
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null));
|
||||||
properties.add(DiagnosticsProperty<bool>('enableFeedback', enableFeedback, defaultValue: null));
|
properties.add(DiagnosticsProperty<bool>('enableFeedback', enableFeedback, defaultValue: null));
|
||||||
|
properties.add(DoubleProperty('iconSize', iconSize, defaultValue: null));
|
||||||
properties.add(DiagnosticsProperty<BoxConstraints>('sizeConstraints', sizeConstraints, defaultValue: null));
|
properties.add(DiagnosticsProperty<BoxConstraints>('sizeConstraints', sizeConstraints, defaultValue: null));
|
||||||
properties.add(DiagnosticsProperty<BoxConstraints>('smallSizeConstraints', smallSizeConstraints, defaultValue: null));
|
properties.add(DiagnosticsProperty<BoxConstraints>('smallSizeConstraints', smallSizeConstraints, defaultValue: null));
|
||||||
properties.add(DiagnosticsProperty<BoxConstraints>('largeSizeConstraints', largeSizeConstraints, defaultValue: null));
|
properties.add(DiagnosticsProperty<BoxConstraints>('largeSizeConstraints', largeSizeConstraints, defaultValue: null));
|
||||||
|
@ -1134,10 +1134,6 @@ class ThemeData with Diagnosticable {
|
|||||||
/// start using new colors, typography and other features of Material 3.
|
/// start using new colors, typography and other features of Material 3.
|
||||||
/// If false, they will use the Material 2 look and feel.
|
/// If false, they will use the Material 2 look and feel.
|
||||||
///
|
///
|
||||||
/// Currently no components have been migrated to support Material 3.
|
|
||||||
/// As they are updated to include Material 3 support this documentation
|
|
||||||
/// will be modified to indicate exactly what widgets this flag will affect.
|
|
||||||
///
|
|
||||||
/// During the migration to Material 3, turning this on may yield
|
/// During the migration to Material 3, turning this on may yield
|
||||||
/// inconsistent look and feel in your app. Some components will be migrated
|
/// inconsistent look and feel in your app. Some components will be migrated
|
||||||
/// before others and typography changes will be coming in stages.
|
/// before others and typography changes will be coming in stages.
|
||||||
@ -1148,6 +1144,10 @@ class ThemeData with Diagnosticable {
|
|||||||
/// all uses of it. Everything will use the Material 3 look and feel at
|
/// all uses of it. Everything will use the Material 3 look and feel at
|
||||||
/// that point.
|
/// that point.
|
||||||
///
|
///
|
||||||
|
/// Components that have been migrated to Material 3 are:
|
||||||
|
///
|
||||||
|
/// * [FloatingActionButton]
|
||||||
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
///
|
///
|
||||||
/// * [Material Design 3](https://m3.material.io/).
|
/// * [Material Design 3](https://m3.material.io/).
|
||||||
|
@ -18,6 +18,10 @@ import '../widgets/semantics_tester.dart';
|
|||||||
import 'feedback_tester.dart';
|
import 'feedback_tester.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
|
||||||
|
final ThemeData material3Theme = ThemeData.light().copyWith(useMaterial3: true);
|
||||||
|
final ThemeData material2Theme = ThemeData.light().copyWith(useMaterial3: false);
|
||||||
|
|
||||||
testWidgets('Floating Action Button control test', (WidgetTester tester) async {
|
testWidgets('Floating Action Button control test', (WidgetTester tester) async {
|
||||||
bool didPressButton = false;
|
bool didPressButton = false;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -171,6 +175,7 @@ void main() {
|
|||||||
testWidgets('Floating Action Button elevation when highlighted - effect', (WidgetTester tester) async {
|
testWidgets('Floating Action Button elevation when highlighted - effect', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
theme: material3Theme,
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
onPressed: () { },
|
onPressed: () { },
|
||||||
@ -183,7 +188,7 @@ void main() {
|
|||||||
await tester.pump();
|
await tester.pump();
|
||||||
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0);
|
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0);
|
||||||
await tester.pump(const Duration(seconds: 1));
|
await tester.pump(const Duration(seconds: 1));
|
||||||
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 12.0);
|
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0);
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
@ -195,7 +200,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 12.0);
|
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0);
|
||||||
await tester.pump(const Duration(seconds: 1));
|
await tester.pump(const Duration(seconds: 1));
|
||||||
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 20.0);
|
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 20.0);
|
||||||
await gesture.up();
|
await gesture.up();
|
||||||
@ -277,6 +282,7 @@ void main() {
|
|||||||
testWidgets('Floating Action Button elevation when disabled while highlighted - effect', (WidgetTester tester) async {
|
testWidgets('Floating Action Button elevation when disabled while highlighted - effect', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
theme: material3Theme,
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
onPressed: () { },
|
onPressed: () { },
|
||||||
@ -289,10 +295,11 @@ void main() {
|
|||||||
await tester.pump();
|
await tester.pump();
|
||||||
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0);
|
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0);
|
||||||
await tester.pump(const Duration(seconds: 1));
|
await tester.pump(const Duration(seconds: 1));
|
||||||
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 12.0);
|
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0);
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
const MaterialApp(
|
MaterialApp(
|
||||||
home: Scaffold(
|
theme: material3Theme,
|
||||||
|
home: const Scaffold(
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
onPressed: null,
|
onPressed: null,
|
||||||
),
|
),
|
||||||
@ -300,11 +307,12 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 12.0);
|
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0);
|
||||||
await tester.pump(const Duration(seconds: 1));
|
await tester.pump(const Duration(seconds: 1));
|
||||||
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0);
|
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0);
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
theme: material3Theme,
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
onPressed: () { },
|
onPressed: () { },
|
||||||
@ -323,6 +331,7 @@ void main() {
|
|||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
theme: material3Theme,
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
body: FloatingActionButton.extended(
|
body: FloatingActionButton.extended(
|
||||||
label: const Text('tooltip'),
|
label: const Text('tooltip'),
|
||||||
@ -359,7 +368,7 @@ void main() {
|
|||||||
await gesture.down(center);
|
await gesture.down(center);
|
||||||
await tester.pump(); // Start the splash and highlight animations.
|
await tester.pump(); // Start the splash and highlight animations.
|
||||||
await tester.pump(const Duration(milliseconds: 800)); // Wait for splash and highlight to be well under way.
|
await tester.pump(const Duration(milliseconds: 800)); // Wait for splash and highlight to be well under way.
|
||||||
expect(getFABWidget(fabFinder).elevation, 12);
|
expect(getFABWidget(fabFinder).elevation, 6);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('FlatActionButton mini size is configurable by ThemeData.materialTapTargetSize', (WidgetTester tester) async {
|
testWidgets('FlatActionButton mini size is configurable by ThemeData.materialTapTargetSize', (WidgetTester tester) async {
|
||||||
@ -402,8 +411,9 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('FloatingActionButton.isExtended', (WidgetTester tester) async {
|
testWidgets('FloatingActionButton.isExtended', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
const MaterialApp(
|
MaterialApp(
|
||||||
home: Scaffold(
|
theme: material3Theme,
|
||||||
|
home: const Scaffold(
|
||||||
floatingActionButton: FloatingActionButton(onPressed: null),
|
floatingActionButton: FloatingActionButton(onPressed: null),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -422,7 +432,10 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
expect(getFabWidget().isExtended, false);
|
expect(getFabWidget().isExtended, false);
|
||||||
expect(getRawMaterialButtonWidget().shape, const CircleBorder());
|
expect(
|
||||||
|
getRawMaterialButtonWidget().shape,
|
||||||
|
const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16.0)))
|
||||||
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
const MaterialApp(
|
const MaterialApp(
|
||||||
@ -440,13 +453,16 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(getFabWidget().isExtended, true);
|
expect(getFabWidget().isExtended, true);
|
||||||
expect(getRawMaterialButtonWidget().shape, const StadiumBorder());
|
expect(
|
||||||
|
getRawMaterialButtonWidget().shape,
|
||||||
|
const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16.0)))
|
||||||
|
);
|
||||||
expect(find.text('label'), findsOneWidget);
|
expect(find.text('label'), findsOneWidget);
|
||||||
expect(find.byType(Icon), findsOneWidget);
|
expect(find.byType(Icon), findsOneWidget);
|
||||||
|
|
||||||
// Verify that the widget's height is 48 and that its internal
|
// Verify that the widget's height is 56 and that its internal
|
||||||
/// horizontal layout is: 16 icon 8 label 20
|
/// horizontal layout is: 16 icon 8 label 20
|
||||||
expect(tester.getSize(fabFinder).height, 48.0);
|
expect(tester.getSize(fabFinder).height, 56.0);
|
||||||
|
|
||||||
final double fabLeft = tester.getTopLeft(fabFinder).dx;
|
final double fabLeft = tester.getTopLeft(fabFinder).dx;
|
||||||
final double fabRight = tester.getTopRight(fabFinder).dx;
|
final double fabRight = tester.getTopRight(fabFinder).dx;
|
||||||
@ -479,8 +495,9 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
const MaterialApp(
|
MaterialApp(
|
||||||
home: Scaffold(
|
theme: material3Theme,
|
||||||
|
home: const Scaffold(
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
label: SizedBox(
|
label: SizedBox(
|
||||||
width: 100.0,
|
width: 100.0,
|
||||||
@ -493,13 +510,16 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(getFabWidget().isExtended, true);
|
expect(getFabWidget().isExtended, true);
|
||||||
expect(getRawMaterialButtonWidget().shape, const StadiumBorder());
|
expect(
|
||||||
|
getRawMaterialButtonWidget().shape,
|
||||||
|
const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16.0)))
|
||||||
|
);
|
||||||
expect(find.text('label'), findsOneWidget);
|
expect(find.text('label'), findsOneWidget);
|
||||||
expect(find.byType(Icon), findsNothing);
|
expect(find.byType(Icon), findsNothing);
|
||||||
|
|
||||||
// Verify that the widget's height is 48 and that its internal
|
// Verify that the widget's height is 56 and that its internal
|
||||||
/// horizontal layout is: 20 label 20
|
/// horizontal layout is: 20 label 20
|
||||||
expect(tester.getSize(fabFinder).height, 48.0);
|
expect(tester.getSize(fabFinder).height, 56.0);
|
||||||
|
|
||||||
final double fabLeft = tester.getTopLeft(fabFinder).dx;
|
final double fabLeft = tester.getTopLeft(fabFinder).dx;
|
||||||
final double fabRight = tester.getTopRight(fabFinder).dx;
|
final double fabRight = tester.getTopRight(fabFinder).dx;
|
||||||
@ -770,6 +790,7 @@ void main() {
|
|||||||
final GlobalKey key = GlobalKey();
|
final GlobalKey key = GlobalKey();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
theme: material3Theme,
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
body: Center(
|
body: Center(
|
||||||
child: RepaintBoundary(
|
child: RepaintBoundary(
|
||||||
@ -1054,6 +1075,289 @@ void main() {
|
|||||||
expect(rawMaterialButton.textStyle, style.copyWith(color: const Color(0xffffffff)));
|
expect(rawMaterialButton.textStyle, style.copyWith(color: const Color(0xffffffff)));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('Material 2', () {
|
||||||
|
// Tests that are only relevant for Material 2. Once ThemeData.useMaterial3
|
||||||
|
// is turned on by default, these tests can be removed.
|
||||||
|
|
||||||
|
testWidgets('Floating Action Button elevation when highlighted - effect', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: material2Theme,
|
||||||
|
home: Scaffold(
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
onPressed: () { },
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0);
|
||||||
|
final TestGesture gesture = await tester.press(find.byType(PhysicalShape));
|
||||||
|
await tester.pump();
|
||||||
|
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0);
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 12.0);
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: material2Theme,
|
||||||
|
home: Scaffold(
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
onPressed: () { },
|
||||||
|
highlightElevation: 20.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 12.0);
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 20.0);
|
||||||
|
await gesture.up();
|
||||||
|
await tester.pump();
|
||||||
|
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 20.0);
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Floating Action Button elevation when disabled while highlighted - effect', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: material2Theme,
|
||||||
|
home: Scaffold(
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
onPressed: () { },
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0);
|
||||||
|
await tester.press(find.byType(PhysicalShape));
|
||||||
|
await tester.pump();
|
||||||
|
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0);
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 12.0);
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: material2Theme,
|
||||||
|
home: const Scaffold(
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
onPressed: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 12.0);
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0);
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: material2Theme,
|
||||||
|
home: Scaffold(
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
onPressed: () { },
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0);
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
expect(tester.widget<PhysicalShape>(find.byType(PhysicalShape)).elevation, 6.0);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Floating Action Button states elevation', (WidgetTester tester) async {
|
||||||
|
final FocusNode focusNode = FocusNode();
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: material2Theme,
|
||||||
|
home: Scaffold(
|
||||||
|
body: FloatingActionButton.extended(
|
||||||
|
label: const Text('tooltip'),
|
||||||
|
onPressed: () {},
|
||||||
|
focusNode: focusNode,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final Finder fabFinder = find.byType(PhysicalShape);
|
||||||
|
PhysicalShape getFABWidget(Finder finder) => tester.widget<PhysicalShape>(finder);
|
||||||
|
|
||||||
|
// Default, not disabled.
|
||||||
|
expect(getFABWidget(fabFinder).elevation, 6);
|
||||||
|
|
||||||
|
// Focused.
|
||||||
|
focusNode.requestFocus();
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(getFABWidget(fabFinder).elevation, 6);
|
||||||
|
|
||||||
|
// Hovered.
|
||||||
|
final Offset center = tester.getCenter(fabFinder);
|
||||||
|
final TestGesture gesture = await tester.createGesture(
|
||||||
|
kind: PointerDeviceKind.mouse,
|
||||||
|
);
|
||||||
|
await gesture.addPointer();
|
||||||
|
addTearDown(gesture.removePointer);
|
||||||
|
await gesture.moveTo(center);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(getFABWidget(fabFinder).elevation, 8);
|
||||||
|
|
||||||
|
// Highlighted (pressed).
|
||||||
|
await gesture.down(center);
|
||||||
|
await tester.pump(); // Start the splash and highlight animations.
|
||||||
|
await tester.pump(const Duration(milliseconds: 800)); // Wait for splash and highlight to be well under way.
|
||||||
|
expect(getFABWidget(fabFinder).elevation, 12);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('FloatingActionButton.isExtended', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: material2Theme,
|
||||||
|
home: const Scaffold(
|
||||||
|
floatingActionButton: FloatingActionButton(onPressed: null),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final Finder fabFinder = find.byType(FloatingActionButton);
|
||||||
|
|
||||||
|
FloatingActionButton getFabWidget() {
|
||||||
|
return tester.widget<FloatingActionButton>(fabFinder);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Finder materialButtonFinder = find.byType(RawMaterialButton);
|
||||||
|
|
||||||
|
RawMaterialButton getRawMaterialButtonWidget() {
|
||||||
|
return tester.widget<RawMaterialButton>(materialButtonFinder);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(getFabWidget().isExtended, false);
|
||||||
|
expect(getRawMaterialButtonWidget().shape, const CircleBorder());
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: material2Theme,
|
||||||
|
home: const Scaffold(
|
||||||
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
|
label: SizedBox(
|
||||||
|
width: 100.0,
|
||||||
|
child: Text('label'),
|
||||||
|
),
|
||||||
|
icon: Icon(Icons.android),
|
||||||
|
onPressed: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(getFabWidget().isExtended, true);
|
||||||
|
expect(getRawMaterialButtonWidget().shape, const StadiumBorder());
|
||||||
|
expect(find.text('label'), findsOneWidget);
|
||||||
|
expect(find.byType(Icon), findsOneWidget);
|
||||||
|
|
||||||
|
// Verify that the widget's height is 48 and that its internal
|
||||||
|
/// horizontal layout is: 16 icon 8 label 20
|
||||||
|
expect(tester.getSize(fabFinder).height, 48.0);
|
||||||
|
|
||||||
|
final double fabLeft = tester.getTopLeft(fabFinder).dx;
|
||||||
|
final double fabRight = tester.getTopRight(fabFinder).dx;
|
||||||
|
final double iconLeft = tester.getTopLeft(find.byType(Icon)).dx;
|
||||||
|
final double iconRight = tester.getTopRight(find.byType(Icon)).dx;
|
||||||
|
final double labelLeft = tester.getTopLeft(find.text('label')).dx;
|
||||||
|
final double labelRight = tester.getTopRight(find.text('label')).dx;
|
||||||
|
expect(iconLeft - fabLeft, 16.0);
|
||||||
|
expect(labelLeft - iconRight, 8.0);
|
||||||
|
expect(fabRight - labelRight, 20.0);
|
||||||
|
|
||||||
|
// The overall width of the button is:
|
||||||
|
// 168 = 16 + 24(icon) + 8 + 100(label) + 20
|
||||||
|
expect(tester.getSize(find.byType(Icon)).width, 24.0);
|
||||||
|
expect(tester.getSize(find.text('label')).width, 100.0);
|
||||||
|
expect(tester.getSize(fabFinder).width, 168);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('FloatingActionButton.isExtended (without icon)', (WidgetTester tester) async {
|
||||||
|
final Finder fabFinder = find.byType(FloatingActionButton);
|
||||||
|
|
||||||
|
FloatingActionButton getFabWidget() {
|
||||||
|
return tester.widget<FloatingActionButton>(fabFinder);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Finder materialButtonFinder = find.byType(RawMaterialButton);
|
||||||
|
|
||||||
|
RawMaterialButton getRawMaterialButtonWidget() {
|
||||||
|
return tester.widget<RawMaterialButton>(materialButtonFinder);
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: material2Theme,
|
||||||
|
home: const Scaffold(
|
||||||
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
|
label: SizedBox(
|
||||||
|
width: 100.0,
|
||||||
|
child: Text('label'),
|
||||||
|
),
|
||||||
|
onPressed: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(getFabWidget().isExtended, true);
|
||||||
|
expect(getRawMaterialButtonWidget().shape, const StadiumBorder());
|
||||||
|
expect(find.text('label'), findsOneWidget);
|
||||||
|
expect(find.byType(Icon), findsNothing);
|
||||||
|
|
||||||
|
// Verify that the widget's height is 48 and that its internal
|
||||||
|
/// horizontal layout is: 20 label 20
|
||||||
|
expect(tester.getSize(fabFinder).height, 48.0);
|
||||||
|
|
||||||
|
final double fabLeft = tester.getTopLeft(fabFinder).dx;
|
||||||
|
final double fabRight = tester.getTopRight(fabFinder).dx;
|
||||||
|
final double labelLeft = tester.getTopLeft(find.text('label')).dx;
|
||||||
|
final double labelRight = tester.getTopRight(find.text('label')).dx;
|
||||||
|
expect(labelLeft - fabLeft, 20.0);
|
||||||
|
expect(fabRight - labelRight, 20.0);
|
||||||
|
|
||||||
|
// The overall width of the button is:
|
||||||
|
// 140 = 20 + 100(label) + 20
|
||||||
|
expect(tester.getSize(find.text('label')).width, 100.0);
|
||||||
|
expect(tester.getSize(fabFinder).width, 140);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// This test prevents https://github.com/flutter/flutter/issues/20483
|
||||||
|
testWidgets('Floating Action Button clips ink splash and highlight', (WidgetTester tester) async {
|
||||||
|
final GlobalKey key = GlobalKey();
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: material2Theme,
|
||||||
|
home: Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: RepaintBoundary(
|
||||||
|
key: key,
|
||||||
|
child: FloatingActionButton(
|
||||||
|
onPressed: () { },
|
||||||
|
child: const Icon(Icons.add),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.press(find.byKey(key));
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(milliseconds: 1000));
|
||||||
|
await expectLater(
|
||||||
|
find.byKey(key),
|
||||||
|
matchesGoldenFile('floating_action_button_test_m2.clip.png'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
group('feedback', () {
|
group('feedback', () {
|
||||||
late FeedbackTester feedback;
|
late FeedbackTester feedback;
|
||||||
|
|
||||||
|
@ -33,6 +33,8 @@ void main() {
|
|||||||
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));
|
expect(_getRawMaterialButton(tester).constraints, const BoxConstraints.tightFor(width: 56.0, height: 56.0));
|
||||||
|
expect(_getIconSize(tester).width, 24.0);
|
||||||
|
expect(_getIconSize(tester).height, 24.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 {
|
||||||
@ -138,6 +140,7 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('FloatingActionButton.small uses custom constraints when specified in the theme', (WidgetTester tester) async {
|
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);
|
const BoxConstraints constraints = BoxConstraints.tightFor(width: 100.0, height: 100.0);
|
||||||
|
const double iconSize = 24.0;
|
||||||
|
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
theme: ThemeData().copyWith(
|
theme: ThemeData().copyWith(
|
||||||
@ -154,10 +157,13 @@ void main() {
|
|||||||
));
|
));
|
||||||
|
|
||||||
expect(_getRawMaterialButton(tester).constraints, constraints);
|
expect(_getRawMaterialButton(tester).constraints, constraints);
|
||||||
|
expect(_getIconSize(tester).width, iconSize);
|
||||||
|
expect(_getIconSize(tester).height, iconSize);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('FloatingActionButton.large uses custom constraints when specified in the theme', (WidgetTester tester) async {
|
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);
|
const BoxConstraints constraints = BoxConstraints.tightFor(width: 100.0, height: 100.0);
|
||||||
|
const double iconSize = 36.0;
|
||||||
|
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
theme: ThemeData().copyWith(
|
theme: ThemeData().copyWith(
|
||||||
@ -174,6 +180,8 @@ void main() {
|
|||||||
));
|
));
|
||||||
|
|
||||||
expect(_getRawMaterialButton(tester).constraints, constraints);
|
expect(_getRawMaterialButton(tester).constraints, constraints);
|
||||||
|
expect(_getIconSize(tester).width, iconSize);
|
||||||
|
expect(_getIconSize(tester).height, iconSize);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('FloatingActionButton.extended uses custom properties when specified in the theme', (WidgetTester tester) async {
|
testWidgets('FloatingActionButton.extended uses custom properties when specified in the theme', (WidgetTester tester) async {
|
||||||
@ -271,6 +279,7 @@ void main() {
|
|||||||
highlightElevation: 43,
|
highlightElevation: 43,
|
||||||
shape: BeveledRectangleBorder(),
|
shape: BeveledRectangleBorder(),
|
||||||
enableFeedback: true,
|
enableFeedback: true,
|
||||||
|
iconSize: 42,
|
||||||
sizeConstraints: BoxConstraints.tightFor(width: 100.0, height: 100.0),
|
sizeConstraints: BoxConstraints.tightFor(width: 100.0, height: 100.0),
|
||||||
smallSizeConstraints: BoxConstraints.tightFor(width: 101.0, height: 101.0),
|
smallSizeConstraints: BoxConstraints.tightFor(width: 101.0, height: 101.0),
|
||||||
largeSizeConstraints: BoxConstraints.tightFor(width: 102.0, height: 102.0),
|
largeSizeConstraints: BoxConstraints.tightFor(width: 102.0, height: 102.0),
|
||||||
@ -298,6 +307,7 @@ void main() {
|
|||||||
'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',
|
'enableFeedback: true',
|
||||||
|
'iconSize: 42.0',
|
||||||
'sizeConstraints: BoxConstraints(w=100.0, h=100.0)',
|
'sizeConstraints: BoxConstraints(w=100.0, h=100.0)',
|
||||||
'smallSizeConstraints: BoxConstraints(w=101.0, h=101.0)',
|
'smallSizeConstraints: BoxConstraints(w=101.0, h=101.0)',
|
||||||
'largeSizeConstraints: BoxConstraints(w=102.0, h=102.0)',
|
'largeSizeConstraints: BoxConstraints(w=102.0, h=102.0)',
|
||||||
@ -326,3 +336,15 @@ RichText _getRichText(WidgetTester tester) {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SizedBox _getIconSize(WidgetTester tester) {
|
||||||
|
return tester.widget<SizedBox>(
|
||||||
|
find.descendant(
|
||||||
|
of: find.descendant(
|
||||||
|
of: find.byType(FloatingActionButton),
|
||||||
|
matching: find.byType(Icon),
|
||||||
|
),
|
||||||
|
matching: find.byType(SizedBox),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user