blob: 3c61054c6ed79c97cab97a1bbf62133aff0fd2e9 [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 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'color_scheme.dart';
import 'colors.dart';
import 'constants.dart';
import 'flat_button.dart';
import 'material_button.dart';
import 'material_state.dart';
import 'outline_button.dart';
import 'raised_button.dart';
import 'theme.dart';
import 'theme_data.dart' show MaterialTapTargetSize;
/// Used with [ButtonTheme] and [ButtonThemeData] to define a button's base
/// colors, and the defaults for the button's minimum size, internal padding,
/// and shape.
///
/// See also:
///
/// * [RaisedButton], [FlatButton], [OutlineButton], which are configured
/// based on the ambient [ButtonTheme].
enum ButtonTextTheme {
/// Button text is black or white depending on [ThemeData.brightness].
normal,
/// Button text is [ThemeData.accentColor].
accent,
/// Button text is based on [ThemeData.primaryColor].
primary,
}
/// Used with [ButtonTheme] and [ButtonThemeData] to define how the button bar
/// should size itself with either constraints or internal padding.
enum ButtonBarLayoutBehavior {
/// Button bars will be constrained to a minimum height of 52.
///
/// This setting is require to create button bars which conform to the
/// material specification.
constrained,
/// Button bars will calculate their padding from the button theme padding.
padded,
}
/// Used with [ButtonThemeData] to configure the color and geometry of buttons.
///
/// A button theme can be specified as part of the overall Material theme
/// using [ThemeData.buttonTheme]. The Material theme's button theme data
/// can be overridden with [ButtonTheme].
///
/// The actual appearance of buttons depends on the button theme, the
/// button's enabled state, its elevation (if any), and the overall [Theme].
///
/// See also:
///
/// * [FlatButton] [RaisedButton], and [OutlineButton], which are styled
/// based on the ambient button theme.
/// * [RawMaterialButton], which can be used to configure a button that doesn't
/// depend on any inherited themes.
class ButtonTheme extends InheritedTheme {
/// Creates a button theme.
///
/// The [textTheme], [minWidth], [height], and [colorScheme] arguments
/// must not be null.
ButtonTheme({
Key key,
ButtonTextTheme textTheme = ButtonTextTheme.normal,
ButtonBarLayoutBehavior layoutBehavior = ButtonBarLayoutBehavior.padded,
double minWidth = 88.0,
double height = 36.0,
EdgeInsetsGeometry padding,
ShapeBorder shape,
bool alignedDropdown = false,
Color buttonColor,
Color disabledColor,
Color focusColor,
Color hoverColor,
Color highlightColor,
Color splashColor,
ColorScheme colorScheme,
MaterialTapTargetSize materialTapTargetSize,
Widget child,
}) : assert(textTheme != null),
assert(minWidth != null && minWidth >= 0.0),
assert(height != null && height >= 0.0),
assert(alignedDropdown != null),
assert(layoutBehavior != null),
data = ButtonThemeData(
textTheme: textTheme,
minWidth: minWidth,
height: height,
padding: padding,
shape: shape,
alignedDropdown: alignedDropdown,
layoutBehavior: layoutBehavior,
buttonColor: buttonColor,
disabledColor: disabledColor,
focusColor: focusColor,
hoverColor: hoverColor,
highlightColor: highlightColor,
splashColor: splashColor,
colorScheme: colorScheme,
materialTapTargetSize: materialTapTargetSize,
),
super(key: key, child: child);
/// Creates a button theme from [data].
///
/// The [data] argument must not be null.
const ButtonTheme.fromButtonThemeData({
Key key,
@required this.data,
Widget child,
}) : assert(data != null),
super(key: key, child: child);
// TODO(darrenaustin): remove after this deprecation warning has been on
// stable for a couple of releases.
// See https://github.com/flutter/flutter/issues/37333
//
/// Creates a button theme that is appropriate for button bars, as used in
/// dialog footers and in the headers of data tables.
///
/// Deprecated. Please use [ButtonBarTheme] instead which offers more
/// flexibility to configure [ButtonBar] widgets.
///
/// To migrate instances of code that were just wrapping a [ButtonBar]:
///
/// ```dart
/// ButtonTheme.bar(
/// child: ButtonBar(...)
/// );
/// ```
///
/// you can just remove the `ButtonTheme.bar` as the defaults are now handled
/// by [ButtonBar] directly.
///
/// If you have more complicated usages of `ButtonTheme.bar` like:
///
/// ```dart
/// ButtonTheme.bar(
/// padding: EdgeInsets.symmetric(horizontal: 10.0),
/// textTheme: ButtonTextTheme.accent,
/// child: ButtonBar(...),
/// );
/// ```
///
/// you can remove the `ButtonTheme.bar` and move the parameters to the
/// [ButtonBar] instance directly:
///
/// ```dart
/// ButtonBar(
/// padding: EdgeInsets.symmetric(horizontal: 10.0),
/// textTheme: ButtonTextTheme.accent,
/// ...
/// );
/// ```
///
/// You can also replace the defaults for all [ButtonBar] widgets by updating
/// [ThemeData.buttonBarTheme] for your app.
@Deprecated(
'Use ButtonBarTheme instead. '
'This feature was deprecated after v1.9.1.'
)
ButtonTheme.bar({
Key key,
ButtonTextTheme textTheme = ButtonTextTheme.accent,
double minWidth = 64.0,
double height = 36.0,
EdgeInsetsGeometry padding = const EdgeInsets.symmetric(horizontal: 8.0),
ShapeBorder shape,
bool alignedDropdown = false,
Color buttonColor,
Color disabledColor,
Color focusColor,
Color hoverColor,
Color highlightColor,
Color splashColor,
ColorScheme colorScheme,
Widget child,
ButtonBarLayoutBehavior layoutBehavior = ButtonBarLayoutBehavior.padded,
}) : assert(textTheme != null),
assert(minWidth != null && minWidth >= 0.0),
assert(height != null && height >= 0.0),
assert(alignedDropdown != null),
data = ButtonThemeData(
textTheme: textTheme,
minWidth: minWidth,
height: height,
padding: padding,
shape: shape,
alignedDropdown: alignedDropdown,
layoutBehavior: layoutBehavior,
buttonColor: buttonColor,
disabledColor: disabledColor,
focusColor: focusColor,
hoverColor: hoverColor,
highlightColor: highlightColor,
splashColor: splashColor,
colorScheme: colorScheme,
),
super(key: key, child: child);
/// Specifies the color and geometry of buttons.
final ButtonThemeData data;
/// The closest instance of this class that encloses the given context.
///
/// Typical usage is as follows:
///
/// ```dart
/// ButtonThemeData theme = ButtonTheme.of(context);
/// ```
static ButtonThemeData of(BuildContext context) {
final ButtonTheme inheritedButtonTheme = context.dependOnInheritedWidgetOfExactType<ButtonTheme>();
ButtonThemeData buttonTheme = inheritedButtonTheme?.data;
if (buttonTheme?.colorScheme == null) { // if buttonTheme or buttonTheme.colorScheme is null
final ThemeData theme = Theme.of(context);
buttonTheme ??= theme.buttonTheme;
if (buttonTheme.colorScheme == null) {
buttonTheme = buttonTheme.copyWith(
colorScheme: theme.buttonTheme.colorScheme ?? theme.colorScheme,
);
assert(buttonTheme.colorScheme != null);
}
}
return buttonTheme;
}
@override
Widget wrap(BuildContext context, Widget child) {
final ButtonTheme ancestorTheme = context.findAncestorWidgetOfExactType<ButtonTheme>();
return identical(this, ancestorTheme) ? child : ButtonTheme.fromButtonThemeData(data: data, child: child);
}
@override
bool updateShouldNotify(ButtonTheme oldWidget) => data != oldWidget.data;
}
/// Used with [ButtonTheme] to configure the color and geometry of buttons.
///
/// A button theme can be specified as part of the overall Material theme
/// using [ThemeData.buttonTheme]. The Material theme's button theme data
/// can be overridden with [ButtonTheme].
class ButtonThemeData with Diagnosticable {
/// Create a button theme object that can be used with [ButtonTheme]
/// or [ThemeData].
///
/// The [textTheme], [minWidth], [height], [alignedDropDown], and
/// [layoutBehavior] parameters must not be null. The [minWidth] and
/// [height] parameters must greater than or equal to zero.
///
/// The ButtonTheme's methods that have a [MaterialButton] parameter and
/// have a name with a `get` prefix are used by [RaisedButton],
/// [OutlineButton], and [FlatButton] to configure a [RawMaterialButton].
const ButtonThemeData({
this.textTheme = ButtonTextTheme.normal,
this.minWidth = 88.0,
this.height = 36.0,
EdgeInsetsGeometry padding,
ShapeBorder shape,
this.layoutBehavior = ButtonBarLayoutBehavior.padded,
this.alignedDropdown = false,
Color buttonColor,
Color disabledColor,
Color focusColor,
Color hoverColor,
Color highlightColor,
Color splashColor,
this.colorScheme,
MaterialTapTargetSize materialTapTargetSize,
}) : assert(textTheme != null),
assert(minWidth != null && minWidth >= 0.0),
assert(height != null && height >= 0.0),
assert(alignedDropdown != null),
assert(layoutBehavior != null),
_buttonColor = buttonColor,
_disabledColor = disabledColor,
_focusColor = focusColor,
_hoverColor = hoverColor,
_highlightColor = highlightColor,
_splashColor = splashColor,
_padding = padding,
_shape = shape,
_materialTapTargetSize = materialTapTargetSize;
/// The minimum width for buttons.
///
/// The actual horizontal space allocated for a button's child is
/// at least this value less the theme's horizontal [padding].
///
/// Defaults to 88.0 logical pixels.
final double minWidth;
/// The minimum height for buttons.
///
/// Defaults to 36.0 logical pixels.
final double height;
/// Defines a button's base colors, and the defaults for the button's minimum
/// size, internal padding, and shape.
///
/// Despite the name, this property is not a [TextTheme], its value is not a
/// collection of [TextStyle]s.
final ButtonTextTheme textTheme;
/// Defines whether a [ButtonBar] should size itself with a minimum size
/// constraint or with padding.
///
/// Defaults to [ButtonBarLayoutBehavior.padded].
final ButtonBarLayoutBehavior layoutBehavior;
/// Simply a convenience that returns [minWidth] and [height] as a
/// [BoxConstraints] object:
///
/// ```dart
/// return BoxConstraints(
/// minWidth: minWidth,
/// minHeight: height,
/// );
/// ```
BoxConstraints get constraints {
return BoxConstraints(
minWidth: minWidth,
minHeight: height,
);
}
/// Padding for a button's child (typically the button's label).
///
/// Defaults to 24.0 on the left and right if [textTheme] is
/// [ButtonTextTheme.primary], 16.0 on the left and right otherwise.
///
/// See also:
///
/// * [getPadding], which is used by [RaisedButton], [OutlineButton]
/// and [FlatButton].
EdgeInsetsGeometry get padding {
if (_padding != null)
return _padding;
switch (textTheme) {
case ButtonTextTheme.normal:
case ButtonTextTheme.accent:
return const EdgeInsets.symmetric(horizontal: 16.0);
case ButtonTextTheme.primary:
return const EdgeInsets.symmetric(horizontal: 24.0);
}
assert(false);
return EdgeInsets.zero;
}
final EdgeInsetsGeometry _padding;
/// The shape of a button's material.
///
/// The button's highlight and splash are clipped to this shape. If the
/// button has an elevation, then its drop shadow is defined by this
/// shape as well.
///
/// Defaults to a rounded rectangle with circular corner radii of 4.0 if
/// [textTheme] is [ButtonTextTheme.primary], a rounded rectangle with
/// circular corner radii of 2.0 otherwise.
///
/// See also:
///
/// * [getShape], which is used by [RaisedButton], [OutlineButton]
/// and [FlatButton].
ShapeBorder get shape {
if (_shape != null)
return _shape;
switch (textTheme) {
case ButtonTextTheme.normal:
case ButtonTextTheme.accent:
return const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(2.0)),
);
case ButtonTextTheme.primary:
return const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(4.0)),
);
}
return const RoundedRectangleBorder();
}
final ShapeBorder _shape;
/// If true, then a [DropdownButton] menu's width will match the button's
/// width.
///
/// If false (the default), then the dropdown's menu will be wider than
/// its button. In either case the dropdown button will line up the leading
/// edge of the menu's value with the leading edge of the values
/// displayed by the menu items.
///
/// This property only affects [DropdownButton] and its menu.
final bool alignedDropdown;
/// The background fill color for [RaisedButton]s.
///
/// This property is null by default.
///
/// If the button is in the focused, hovering, or highlighted state, then the
/// [focusColor], [hoverColor], or [highlightColor] will take precedence over
/// the [focusColor].
///
/// See also:
///
/// * [getFillColor], which is used by [RaisedButton] to compute its
/// background fill color.
final Color _buttonColor;
/// The background fill color for disabled [RaisedButton]s.
///
/// This property is null by default.
///
/// See also:
///
/// * [getDisabledFillColor], which is used by [RaisedButton] to compute its
/// background fill color.
final Color _disabledColor;
/// The fill color of the button when it has the input focus.
///
/// This property is null by default.
///
/// If the button is in the hovering or highlighted state, then the [hoverColor]
/// or [highlightColor] will take precedence over the [focusColor].
///
/// See also:
///
/// * [getFocusColor], which is used by [RaisedButton], [OutlineButton]
/// and [FlatButton].
final Color _focusColor;
/// The fill color of the button when a pointer is hovering over it.
///
/// This property is null by default.
///
/// If the button is in the highlighted state, then the [highlightColor] will
/// take precedence over the [hoverColor].
///
/// See also:
///
/// * [getHoverColor], which is used by [RaisedButton], [OutlineButton]
/// and [FlatButton].
final Color _hoverColor;
/// The color of the overlay that appears when a button is pressed.
///
/// This property is null by default.
///
/// See also:
///
/// * [getHighlightColor], which is used by [RaisedButton], [OutlineButton]
/// and [FlatButton].
final Color _highlightColor;
/// The color of the ink "splash" overlay that appears when a button is tapped.
///
/// This property is null by default.
///
/// See also:
///
/// * [getSplashColor], which is used by [RaisedButton], [OutlineButton]
/// and [FlatButton].
final Color _splashColor;
/// A set of thirteen colors that can be used to derive the button theme's
/// colors.
///
/// This property was added much later than the theme's set of highly
/// specific colors, like [ThemeData.buttonColor], [ThemeData.highlightColor],
/// [ThemeData.splashColor] etc.
///
/// The colors for new button classes can be defined exclusively in terms
/// of [colorScheme]. When it's possible, the existing buttons will
/// (continue to) gradually migrate to it.
final ColorScheme colorScheme;
// The minimum size of a button's tap target.
//
// This property is null by default.
//
// See also:
//
// * [getMaterialTargetTapSize], which is used by [RaisedButton],
// [OutlineButton] and [FlatButton].
final MaterialTapTargetSize _materialTapTargetSize;
/// The [button]'s overall brightness.
///
/// Returns the button's [MaterialButton.colorBrightness] if it is non-null,
/// otherwise the color scheme's [ColorScheme.brightness] is returned.
Brightness getBrightness(MaterialButton button) {
return button.colorBrightness ?? colorScheme.brightness;
}
/// Defines the [button]'s base colors, and the defaults for the button's
/// minimum size, internal padding, and shape.
///
/// Despite the name, this property is not the [TextTheme] whose
/// [TextTheme.button] is used as the button text's [TextStyle].
ButtonTextTheme getTextTheme(MaterialButton button) {
return button.textTheme ?? textTheme;
}
/// The foreground color of the [button]'s text and icon when
/// [MaterialButton.onPressed] is null (when MaterialButton.enabled is false).
///
/// Returns the button's [MaterialButton.disabledColor] if it is non-null.
/// Otherwise the color scheme's [ColorScheme.onSurface] color is returned
/// with its opacity set to 0.38.
///
/// If [MaterialButton.textColor] is a [MaterialStateProperty<Color>], it will be
/// used as the `disabledTextColor`. It will be resolved in the [MaterialState.disabled] state.
Color getDisabledTextColor(MaterialButton button) {
if (button.textColor is MaterialStateProperty<Color>)
return button.textColor;
if (button.disabledTextColor != null)
return button.disabledTextColor;
return colorScheme.onSurface.withOpacity(0.38);
}
/// The [button]'s background color when [MaterialButton.onPressed] is null
/// (when [MaterialButton.enabled] is false).
///
/// Returns the button's [MaterialButton.disabledColor] if it is non-null.
///
/// Otherwise the value of the `disabledColor` constructor parameter
/// is returned, if it is non-null.
///
/// Otherwise the color scheme's [ColorScheme.onSurface] color is returned
/// with its opacity set to 0.38.
Color getDisabledFillColor(MaterialButton button) {
if (button.disabledColor != null)
return button.disabledColor;
if (_disabledColor != null)
return _disabledColor;
return colorScheme.onSurface.withOpacity(0.38);
}
/// The button's background fill color or null for buttons that don't have
/// a background color.
///
/// Returns [MaterialButton.color] if it is non-null and the button
/// is enabled.
///
/// Otherwise, returns [MaterialButton.disabledColor] if it is non-null and
/// the button is disabled.
///
/// Otherwise, if button is a [FlatButton] or an [OutlineButton] then null is
/// returned.
///
/// Otherwise, if button is a [RaisedButton], returns the `buttonColor`
/// constructor parameter if it was non-null and the button is enabled.
///
/// Otherwise the fill color depends on the value of [getTextTheme].
///
/// * [ButtonTextTheme.normal] or [ButtonTextTheme.accent], the
/// color scheme's [ColorScheme.primary] color if the [button] is enabled
/// the value of [getDisabledFillColor] otherwise.
/// * [ButtonTextTheme.primary], if the [button] is enabled then the value
/// of the `buttonColor` constructor parameter if it is non-null,
/// otherwise the color scheme's ColorScheme.primary color. If the button
/// is not enabled then the colorScheme's [ColorScheme.onSurface] color
/// with opacity 0.12.
Color getFillColor(MaterialButton button) {
final Color fillColor = button.enabled ? button.color : button.disabledColor;
if (fillColor != null)
return fillColor;
if (button is FlatButton || button is OutlineButton || button.runtimeType == MaterialButton)
return null;
if (button.enabled && button is RaisedButton && _buttonColor != null)
return _buttonColor;
switch (getTextTheme(button)) {
case ButtonTextTheme.normal:
case ButtonTextTheme.accent:
return button.enabled ? colorScheme.primary : getDisabledFillColor(button);
case ButtonTextTheme.primary:
return button.enabled
? _buttonColor ?? colorScheme.primary
: colorScheme.onSurface.withOpacity(0.12);
}
assert(false);
return null;
}
/// The foreground color of the [button]'s text and icon.
///
/// If [button] is not [MaterialButton.enabled], the value of
/// [getDisabledTextColor] is returned. If the button is enabled and
/// [buttonTextColor] is non-null, then [buttonTextColor] is returned.
///
/// Otherwise the text color depends on the value of [getTextTheme]
/// and [getBrightness].
///
/// * [ButtonTextTheme.normal]: [Colors.white] is used if [getBrightness]
/// resolves to [Brightness.dark]. [Colors.black87] is used if
/// [getBrightness] resolves to [Brightness.light].
/// * [ButtonTextTheme.accent]: [colorScheme.secondary].
/// * [ButtonTextTheme.primary]: If [getFillColor] is dark then [Colors.white],
/// otherwise if [button] is a [FlatButton] or an [OutlineButton] then
/// [colorScheme.primary], otherwise [Colors.black].
Color getTextColor(MaterialButton button) {
if (!button.enabled)
return getDisabledTextColor(button);
if (button.textColor != null)
return button.textColor;
switch (getTextTheme(button)) {
case ButtonTextTheme.normal:
return getBrightness(button) == Brightness.dark ? Colors.white : Colors.black87;
case ButtonTextTheme.accent:
return colorScheme.secondary;
case ButtonTextTheme.primary:
final Color fillColor = getFillColor(button);
final bool fillIsDark = fillColor != null
? ThemeData.estimateBrightnessForColor(fillColor) == Brightness.dark
: getBrightness(button) == Brightness.dark;
if (fillIsDark)
return Colors.white;
if (button is FlatButton || button is OutlineButton)
return colorScheme.primary;
return Colors.black;
}
assert(false);
return null;
}
/// The color of the ink "splash" overlay that appears when the (enabled)
/// [button] is tapped.
///
/// Returns the button's [MaterialButton.splashColor] if it is non-null.
///
/// Otherwise, returns the value of the `splashColor` constructor parameter
/// it is non-null and [button] is a [RaisedButton] or an [OutlineButton].
///
/// Otherwise, returns the value of the `splashColor` constructor parameter
/// if it is non-null and [button] is a [FlatButton] and
/// [getTextTheme] is not [ButtonTextTheme.primary]
///
/// Otherwise, returns [getTextColor] with an opacity of 0.12.
Color getSplashColor(MaterialButton button) {
if (button.splashColor != null)
return button.splashColor;
if (_splashColor != null && (button is RaisedButton || button is OutlineButton))
return _splashColor;
if (_splashColor != null && button is FlatButton) {
switch (getTextTheme(button)) {
case ButtonTextTheme.normal:
case ButtonTextTheme.accent:
return _splashColor;
case ButtonTextTheme.primary:
break;
}
}
return getTextColor(button).withOpacity(0.12);
}
/// The fill color of the button when it has input focus.
///
/// Returns the button's [MaterialButton.focusColor] if it is non-null.
/// Otherwise the focus color depends on [getTextTheme]:
///
/// * [ButtonTextTheme.normal], [ButtonTextTheme.accent]: returns the
/// value of the `focusColor` constructor parameter if it is non-null,
/// otherwise the value of [getTextColor] with opacity 0.12.
/// * [ButtonTextTheme.primary], returns [Colors.transparent].
Color getFocusColor(MaterialButton button) {
return button.focusColor ?? _focusColor ?? getTextColor(button).withOpacity(0.12);
}
/// The fill color of the button when it has input focus.
///
/// Returns the button's [MaterialButton.focusColor] if it is non-null.
/// Otherwise the focus color depends on [getTextTheme]:
///
/// * [ButtonTextTheme.normal], [ButtonTextTheme.accent],
/// [ButtonTextTheme.primary]: returns the value of the `focusColor`
/// constructor parameter if it is non-null, otherwise the value of
/// [getTextColor] with opacity 0.04.
Color getHoverColor(MaterialButton button) {
return button.hoverColor ?? _hoverColor ?? getTextColor(button).withOpacity(0.04);
}
/// The color of the overlay that appears when the [button] is pressed.
///
/// Returns the button's [MaterialButton.highlightColor] if it is non-null.
/// Otherwise the highlight color depends on [getTextTheme]:
///
/// * [ButtonTextTheme.normal], [ButtonTextTheme.accent]: returns the
/// value of the `highlightColor` constructor parameter if it is non-null,
/// otherwise the value of [getTextColor] with opacity 0.16.
/// * [ButtonTextTheme.primary], returns [Colors.transparent].
Color getHighlightColor(MaterialButton button) {
if (button.highlightColor != null)
return button.highlightColor;
switch (getTextTheme(button)) {
case ButtonTextTheme.normal:
case ButtonTextTheme.accent:
return _highlightColor ?? getTextColor(button).withOpacity(0.16);
case ButtonTextTheme.primary:
return Colors.transparent;
}
assert(false);
return Colors.transparent;
}
/// The [button]'s elevation when it is enabled and has not been pressed.
///
/// Returns the button's [MaterialButton.elevation] if it is non-null.
///
/// If button is a [FlatButton] then elevation is 0.0, otherwise it is 2.0.
double getElevation(MaterialButton button) {
if (button.elevation != null)
return button.elevation;
if (button is FlatButton)
return 0.0;
return 2.0;
}
/// The [button]'s elevation when it is enabled and has focus.
///
/// Returns the button's [MaterialButton.focusElevation] if it is non-null.
///
/// If button is a [FlatButton] or an [OutlineButton] then the focus
/// elevation is 0.0, otherwise the highlight elevation is 4.0.
double getFocusElevation(MaterialButton button) {
if (button.focusElevation != null)
return button.focusElevation;
if (button is FlatButton)
return 0.0;
if (button is OutlineButton)
return 0.0;
return 4.0;
}
/// The [button]'s elevation when it is enabled and has focus.
///
/// Returns the button's [MaterialButton.hoverElevation] if it is non-null.
///
/// If button is a [FlatButton] or an [OutlineButton] then the hover
/// elevation is 0.0, otherwise the highlight elevation is 4.0.
double getHoverElevation(MaterialButton button) {
if (button.hoverElevation != null)
return button.hoverElevation;
if (button is FlatButton)
return 0.0;
if (button is OutlineButton)
return 0.0;
return 4.0;
}
/// The [button]'s elevation when it is enabled and has been pressed.
///
/// Returns the button's [MaterialButton.highlightElevation] if it is non-null.
///
/// If button is a [FlatButton] or an [OutlineButton] then the highlight
/// elevation is 0.0, otherwise the highlight elevation is 8.0.
double getHighlightElevation(MaterialButton button) {
if (button.highlightElevation != null)
return button.highlightElevation;
if (button is FlatButton)
return 0.0;
if (button is OutlineButton)
return 0.0;
return 8.0;
}
/// The [button]'s elevation when [MaterialButton.onPressed] is null (when
/// MaterialButton.enabled is false).
///
/// Returns the button's [MaterialButton.elevation] if it is non-null.
///
/// Otherwise the disabled elevation is 0.0.
double getDisabledElevation(MaterialButton button) {
if (button.disabledElevation != null)
return button.disabledElevation;
return 0.0;
}
/// Padding for the [button]'s child (typically the button's label).
///
/// Returns the button's [MaterialButton.padding] if it is non-null.
///
/// If this is a button constructed with [RaisedButton.icon] or
/// [FlatButton.icon] or [OutlineButton.icon] then the padding is:
/// `EdgeInsetsDirectional.only(start: 12.0, end: 16.0)`.
///
/// Otherwise, returns [padding] if it is non-null.
///
/// Otherwise, returns horizontal padding of 24.0 on the left and right if
/// [getTextTheme] is [ButtonTextTheme.primary], 16.0 on the left and right
/// otherwise.
EdgeInsetsGeometry getPadding(MaterialButton button) {
if (button.padding != null)
return button.padding;
if (button is MaterialButtonWithIconMixin)
return const EdgeInsetsDirectional.only(start: 12.0, end: 16.0);
if (_padding != null)
return _padding;
switch (getTextTheme(button)) {
case ButtonTextTheme.normal:
case ButtonTextTheme.accent:
return const EdgeInsets.symmetric(horizontal: 16.0);
case ButtonTextTheme.primary:
return const EdgeInsets.symmetric(horizontal: 24.0);
}
assert(false);
return EdgeInsets.zero;
}
/// The shape of the [button]'s [Material].
///
/// Returns the button's [MaterialButton.shape] if it is non-null, otherwise
/// [shape] is returned.
ShapeBorder getShape(MaterialButton button) {
return button.shape ?? shape;
}
/// The duration of the [button]'s highlight animation.
///
/// Returns the button's [MaterialButton.animationDuration] it if is non-null,
/// otherwise 200ms.
Duration getAnimationDuration(MaterialButton button) {
return button.animationDuration ?? kThemeChangeDuration;
}
/// The [BoxConstraints] that the define the [button]'s size.
///
/// By default this method just returns [constraints]. Subclasses
/// could override this method to return a value that was,
/// for example, based on the button's type.
BoxConstraints getConstraints(MaterialButton button) => constraints;
/// The minimum size of the [button]'s tap target.
///
/// Returns the button's [MaterialButton.tapTargetSize] if it is non-null.
///
/// Otherwise the value of the [materialTapTargetSize] constructor
/// parameter is returned if that's non-null.
///
/// Otherwise [MaterialTapTargetSize.padded] is returned.
MaterialTapTargetSize getMaterialTapTargetSize(MaterialButton button) {
return button.materialTapTargetSize ?? _materialTapTargetSize ?? MaterialTapTargetSize.padded;
}
/// Creates a copy of this button theme data object with the matching fields
/// replaced with the non-null parameter values.
ButtonThemeData copyWith({
ButtonTextTheme textTheme,
ButtonBarLayoutBehavior layoutBehavior,
double minWidth,
double height,
EdgeInsetsGeometry padding,
ShapeBorder shape,
bool alignedDropdown,
Color buttonColor,
Color disabledColor,
Color focusColor,
Color hoverColor,
Color highlightColor,
Color splashColor,
ColorScheme colorScheme,
MaterialTapTargetSize materialTapTargetSize,
}) {
return ButtonThemeData(
textTheme: textTheme ?? this.textTheme,
layoutBehavior: layoutBehavior ?? this.layoutBehavior,
minWidth: minWidth ?? this.minWidth,
height: height ?? this.height,
padding: padding ?? this.padding,
shape: shape ?? this.shape,
alignedDropdown: alignedDropdown ?? this.alignedDropdown,
buttonColor: buttonColor ?? _buttonColor,
disabledColor: disabledColor ?? _disabledColor,
focusColor: focusColor ?? _focusColor,
hoverColor: hoverColor ?? _hoverColor,
highlightColor: highlightColor ?? _highlightColor,
splashColor: splashColor ?? _splashColor,
colorScheme: colorScheme ?? this.colorScheme,
materialTapTargetSize: materialTapTargetSize ?? _materialTapTargetSize,
);
}
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType)
return false;
return other is ButtonThemeData
&& other.textTheme == textTheme
&& other.minWidth == minWidth
&& other.height == height
&& other.padding == padding
&& other.shape == shape
&& other.alignedDropdown == alignedDropdown
&& other._buttonColor == _buttonColor
&& other._disabledColor == _disabledColor
&& other._focusColor == _focusColor
&& other._hoverColor == _hoverColor
&& other._highlightColor == _highlightColor
&& other._splashColor == _splashColor
&& other.colorScheme == colorScheme
&& other._materialTapTargetSize == _materialTapTargetSize;
}
@override
int get hashCode {
return hashValues(
textTheme,
minWidth,
height,
padding,
shape,
alignedDropdown,
_buttonColor,
_disabledColor,
_focusColor,
_hoverColor,
_highlightColor,
_splashColor,
colorScheme,
_materialTapTargetSize,
);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
const ButtonThemeData defaultTheme = ButtonThemeData();
properties.add(EnumProperty<ButtonTextTheme>('textTheme', textTheme, defaultValue: defaultTheme.textTheme));
properties.add(DoubleProperty('minWidth', minWidth, defaultValue: defaultTheme.minWidth));
properties.add(DoubleProperty('height', height, defaultValue: defaultTheme.height));
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: defaultTheme.padding));
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: defaultTheme.shape));
properties.add(FlagProperty('alignedDropdown',
value: alignedDropdown,
defaultValue: defaultTheme.alignedDropdown,
ifTrue: 'dropdown width matches button',
));
properties.add(ColorProperty('buttonColor', _buttonColor, defaultValue: null));
properties.add(ColorProperty('disabledColor', _disabledColor, defaultValue: null));
properties.add(ColorProperty('focusColor', _focusColor, defaultValue: null));
properties.add(ColorProperty('hoverColor', _hoverColor, defaultValue: null));
properties.add(ColorProperty('highlightColor', _highlightColor, defaultValue: null));
properties.add(ColorProperty('splashColor', _splashColor, defaultValue: null));
properties.add(DiagnosticsProperty<ColorScheme>('colorScheme', colorScheme, defaultValue: defaultTheme.colorScheme));
properties.add(DiagnosticsProperty<MaterialTapTargetSize>('materialTapTargetSize', _materialTapTargetSize, defaultValue: null));
}
}