blob: 0f3f77edef1f9381800e982980baf13bed1e5ac1 [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:math' as math;
import 'dart:ui' show lerpDouble;
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'button_style.dart';
import 'button_style_button.dart';
import 'color_scheme.dart';
import 'colors.dart';
import 'constants.dart';
import 'filled_button_theme.dart';
import 'ink_well.dart';
import 'material_state.dart';
import 'theme.dart';
import 'theme_data.dart';
enum _FilledButtonVariant { filled, tonal }
/// A Material Design filled button.
///
/// Filled buttons have the most visual impact after the [FloatingActionButton],
/// and should be used for important, final actions that complete a flow,
/// like **Save**, **Join now**, or **Confirm**.
///
/// A filled button is a label [child] displayed on a [Material]
/// widget. The label's [Text] and [Icon] widgets are displayed in
/// [style]'s [ButtonStyle.foregroundColor] and the button's filled
/// background is the [ButtonStyle.backgroundColor].
///
/// The filled button's default style is defined by
/// [defaultStyleOf]. The style of this filled button can be
/// overridden with its [style] parameter. The style of all filled
/// buttons in a subtree can be overridden with the
/// [FilledButtonTheme], and the style of all of the filled
/// buttons in an app can be overridden with the [Theme]'s
/// [ThemeData.filledButtonTheme] property.
///
/// The static [styleFrom] method is a convenient way to create a
/// filled button [ButtonStyle] from simple values.
///
/// If [onPressed] and [onLongPress] callbacks are null, then the
/// button will be disabled.
///
/// To create a 'filled tonal' button, use [FilledButton.tonal].
///
/// {@tool dartpad}
/// This sample produces enabled and disabled filled and filled tonal
/// buttons.
///
/// ** See code in examples/api/lib/material/filled_button/filled_button.0.dart **
/// {@end-tool}
///
/// See also:
///
/// * [ElevatedButton], a filled button whose material elevates when pressed.
/// * [OutlinedButton], a button with an outlined border and no fill color.
/// * [TextButton], a button with no outline or fill color.
/// * <https://material.io/design/components/buttons.html>
/// * <https://m3.material.io/components/buttons>
class FilledButton extends ButtonStyleButton {
/// Create a FilledButton.
///
/// The [autofocus] and [clipBehavior] arguments must not be null.
const FilledButton({
super.key,
required super.onPressed,
super.onLongPress,
super.onHover,
super.onFocusChange,
super.style,
super.focusNode,
super.autofocus = false,
super.clipBehavior = Clip.none,
super.statesController,
required super.child,
}) : _variant = _FilledButtonVariant.filled;
/// Create a filled button from [icon] and [label].
///
/// The icon and label are arranged in a row with padding at the start and end
/// and a gap between them.
///
/// The [icon] and [label] arguments must not be null.
factory FilledButton.icon({
Key? key,
required VoidCallback? onPressed,
VoidCallback? onLongPress,
ValueChanged<bool>? onHover,
ValueChanged<bool>? onFocusChange,
ButtonStyle? style,
FocusNode? focusNode,
bool? autofocus,
Clip? clipBehavior,
MaterialStatesController? statesController,
required Widget icon,
required Widget label,
}) = _FilledButtonWithIcon;
/// Create a tonal variant of FilledButton.
///
/// A filled tonal button is an alternative middle ground between
/// [FilledButton] and [OutlinedButton]. They’re useful in contexts where
/// a lower-priority button requires slightly more emphasis than an
/// outline would give, such as "Next" in an onboarding flow.
///
/// The [autofocus] and [clipBehavior] arguments must not be null.
const FilledButton.tonal({
super.key,
required super.onPressed,
super.onLongPress,
super.onHover,
super.onFocusChange,
super.style,
super.focusNode,
super.autofocus = false,
super.clipBehavior = Clip.none,
super.statesController,
required super.child,
}) : _variant = _FilledButtonVariant.tonal;
/// Create a filled tonal button from [icon] and [label].
///
/// The icon and label are arranged in a row with padding at the start and end
/// and a gap between them.
///
/// The [icon] and [label] arguments must not be null.
factory FilledButton.tonalIcon({
Key? key,
required VoidCallback? onPressed,
VoidCallback? onLongPress,
ValueChanged<bool>? onHover,
ValueChanged<bool>? onFocusChange,
ButtonStyle? style,
FocusNode? focusNode,
bool? autofocus,
Clip? clipBehavior,
MaterialStatesController? statesController,
required Widget icon,
required Widget label,
}) {
return _FilledButtonWithIcon.tonal(
key: key,
onPressed: onPressed,
onLongPress: onLongPress,
onHover: onHover,
onFocusChange: onFocusChange,
style: style,
focusNode: focusNode,
autofocus: autofocus,
clipBehavior: clipBehavior,
statesController: statesController,
icon: icon,
label: label,
);
}
/// A static convenience method that constructs a filled button
/// [ButtonStyle] given simple values.
///
/// The [foregroundColor], and [disabledForegroundColor] colors are used to create a
/// [MaterialStateProperty] [ButtonStyle.foregroundColor] value. The
/// [backgroundColor] and [disabledBackgroundColor] are used to create a
/// [MaterialStateProperty] [ButtonStyle.backgroundColor] value.
///
/// The button's elevations are defined relative to the [elevation]
/// parameter. The disabled elevation is the same as the parameter
/// value, [elevation] + 2 is used when the button is hovered
/// or focused, and elevation + 6 is used when the button is pressed.
///
/// Similarly, the [enabledMouseCursor] and [disabledMouseCursor]
/// parameters are used to construct [ButtonStyle.mouseCursor].
///
/// All of the other parameters are either used directly or used to
/// create a [MaterialStateProperty] with a single value for all
/// states.
///
/// All parameters default to null, by default this method returns
/// a [ButtonStyle] that doesn't override anything.
///
/// For example, to override the default text and icon colors for a
/// [FilledButton], as well as its overlay color, with all of the
/// standard opacity adjustments for the pressed, focused, and
/// hovered states, one could write:
///
/// ```dart
/// FilledButton(
/// style: FilledButton.styleFrom(foregroundColor: Colors.green),
/// onPressed: () {},
/// child: const Text('Filled button'),
/// );
/// ```
///
/// or for a Filled tonal variant:
/// ```dart
/// FilledButton.tonal(
/// style: FilledButton.styleFrom(foregroundColor: Colors.green),
/// onPressed: () {},
/// child: const Text('Filled tonal button'),
/// );
/// ```
static ButtonStyle styleFrom({
Color? foregroundColor,
Color? backgroundColor,
Color? disabledForegroundColor,
Color? disabledBackgroundColor,
Color? shadowColor,
Color? surfaceTintColor,
double? elevation,
TextStyle? textStyle,
EdgeInsetsGeometry? padding,
Size? minimumSize,
Size? fixedSize,
Size? maximumSize,
BorderSide? side,
OutlinedBorder? shape,
MouseCursor? enabledMouseCursor,
MouseCursor? disabledMouseCursor,
VisualDensity? visualDensity,
MaterialTapTargetSize? tapTargetSize,
Duration? animationDuration,
bool? enableFeedback,
AlignmentGeometry? alignment,
InteractiveInkFeatureFactory? splashFactory,
}) {
final MaterialStateProperty<Color?>? backgroundColorProp =
(backgroundColor == null && disabledBackgroundColor == null)
? null
: _FilledButtonDefaultColor(backgroundColor, disabledBackgroundColor);
final Color? foreground = foregroundColor;
final Color? disabledForeground = disabledForegroundColor;
final MaterialStateProperty<Color?>? foregroundColorProp =
(foreground == null && disabledForeground == null)
? null
: _FilledButtonDefaultColor(foreground, disabledForeground);
final MaterialStateProperty<Color?>? overlayColor = (foreground == null)
? null
: _FilledButtonDefaultOverlay(foreground);
final MaterialStateProperty<MouseCursor?>? mouseCursor =
(enabledMouseCursor == null && disabledMouseCursor == null)
? null
: _FilledButtonDefaultMouseCursor(enabledMouseCursor, disabledMouseCursor);
return ButtonStyle(
textStyle: MaterialStatePropertyAll<TextStyle?>(textStyle),
backgroundColor: backgroundColorProp,
foregroundColor: foregroundColorProp,
overlayColor: overlayColor,
shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
surfaceTintColor: ButtonStyleButton.allOrNull<Color>(surfaceTintColor),
elevation: ButtonStyleButton.allOrNull(elevation),
padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
fixedSize: ButtonStyleButton.allOrNull<Size>(fixedSize),
maximumSize: ButtonStyleButton.allOrNull<Size>(maximumSize),
side: ButtonStyleButton.allOrNull<BorderSide>(side),
shape: ButtonStyleButton.allOrNull<OutlinedBorder>(shape),
mouseCursor: mouseCursor,
visualDensity: visualDensity,
tapTargetSize: tapTargetSize,
animationDuration: animationDuration,
enableFeedback: enableFeedback,
alignment: alignment,
splashFactory: splashFactory,
);
}
final _FilledButtonVariant _variant;
/// Defines the button's default appearance.
///
/// The button [child]'s [Text] and [Icon] widgets are rendered with
/// the [ButtonStyle]'s foreground color. The button's [InkWell] adds
/// the style's overlay color when the button is focused, hovered
/// or pressed. The button's background color becomes its [Material]
/// color.
///
/// All of the ButtonStyle's defaults appear below. In this list
/// "Theme.foo" is shorthand for `Theme.of(context).foo`. Color
/// scheme values like "onSurface(0.38)" are shorthand for
/// `onSurface.withOpacity(0.38)`. [MaterialStateProperty] valued
/// properties that are not followed by a sublist have the same
/// value for all states, otherwise the values are as specified for
/// each state, and "others" means all other states.
///
/// The `textScaleFactor` is the value of
/// `MediaQuery.textScaleFactorOf(context)` and the names of the
/// EdgeInsets constructors and `EdgeInsetsGeometry.lerp` have been
/// abbreviated for readability.
///
/// The color of the [ButtonStyle.textStyle] is not used, the
/// [ButtonStyle.foregroundColor] color is used instead.
///
/// * `textStyle` - Theme.textTheme.labelLarge
/// * `backgroundColor`
/// * disabled - Theme.colorScheme.onSurface(0.12)
/// * others - Theme.colorScheme.secondaryContainer
/// * `foregroundColor`
/// * disabled - Theme.colorScheme.onSurface(0.38)
/// * others - Theme.colorScheme.onSecondaryContainer
/// * `overlayColor`
/// * hovered - Theme.colorScheme.onSecondaryContainer(0.08)
/// * focused or pressed - Theme.colorScheme.onSecondaryContainer(0.12)
/// * `shadowColor` - Theme.colorScheme.shadow
/// * `surfaceTintColor` - null
/// * `elevation`
/// * disabled - 0
/// * default - 0
/// * hovered - 1
/// * focused or pressed - 0
/// * `padding`
/// * `textScaleFactor <= 1` - horizontal(16)
/// * `1 < textScaleFactor <= 2` - lerp(horizontal(16), horizontal(8))
/// * `2 < textScaleFactor <= 3` - lerp(horizontal(8), horizontal(4))
/// * `3 < textScaleFactor` - horizontal(4)
/// * `minimumSize` - Size(64, 40)
/// * `fixedSize` - null
/// * `maximumSize` - Size.infinite
/// * `side` - null
/// * `shape` - StadiumBorder()
/// * `mouseCursor`
/// * disabled - SystemMouseCursors.basic
/// * others - SystemMouseCursors.click
/// * `visualDensity` - Theme.visualDensity
/// * `tapTargetSize` - Theme.materialTapTargetSize
/// * `animationDuration` - kThemeChangeDuration
/// * `enableFeedback` - true
/// * `alignment` - Alignment.center
/// * `splashFactory` - Theme.splashFactory
///
/// The default padding values for the [FilledButton.icon] factory are slightly different:
///
/// * `padding`
/// * `textScaleFactor <= 1` - start(12) end(16)
/// * `1 < textScaleFactor <= 2` - lerp(start(12) end(16), horizontal(8))
/// * `2 < textScaleFactor <= 3` - lerp(horizontal(8), horizontal(4))
/// * `3 < textScaleFactor` - horizontal(4)
///
/// The default value for `side`, which defines the appearance of the button's
/// outline, is null. That means that the outline is defined by the button
/// shape's [OutlinedBorder.side]. Typically the default value of an
/// [OutlinedBorder]'s side is [BorderSide.none], so an outline is not drawn.
///
/// ## Material 3 defaults
///
/// If [ThemeData.useMaterial3] is set to true the following defaults will
/// be used:
///
/// * `textStyle` - Theme.textTheme.labelLarge
/// * `backgroundColor`
/// * disabled - Theme.colorScheme.onSurface(0.12)
/// * others - Theme.colorScheme.secondaryContainer
/// * `foregroundColor`
/// * disabled - Theme.colorScheme.onSurface(0.38)
/// * others - Theme.colorScheme.onSecondaryContainer
/// * `overlayColor`
/// * hovered - Theme.colorScheme.onSecondaryContainer(0.08)
/// * focused or pressed - Theme.colorScheme.onSecondaryContainer(0.12)
/// * `shadowColor` - Theme.colorScheme.shadow
/// * `surfaceTintColor` - Colors.transparent
/// * `elevation`
/// * disabled - 0
/// * default - 1
/// * hovered - 3
/// * focused or pressed - 1
/// * `padding`
/// * `textScaleFactor <= 1` - horizontal(24)
/// * `1 < textScaleFactor <= 2` - lerp(horizontal(24), horizontal(12))
/// * `2 < textScaleFactor <= 3` - lerp(horizontal(12), horizontal(6))
/// * `3 < textScaleFactor` - horizontal(6)
/// * `minimumSize` - Size(64, 40)
/// * `fixedSize` - null
/// * `maximumSize` - Size.infinite
/// * `side` - null
/// * `shape` - StadiumBorder()
/// * `mouseCursor`
/// * disabled - SystemMouseCursors.basic
/// * others - SystemMouseCursors.click
/// * `visualDensity` - Theme.visualDensity
/// * `tapTargetSize` - Theme.materialTapTargetSize
/// * `animationDuration` - kThemeChangeDuration
/// * `enableFeedback` - true
/// * `alignment` - Alignment.center
/// * `splashFactory` - Theme.splashFactory
///
/// For the [FilledButton.icon] factory, the start (generally the left) value of
/// [padding] is reduced from 24 to 16.
@override
ButtonStyle defaultStyleOf(BuildContext context) {
switch (_variant) {
case _FilledButtonVariant.filled:
return _FilledButtonDefaultsM3(context);
case _FilledButtonVariant.tonal:
return _FilledTonalButtonDefaultsM3(context);
}
}
/// Returns the [FilledButtonThemeData.style] of the closest
/// [FilledButtonTheme] ancestor.
@override
ButtonStyle? themeStyleOf(BuildContext context) {
return FilledButtonTheme.of(context).style;
}
}
EdgeInsetsGeometry _scaledPadding(BuildContext context) {
final bool useMaterial3 = Theme.of(context).useMaterial3;
final double padding1x = useMaterial3 ? 24.0 : 16.0;
return ButtonStyleButton.scaledPadding(
EdgeInsets.symmetric(horizontal: padding1x),
EdgeInsets.symmetric(horizontal: padding1x / 2),
EdgeInsets.symmetric(horizontal: padding1x / 2 / 2),
MediaQuery.textScaleFactorOf(context),
);
}
@immutable
class _FilledButtonDefaultColor extends MaterialStateProperty<Color?> with Diagnosticable {
_FilledButtonDefaultColor(this.color, this.disabled);
final Color? color;
final Color? disabled;
@override
Color? resolve(Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return disabled;
}
return color;
}
}
@immutable
class _FilledButtonDefaultOverlay extends MaterialStateProperty<Color?> with Diagnosticable {
_FilledButtonDefaultOverlay(this.overlay);
final Color overlay;
@override
Color? resolve(Set<MaterialState> states) {
if (states.contains(MaterialState.hovered)) {
return overlay.withOpacity(0.08);
}
if (states.contains(MaterialState.focused) || states.contains(MaterialState.pressed)) {
return overlay.withOpacity(0.12);
}
return null;
}
}
@immutable
class _FilledButtonDefaultMouseCursor extends MaterialStateProperty<MouseCursor?> with Diagnosticable {
_FilledButtonDefaultMouseCursor(this.enabledCursor, this.disabledCursor);
final MouseCursor? enabledCursor;
final MouseCursor? disabledCursor;
@override
MouseCursor? resolve(Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return disabledCursor;
}
return enabledCursor;
}
}
class _FilledButtonWithIcon extends FilledButton {
_FilledButtonWithIcon({
super.key,
required super.onPressed,
super.onLongPress,
super.onHover,
super.onFocusChange,
super.style,
super.focusNode,
bool? autofocus,
Clip? clipBehavior,
super.statesController,
required Widget icon,
required Widget label,
}) : assert(icon != null),
assert(label != null),
super(
autofocus: autofocus ?? false,
clipBehavior: clipBehavior ?? Clip.none,
child: _FilledButtonWithIconChild(icon: icon, label: label)
);
_FilledButtonWithIcon.tonal({
super.key,
required super.onPressed,
super.onLongPress,
super.onHover,
super.onFocusChange,
super.style,
super.focusNode,
bool? autofocus,
Clip? clipBehavior,
super.statesController,
required Widget icon,
required Widget label,
}) : assert(icon != null),
assert(label != null),
super.tonal(
autofocus: autofocus ?? false,
clipBehavior: clipBehavior ?? Clip.none,
child: _FilledButtonWithIconChild(icon: icon, label: label)
);
@override
ButtonStyle defaultStyleOf(BuildContext context) {
final bool useMaterial3 = Theme.of(context).useMaterial3;
final EdgeInsetsGeometry scaledPadding = useMaterial3 ? ButtonStyleButton.scaledPadding(
const EdgeInsetsDirectional.fromSTEB(16, 0, 24, 0),
const EdgeInsetsDirectional.fromSTEB(8, 0, 12, 0),
const EdgeInsetsDirectional.fromSTEB(4, 0, 6, 0),
MediaQuery.textScaleFactorOf(context),
) : ButtonStyleButton.scaledPadding(
const EdgeInsetsDirectional.fromSTEB(12, 0, 16, 0),
const EdgeInsets.symmetric(horizontal: 8),
const EdgeInsetsDirectional.fromSTEB(8, 0, 4, 0),
MediaQuery.textScaleFactorOf(context),
);
return super.defaultStyleOf(context).copyWith(
padding: MaterialStatePropertyAll<EdgeInsetsGeometry>(scaledPadding),
);
}
}
class _FilledButtonWithIconChild extends StatelessWidget {
const _FilledButtonWithIconChild({ required this.label, required this.icon });
final Widget label;
final Widget icon;
@override
Widget build(BuildContext context) {
final double scale = MediaQuery.textScaleFactorOf(context);
// Adjust the gap based on the text scale factor. Start at 8, and lerp
// to 4 based on how large the text is.
final double gap = scale <= 1 ? 8 : lerpDouble(8, 4, math.min(scale - 1, 1))!;
return Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[icon, SizedBox(width: gap), Flexible(child: label)],
);
}
}
// BEGIN GENERATED TOKEN PROPERTIES - FilledButton
// Do not edit by hand. The code between the "BEGIN GENERATED" and
// "END GENERATED" comments are generated from data in the Material
// Design token database by the script:
// dev/tools/gen_defaults/bin/gen_defaults.dart.
// Token database version: v0_150
class _FilledButtonDefaultsM3 extends ButtonStyle {
_FilledButtonDefaultsM3(this.context)
: super(
animationDuration: kThemeChangeDuration,
enableFeedback: true,
alignment: Alignment.center,
);
final BuildContext context;
late final ColorScheme _colors = Theme.of(context).colorScheme;
@override
MaterialStateProperty<TextStyle?> get textStyle =>
MaterialStatePropertyAll<TextStyle?>(Theme.of(context).textTheme.labelLarge);
@override
MaterialStateProperty<Color?>? get backgroundColor =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return _colors.onSurface.withOpacity(0.12);
}
return _colors.primary;
});
@override
MaterialStateProperty<Color?>? get foregroundColor =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return _colors.onSurface.withOpacity(0.38);
}
return _colors.onPrimary;
});
@override
MaterialStateProperty<Color?>? get overlayColor =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.hovered)) {
return _colors.onPrimary.withOpacity(0.08);
}
if (states.contains(MaterialState.focused)) {
return _colors.onPrimary.withOpacity(0.12);
}
if (states.contains(MaterialState.pressed)) {
return _colors.onPrimary.withOpacity(0.12);
}
return null;
});
@override
MaterialStateProperty<Color>? get shadowColor =>
MaterialStatePropertyAll<Color>(_colors.shadow);
@override
MaterialStateProperty<Color>? get surfaceTintColor =>
const MaterialStatePropertyAll<Color>(Colors.transparent);
@override
MaterialStateProperty<double>? get elevation =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return 0.0;
}
if (states.contains(MaterialState.hovered)) {
return 1.0;
}
if (states.contains(MaterialState.focused)) {
return 0.0;
}
if (states.contains(MaterialState.pressed)) {
return 0.0;
}
return 0.0;
});
@override
MaterialStateProperty<EdgeInsetsGeometry>? get padding =>
MaterialStatePropertyAll<EdgeInsetsGeometry>(_scaledPadding(context));
@override
MaterialStateProperty<Size>? get minimumSize =>
const MaterialStatePropertyAll<Size>(Size(64.0, 40.0));
// No default fixedSize
@override
MaterialStateProperty<Size>? get maximumSize =>
const MaterialStatePropertyAll<Size>(Size.infinite);
// No default side
@override
MaterialStateProperty<OutlinedBorder>? get shape =>
const MaterialStatePropertyAll<OutlinedBorder>(StadiumBorder());
@override
MaterialStateProperty<MouseCursor?>? get mouseCursor =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return SystemMouseCursors.basic;
}
return SystemMouseCursors.click;
});
@override
VisualDensity? get visualDensity => Theme.of(context).visualDensity;
@override
MaterialTapTargetSize? get tapTargetSize => Theme.of(context).materialTapTargetSize;
@override
InteractiveInkFeatureFactory? get splashFactory => Theme.of(context).splashFactory;
}
// END GENERATED TOKEN PROPERTIES - FilledButton
// BEGIN GENERATED TOKEN PROPERTIES - FilledTonalButton
// Do not edit by hand. The code between the "BEGIN GENERATED" and
// "END GENERATED" comments are generated from data in the Material
// Design token database by the script:
// dev/tools/gen_defaults/bin/gen_defaults.dart.
// Token database version: v0_150
class _FilledTonalButtonDefaultsM3 extends ButtonStyle {
_FilledTonalButtonDefaultsM3(this.context)
: super(
animationDuration: kThemeChangeDuration,
enableFeedback: true,
alignment: Alignment.center,
);
final BuildContext context;
late final ColorScheme _colors = Theme.of(context).colorScheme;
@override
MaterialStateProperty<TextStyle?> get textStyle =>
MaterialStatePropertyAll<TextStyle?>(Theme.of(context).textTheme.labelLarge);
@override
MaterialStateProperty<Color?>? get backgroundColor =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return _colors.onSurface.withOpacity(0.12);
}
return _colors.secondaryContainer;
});
@override
MaterialStateProperty<Color?>? get foregroundColor =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return _colors.onSurface.withOpacity(0.38);
}
return _colors.onSecondaryContainer;
});
@override
MaterialStateProperty<Color?>? get overlayColor =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.hovered)) {
return _colors.onSecondaryContainer.withOpacity(0.08);
}
if (states.contains(MaterialState.focused)) {
return _colors.onSecondaryContainer.withOpacity(0.12);
}
if (states.contains(MaterialState.pressed)) {
return _colors.onSecondaryContainer.withOpacity(0.12);
}
return null;
});
@override
MaterialStateProperty<Color>? get shadowColor =>
MaterialStatePropertyAll<Color>(_colors.shadow);
@override
MaterialStateProperty<Color>? get surfaceTintColor =>
const MaterialStatePropertyAll<Color>(Colors.transparent);
@override
MaterialStateProperty<double>? get elevation =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return 0.0;
}
if (states.contains(MaterialState.hovered)) {
return 1.0;
}
if (states.contains(MaterialState.focused)) {
return 0.0;
}
if (states.contains(MaterialState.pressed)) {
return 0.0;
}
return 0.0;
});
@override
MaterialStateProperty<EdgeInsetsGeometry>? get padding =>
MaterialStatePropertyAll<EdgeInsetsGeometry>(_scaledPadding(context));
@override
MaterialStateProperty<Size>? get minimumSize =>
const MaterialStatePropertyAll<Size>(Size(64.0, 40.0));
// No default fixedSize
@override
MaterialStateProperty<Size>? get maximumSize =>
const MaterialStatePropertyAll<Size>(Size.infinite);
// No default side
@override
MaterialStateProperty<OutlinedBorder>? get shape =>
const MaterialStatePropertyAll<OutlinedBorder>(StadiumBorder());
@override
MaterialStateProperty<MouseCursor?>? get mouseCursor =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return SystemMouseCursors.basic;
}
return SystemMouseCursors.click;
});
@override
VisualDensity? get visualDensity => Theme.of(context).visualDensity;
@override
MaterialTapTargetSize? get tapTargetSize => Theme.of(context).materialTapTargetSize;
@override
InteractiveInkFeatureFactory? get splashFactory => Theme.of(context).splashFactory;
}
// END GENERATED TOKEN PROPERTIES - FilledTonalButton