mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
466 lines
17 KiB
Dart
466 lines
17 KiB
Dart
// 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:ui';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter/widgets.dart';
|
|
|
|
import 'button.dart';
|
|
import 'button_theme.dart';
|
|
import 'constants.dart';
|
|
import 'ink_well.dart';
|
|
import 'material.dart';
|
|
import 'theme.dart';
|
|
import 'theme_data.dart';
|
|
|
|
/// A utility class for building Material buttons that depend on the
|
|
/// ambient [ButtonTheme] and [Theme].
|
|
///
|
|
/// ### This class is obsolete.
|
|
///
|
|
/// FlatButton, RaisedButton, and OutlineButton have been replaced by
|
|
/// TextButton, ElevatedButton, and OutlinedButton respectively.
|
|
/// ButtonTheme has been replaced by TextButtonTheme,
|
|
/// ElevatedButtonTheme, and OutlinedButtonTheme. The appearance of the
|
|
/// new widgets can be customized by specifying a [ButtonStyle]
|
|
/// or by creating a one-off style using a `styleFrom` method like
|
|
/// [TextButton.styleFrom]. The original button classes
|
|
/// will be deprecated soon, please migrate code that uses them.
|
|
/// There's a detailed migration guide for the new button and button
|
|
/// theme classes in
|
|
/// [flutter.dev/go/material-button-migration-guide](https://flutter.dev/go/material-button-migration-guide).
|
|
///
|
|
/// The button's size will expand to fit the child widget, if necessary.
|
|
///
|
|
/// MaterialButtons whose [onPressed] and [onLongPress] callbacks are null will be disabled. To have
|
|
/// an enabled button, make sure to pass a non-null value for [onPressed] or [onLongPress].
|
|
///
|
|
/// Rather than using this class directly, consider using [FlatButton],
|
|
/// [OutlineButton], or [RaisedButton], which configure this class with
|
|
/// appropriate defaults that match the material design specification.
|
|
///
|
|
/// To create a button directly, without inheriting theme defaults, use
|
|
/// [RawMaterialButton].
|
|
///
|
|
/// If you want an ink-splash effect for taps, but don't want to use a button,
|
|
/// consider using [InkWell] directly.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [IconButton], to create buttons that contain icons rather than text.
|
|
class MaterialButton extends StatelessWidget {
|
|
/// Creates a material button.
|
|
///
|
|
/// Rather than creating a material button directly, consider using
|
|
/// [FlatButton] or [RaisedButton]. To create a custom Material button
|
|
/// consider using [RawMaterialButton].
|
|
///
|
|
/// The [autofocus] and [clipBehavior] arguments must not be null.
|
|
/// Additionally, [elevation], [hoverElevation], [focusElevation],
|
|
/// [highlightElevation], and [disabledElevation] must be non-negative, if
|
|
/// specified.
|
|
const MaterialButton({
|
|
Key? key,
|
|
required this.onPressed,
|
|
this.onLongPress,
|
|
this.onHighlightChanged,
|
|
this.mouseCursor,
|
|
this.textTheme,
|
|
this.textColor,
|
|
this.disabledTextColor,
|
|
this.color,
|
|
this.disabledColor,
|
|
this.focusColor,
|
|
this.hoverColor,
|
|
this.highlightColor,
|
|
this.splashColor,
|
|
this.colorBrightness,
|
|
this.elevation,
|
|
this.focusElevation,
|
|
this.hoverElevation,
|
|
this.highlightElevation,
|
|
this.disabledElevation,
|
|
this.padding,
|
|
this.visualDensity,
|
|
this.shape,
|
|
this.clipBehavior = Clip.none,
|
|
this.focusNode,
|
|
this.autofocus = false,
|
|
this.materialTapTargetSize,
|
|
this.animationDuration,
|
|
this.minWidth,
|
|
this.height,
|
|
this.enableFeedback = true,
|
|
this.child,
|
|
}) : assert(clipBehavior != null),
|
|
assert(autofocus != null),
|
|
assert(elevation == null || elevation >= 0.0),
|
|
assert(focusElevation == null || focusElevation >= 0.0),
|
|
assert(hoverElevation == null || hoverElevation >= 0.0),
|
|
assert(highlightElevation == null || highlightElevation >= 0.0),
|
|
assert(disabledElevation == null || disabledElevation >= 0.0),
|
|
super(key: key);
|
|
|
|
/// The callback that is called when the button is tapped or otherwise activated.
|
|
///
|
|
/// If this callback and [onLongPress] are null, then the button will be disabled.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [enabled], which is true if the button is enabled.
|
|
final VoidCallback? onPressed;
|
|
|
|
/// The callback that is called when the button is long-pressed.
|
|
///
|
|
/// If this callback and [onPressed] are null, then the button will be disabled.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [enabled], which is true if the button is enabled.
|
|
final VoidCallback? onLongPress;
|
|
|
|
/// Called by the underlying [InkWell] widget's [InkWell.onHighlightChanged]
|
|
/// callback.
|
|
///
|
|
/// If [onPressed] changes from null to non-null while a gesture is ongoing,
|
|
/// this can fire during the build phase (in which case calling
|
|
/// [State.setState] is not allowed).
|
|
final ValueChanged<bool>? onHighlightChanged;
|
|
|
|
/// {@macro flutter.material.RawMaterialButton.mouseCursor}
|
|
final MouseCursor? mouseCursor;
|
|
|
|
/// Defines the button's base colors, and the defaults for the button's minimum
|
|
/// size, internal padding, and shape.
|
|
///
|
|
/// Defaults to `ButtonTheme.of(context).textTheme`.
|
|
final ButtonTextTheme? textTheme;
|
|
|
|
/// The color to use for this button's text.
|
|
///
|
|
/// The button's [Material.textStyle] will be the current theme's button text
|
|
/// style, [TextTheme.button] of [ThemeData.textTheme], configured with this
|
|
/// color.
|
|
///
|
|
/// The default text color depends on the button theme's text theme,
|
|
/// [ButtonThemeData.textTheme].
|
|
///
|
|
/// If [textColor] is a [MaterialStateProperty<Color>], [disabledTextColor]
|
|
/// will be ignored.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [disabledTextColor], the text color to use when the button has been
|
|
/// disabled.
|
|
final Color? textColor;
|
|
|
|
/// The color to use for this button's text when the button is disabled.
|
|
///
|
|
/// The button's [Material.textStyle] will be the current theme's button text
|
|
/// style, [TextTheme.button] of [ThemeData.textTheme], configured with this
|
|
/// color.
|
|
///
|
|
/// The default value is the theme's disabled color,
|
|
/// [ThemeData.disabledColor].
|
|
///
|
|
/// If [textColor] is a [MaterialStateProperty<Color>], [disabledTextColor]
|
|
/// will be ignored.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [textColor] - The color to use for this button's text when the button is [enabled].
|
|
final Color? disabledTextColor;
|
|
|
|
/// The button's fill color, displayed by its [Material], while it
|
|
/// is in its default (unpressed, [enabled]) state.
|
|
///
|
|
/// The default fill color is the theme's button color, [ThemeData.buttonColor].
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [disabledColor] - the fill color of the button when the button is disabled.
|
|
final Color? color;
|
|
|
|
/// The fill color of the button when the button is disabled.
|
|
///
|
|
/// The default value of this color is the theme's disabled color,
|
|
/// [ThemeData.disabledColor].
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [color] - the fill color of the button when the button is [enabled].
|
|
final Color? disabledColor;
|
|
|
|
/// The splash color of the button's [InkWell].
|
|
///
|
|
/// The ink splash indicates that the button has been touched. It
|
|
/// appears on top of the button's child and spreads in an expanding
|
|
/// circle beginning where the touch occurred.
|
|
///
|
|
/// The default splash color is the current theme's splash color,
|
|
/// [ThemeData.splashColor].
|
|
///
|
|
/// The appearance of the splash can be configured with the theme's splash
|
|
/// factory, [ThemeData.splashFactory].
|
|
final Color? splashColor;
|
|
|
|
/// The fill color of the button's [Material] when it has the input focus.
|
|
///
|
|
/// The button changed focus color when the button has the input focus. It
|
|
/// appears behind the button's child.
|
|
final Color? focusColor;
|
|
|
|
/// The fill color of the button's [Material] when a pointer is hovering over
|
|
/// it.
|
|
///
|
|
/// The button changes fill color when a pointer is hovering over the button.
|
|
/// It appears behind the button's child.
|
|
final Color? hoverColor;
|
|
|
|
/// The highlight color of the button's [InkWell].
|
|
///
|
|
/// The highlight indicates that the button is actively being pressed. It
|
|
/// appears on top of the button's child and quickly spreads to fill
|
|
/// the button, and then fades out.
|
|
///
|
|
/// If [textTheme] is [ButtonTextTheme.primary], the default highlight color is
|
|
/// transparent (in other words the highlight doesn't appear). Otherwise it's
|
|
/// the current theme's highlight color, [ThemeData.highlightColor].
|
|
final Color? highlightColor;
|
|
|
|
/// The z-coordinate at which to place this button relative to its parent.
|
|
///
|
|
/// This controls the size of the shadow below the raised button.
|
|
///
|
|
/// Defaults to 2, the appropriate elevation for raised buttons. The value
|
|
/// is always non-negative.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [FlatButton], a button with no elevation or fill color.
|
|
/// * [focusElevation], the elevation when the button is focused.
|
|
/// * [hoverElevation], the elevation when a pointer is hovering over the
|
|
/// button.
|
|
/// * [disabledElevation], the elevation when the button is disabled.
|
|
/// * [highlightElevation], the elevation when the button is pressed.
|
|
final double? elevation;
|
|
|
|
/// The elevation for the button's [Material] when the button
|
|
/// is [enabled] and a pointer is hovering over it.
|
|
///
|
|
/// Defaults to 4.0. The value is always non-negative.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [elevation], the default elevation.
|
|
/// * [focusElevation], the elevation when the button is focused.
|
|
/// * [disabledElevation], the elevation when the button is disabled.
|
|
/// * [highlightElevation], the elevation when the button is pressed.
|
|
final double? hoverElevation;
|
|
|
|
/// The elevation for the button's [Material] when the button
|
|
/// is [enabled] and has the input focus.
|
|
///
|
|
/// Defaults to 4.0. The value is always non-negative.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [elevation], the default elevation.
|
|
/// * [hoverElevation], the elevation when a pointer is hovering over the
|
|
/// button.
|
|
/// * [disabledElevation], the elevation when the button is disabled.
|
|
/// * [highlightElevation], the elevation when the button is pressed.
|
|
final double? focusElevation;
|
|
|
|
/// The elevation for the button's [Material] relative to its parent when the
|
|
/// button is [enabled] and pressed.
|
|
///
|
|
/// This controls the size of the shadow below the button. When a tap
|
|
/// down gesture occurs within the button, its [InkWell] displays a
|
|
/// [highlightColor] "highlight".
|
|
///
|
|
/// Defaults to 8.0. The value is always non-negative.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [elevation], the default elevation.
|
|
/// * [focusElevation], the elevation when the button is focused.
|
|
/// * [hoverElevation], the elevation when a pointer is hovering over the
|
|
/// button.
|
|
/// * [disabledElevation], the elevation when the button is disabled.
|
|
final double? highlightElevation;
|
|
|
|
/// The elevation for the button's [Material] relative to its parent when the
|
|
/// button is not [enabled].
|
|
///
|
|
/// Defaults to 0.0. The value is always non-negative.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [elevation], the default elevation.
|
|
/// * [highlightElevation], the elevation when the button is pressed.
|
|
final double? disabledElevation;
|
|
|
|
/// The theme brightness to use for this button.
|
|
///
|
|
/// Defaults to the theme's brightness in [ThemeData.brightness]. Setting
|
|
/// this value determines the button text's colors based on
|
|
/// [ButtonThemeData.getTextColor].
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [ButtonTextTheme], uses [Brightness] to determine text color.
|
|
final Brightness? colorBrightness;
|
|
|
|
/// The button's label.
|
|
///
|
|
/// Often a [Text] widget in all caps.
|
|
final Widget? child;
|
|
|
|
/// Whether the button is enabled or disabled.
|
|
///
|
|
/// Buttons are disabled by default. To enable a button, set its [onPressed]
|
|
/// or [onLongPress] properties to a non-null value.
|
|
bool get enabled => onPressed != null || onLongPress != null;
|
|
|
|
/// The internal padding for the button's [child].
|
|
///
|
|
/// Defaults to the value from the current [ButtonTheme],
|
|
/// [ButtonThemeData.padding].
|
|
final EdgeInsetsGeometry? padding;
|
|
|
|
/// Defines how compact the button's layout will be.
|
|
///
|
|
/// {@macro flutter.material.themedata.visualDensity}
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [ThemeData.visualDensity], which specifies the [visualDensity] for all
|
|
/// widgets within a [Theme].
|
|
final VisualDensity? visualDensity;
|
|
|
|
/// The shape of the 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 the value from the current [ButtonTheme],
|
|
/// [ButtonThemeData.shape].
|
|
final ShapeBorder? shape;
|
|
|
|
/// {@macro flutter.material.Material.clipBehavior}
|
|
///
|
|
/// Defaults to [Clip.none], and must not be null.
|
|
final Clip clipBehavior;
|
|
|
|
/// {@macro flutter.widgets.Focus.focusNode}
|
|
final FocusNode? focusNode;
|
|
|
|
/// {@macro flutter.widgets.Focus.autofocus}
|
|
final bool autofocus;
|
|
|
|
/// Defines the duration of animated changes for [shape] and [elevation].
|
|
///
|
|
/// The default value is [kThemeChangeDuration].
|
|
final Duration? animationDuration;
|
|
|
|
/// Configures the minimum size of the tap target.
|
|
///
|
|
/// Defaults to [ThemeData.materialTapTargetSize].
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [MaterialTapTargetSize], for a description of how this affects tap targets.
|
|
final MaterialTapTargetSize? materialTapTargetSize;
|
|
|
|
/// The smallest horizontal extent that the button will occupy.
|
|
///
|
|
/// Defaults to the value from the current [ButtonTheme].
|
|
final double? minWidth;
|
|
|
|
/// The vertical extent of the button.
|
|
///
|
|
/// Defaults to the value from the current [ButtonTheme].
|
|
final double? height;
|
|
|
|
/// Whether detected gestures should provide acoustic and/or haptic feedback.
|
|
///
|
|
/// For example, on Android a tap will produce a clicking sound and a
|
|
/// long-press will produce a short vibration, when feedback is enabled.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [Feedback] for providing platform-specific feedback to certain actions.
|
|
final bool enableFeedback;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final ThemeData theme = Theme.of(context);
|
|
final ButtonThemeData buttonTheme = ButtonTheme.of(context);
|
|
|
|
return RawMaterialButton(
|
|
onPressed: onPressed,
|
|
onLongPress: onLongPress,
|
|
enableFeedback: enableFeedback,
|
|
onHighlightChanged: onHighlightChanged,
|
|
mouseCursor: mouseCursor,
|
|
fillColor: buttonTheme.getFillColor(this),
|
|
textStyle: theme.textTheme.button!.copyWith(color: buttonTheme.getTextColor(this)),
|
|
focusColor: focusColor ?? buttonTheme.getFocusColor(this),
|
|
hoverColor: hoverColor ?? buttonTheme.getHoverColor(this),
|
|
highlightColor: highlightColor ?? theme.highlightColor,
|
|
splashColor: splashColor ?? theme.splashColor,
|
|
elevation: buttonTheme.getElevation(this),
|
|
focusElevation: buttonTheme.getFocusElevation(this),
|
|
hoverElevation: buttonTheme.getHoverElevation(this),
|
|
highlightElevation: buttonTheme.getHighlightElevation(this),
|
|
padding: buttonTheme.getPadding(this),
|
|
visualDensity: visualDensity ?? theme.visualDensity,
|
|
constraints: buttonTheme.getConstraints(this).copyWith(
|
|
minWidth: minWidth,
|
|
minHeight: height,
|
|
),
|
|
shape: buttonTheme.getShape(this),
|
|
clipBehavior: clipBehavior,
|
|
focusNode: focusNode,
|
|
autofocus: autofocus,
|
|
animationDuration: buttonTheme.getAnimationDuration(this),
|
|
child: child,
|
|
materialTapTargetSize: materialTapTargetSize ?? theme.materialTapTargetSize,
|
|
disabledElevation: disabledElevation ?? 0.0,
|
|
);
|
|
}
|
|
|
|
@override
|
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
|
super.debugFillProperties(properties);
|
|
properties.add(FlagProperty('enabled', value: enabled, ifFalse: 'disabled'));
|
|
properties.add(DiagnosticsProperty<ButtonTextTheme>('textTheme', textTheme, defaultValue: null));
|
|
properties.add(ColorProperty('textColor', textColor, defaultValue: null));
|
|
properties.add(ColorProperty('disabledTextColor', disabledTextColor, defaultValue: null));
|
|
properties.add(ColorProperty('color', color, 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<Brightness>('colorBrightness', colorBrightness, defaultValue: null));
|
|
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: null));
|
|
properties.add(DiagnosticsProperty<VisualDensity>('visualDensity', visualDensity, defaultValue: null));
|
|
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null));
|
|
properties.add(DiagnosticsProperty<FocusNode>('focusNode', focusNode, defaultValue: null));
|
|
properties.add(DiagnosticsProperty<MaterialTapTargetSize>('materialTapTargetSize', materialTapTargetSize, defaultValue: null));
|
|
}
|
|
}
|
|
|
|
/// The type of [MaterialButton]s created with [RaisedButton.icon], [FlatButton.icon],
|
|
/// and [OutlineButton.icon].
|
|
///
|
|
/// This mixin only exists to give the "label and icon" button widgets a distinct
|
|
/// type for the sake of [ButtonTheme].
|
|
mixin MaterialButtonWithIconMixin { }
|