| // Copyright 2015 The Chromium 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 'button.dart'; |
| import 'button_theme.dart'; |
| import 'colors.dart'; |
| import 'constants.dart'; |
| import 'theme.dart'; |
| |
| /// A material design "raised button". |
| /// |
| /// A raised button is based on a [Material] widget whose [Material.elevation] |
| /// increases when the button is pressed. |
| /// |
| /// Use raised buttons to add dimension to otherwise mostly flat layouts, e.g. |
| /// in long busy lists of content, or in wide spaces. Avoid using raised buttons |
| /// on already-raised content such as dialogs or cards. |
| /// |
| /// If the [onPressed] callback is null, then the button will be disabled and by |
| /// default will resemble a flat button in the [disabledColor]. If you are |
| /// trying to change the button's [color] and it is not having any effect, check |
| /// that you are passing a non-null [onPressed] handler. |
| /// |
| /// If you want an ink-splash effect for taps, but don't want to use a button, |
| /// consider using [InkWell] directly. |
| /// |
| /// Raised buttons have a minimum size of 88.0 by 36.0 which can be overidden |
| /// with [ButtonTheme]. |
| /// |
| /// See also: |
| /// |
| /// * [FlatButton], a material design button without a shadow. |
| /// * [DropdownButton], a button that shows options to select from. |
| /// * [FloatingActionButton], the round button in material applications. |
| /// * [IconButton], to create buttons that just contain icons. |
| /// * [InkWell], which implements the ink splash part of a flat button. |
| /// * [RawMaterialButton], the widget this widget is based on. |
| /// * <https://material.google.com/components/buttons.html> |
| class RaisedButton extends StatelessWidget { |
| /// Create a filled button. |
| /// |
| /// The [elevation], [highlightElevation], and [disabledElevation] |
| /// arguments must not be null. |
| const RaisedButton({ |
| Key key, |
| @required this.onPressed, |
| this.onHighlightChanged, |
| this.textTheme, |
| this.textColor, |
| this.disabledTextColor, |
| this.color, |
| this.disabledColor, |
| this.highlightColor, |
| this.splashColor, |
| this.colorBrightness, |
| this.elevation = 2.0, |
| this.highlightElevation = 8.0, |
| this.disabledElevation = 0.0, |
| this.padding, |
| this.shape, |
| this.animationDuration = kThemeChangeDuration, |
| this.child, |
| }) : assert(elevation != null), |
| assert(highlightElevation != null), |
| assert(disabledElevation != null), |
| assert(animationDuration != null), |
| super(key: key); |
| |
| /// Create a filled button from a pair of widgets that serve as the button's |
| /// [icon] and [label]. |
| /// |
| /// The icon and label are arranged in a row and padded by 12 logical pixels |
| /// at the start, and 16 at the end, with an 8 pixel gap in between. |
| /// |
| /// The [elevation], [highlightElevation], [disabledElevation], [icon], and |
| /// [label] arguments must not be null. |
| RaisedButton.icon({ |
| Key key, |
| @required this.onPressed, |
| this.onHighlightChanged, |
| this.textTheme, |
| this.textColor, |
| this.disabledTextColor, |
| this.color, |
| this.disabledColor, |
| this.highlightColor, |
| this.splashColor, |
| this.colorBrightness, |
| this.elevation = 2.0, |
| this.highlightElevation = 8.0, |
| this.disabledElevation = 0.0, |
| this.shape, |
| this.animationDuration = kThemeChangeDuration, |
| @required Widget icon, |
| @required Widget label, |
| }) : assert(elevation != null), |
| assert(highlightElevation != null), |
| assert(disabledElevation != null), |
| assert(icon != null), |
| assert(label != null), |
| assert(animationDuration != null), |
| padding = const EdgeInsetsDirectional.only(start: 12.0, end: 16.0), |
| child = new Row( |
| mainAxisSize: MainAxisSize.min, |
| children: <Widget>[ |
| icon, |
| const SizedBox(width: 8.0), |
| label, |
| ], |
| ), |
| super(key: key); |
| |
| /// Called when the button is tapped or otherwise activated. |
| /// |
| /// If this is set to null, the button will be disabled, see [enabled]. |
| final VoidCallback onPressed; |
| |
| /// Called by the underlying [InkWell] widget's [InkWell.onHighlightChanged] |
| /// callback. |
| final ValueChanged<bool> onHighlightChanged; |
| |
| /// 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, [ThemeData.textTheme.button], configured with this color. |
| /// |
| /// The default text color depends on the button theme's text theme, |
| /// [ButtonThemeData.textTheme]. |
| /// |
| /// 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, [ThemeData.textTheme.button], configured with this color. |
| /// |
| /// The default value is the theme's disabled color, |
| /// [ThemeData.disabledColor]. |
| /// |
| /// 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]. |
| /// |
| /// Typically the default color will be overidden with a Material color, |
| /// for example: |
| /// |
| /// ```dart |
| /// new RaisedButton( |
| /// color: Colors.blue, |
| /// onPressed: _handleTap, |
| /// child: new Text('DEMO'), |
| /// ), |
| /// ``` |
| /// |
| /// 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 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. This controls the size of |
| /// the shadow below the raised button. |
| /// |
| /// Defaults to 2, the appropriate elevation for raised buttons. |
| /// |
| /// See also: |
| /// |
| /// * [FlatButton], a button with no elevation or fill color. |
| /// * [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] but not pressed. |
| /// |
| /// Defaults to 2.0. |
| /// |
| /// See also: |
| /// |
| /// * [highlightElevation], the default elevation. |
| /// * [disabledElevation], the elevation when the button is disabled. |
| |
| /// The elevation for the button's [Material] 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. |
| /// |
| /// See also: |
| /// |
| /// * [elevation], the default elevation. |
| /// * [disabledElevation], the elevation when the button is disabled. |
| final double highlightElevation; |
| |
| /// The elevation for the button's [Material] when the button |
| /// is not [enabled]. |
| /// |
| /// Defaults to 0.0. |
| /// |
| /// 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, [ThemeData.brightness]. |
| 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] |
| /// property to a non-null value. |
| bool get enabled => onPressed != null; |
| |
| /// The internal padding for the button's [child]. |
| /// |
| /// Defaults to the value from the current [ButtonTheme], |
| /// [ButtonThemeData.padding]. |
| final EdgeInsetsGeometry padding; |
| |
| /// 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. |
| final ShapeBorder shape; |
| |
| /// Defines the duration of animated changes for [shape] and [elevation]. |
| /// |
| /// The default value is [kThemeChangeDuration]. |
| final Duration animationDuration; |
| |
| Brightness _getBrightness(ThemeData theme) { |
| return colorBrightness ?? theme.brightness; |
| } |
| |
| ButtonTextTheme _getTextTheme(ButtonThemeData buttonTheme) { |
| return textTheme ?? buttonTheme.textTheme; |
| } |
| |
| Color _getFillColor(ThemeData theme, ButtonThemeData buttonTheme) { |
| final Color fillColor = enabled ? color : disabledColor; |
| if (fillColor != null) |
| return fillColor; |
| |
| final bool themeIsDark = _getBrightness(theme) == Brightness.dark; |
| switch (_getTextTheme(buttonTheme)) { |
| case ButtonTextTheme.normal: |
| case ButtonTextTheme.accent: |
| return enabled |
| ? theme.buttonColor |
| : theme.disabledColor; |
| case ButtonTextTheme.primary: |
| return enabled |
| ? theme.buttonColor |
| : (themeIsDark ? Colors.white12 : Colors.black12); |
| } |
| return null; |
| } |
| |
| Color _getTextColor(ThemeData theme, ButtonThemeData buttonTheme, Color fillColor) { |
| final Color color = enabled ? textColor : disabledTextColor; |
| if (color != null) |
| return color; |
| |
| final bool themeIsDark = _getBrightness(theme) == Brightness.dark; |
| final bool fillIsDark = fillColor != null |
| ? ThemeData.estimateBrightnessForColor(fillColor) == Brightness.dark |
| : themeIsDark; |
| |
| switch (_getTextTheme(buttonTheme)) { |
| case ButtonTextTheme.normal: |
| return enabled |
| ? (themeIsDark ? Colors.white : Colors.black87) |
| : theme.disabledColor; |
| case ButtonTextTheme.accent: |
| return enabled |
| ? theme.accentColor |
| : theme.disabledColor; |
| case ButtonTextTheme.primary: |
| return enabled |
| ? (fillIsDark ? Colors.white : Colors.black) |
| : (themeIsDark ? Colors.white30 : Colors.black38); |
| } |
| return null; |
| } |
| |
| Color _getHighlightColor(ThemeData theme, ButtonThemeData buttonTheme) { |
| if (highlightColor != null) |
| return highlightColor; |
| |
| switch (_getTextTheme(buttonTheme)) { |
| case ButtonTextTheme.normal: |
| case ButtonTextTheme.accent: |
| return theme.highlightColor; |
| case ButtonTextTheme.primary: |
| return Colors.transparent; |
| } |
| return Colors.transparent; |
| } |
| |
| @override |
| Widget build(BuildContext context) { |
| final ThemeData theme = Theme.of(context); |
| final ButtonThemeData buttonTheme = ButtonTheme.of(context); |
| final Color fillColor = _getFillColor(theme, buttonTheme); |
| final Color textColor = _getTextColor(theme, buttonTheme, fillColor); |
| |
| return new RawMaterialButton( |
| onPressed: onPressed, |
| onHighlightChanged: onHighlightChanged, |
| fillColor: fillColor, |
| textStyle: theme.textTheme.button.copyWith(color: textColor), |
| highlightColor: _getHighlightColor(theme, buttonTheme), |
| splashColor: splashColor ?? theme.splashColor, |
| elevation: elevation, |
| highlightElevation: highlightElevation, |
| disabledElevation: disabledElevation, |
| padding: padding ?? buttonTheme.padding, |
| constraints: buttonTheme.constraints, |
| shape: shape ?? buttonTheme.shape, |
| animationDuration: animationDuration, |
| child: child, |
| ); |
| } |
| |
| @override |
| void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
| super.debugFillProperties(properties); |
| properties.add(new ObjectFlagProperty<VoidCallback>('onPressed', onPressed, ifNull: 'disabled')); |
| properties.add(new DiagnosticsProperty<Color>('textColor', textColor, defaultValue: null)); |
| properties.add(new DiagnosticsProperty<Color>('disabledTextColor', disabledTextColor, defaultValue: null)); |
| properties.add(new DiagnosticsProperty<Color>('color', color, defaultValue: null)); |
| properties.add(new DiagnosticsProperty<Color>('disabledColor', disabledColor, defaultValue: null)); |
| properties.add(new DiagnosticsProperty<Color>('highlightColor', highlightColor, defaultValue: null)); |
| properties.add(new DiagnosticsProperty<Color>('splashColor', splashColor, defaultValue: null)); |
| properties.add(new DiagnosticsProperty<Brightness>('colorBrightness', colorBrightness, defaultValue: null)); |
| properties.add(new DiagnosticsProperty<double>('elevation', elevation, defaultValue: null)); |
| properties.add(new DiagnosticsProperty<double>('highlightElevation', highlightElevation, defaultValue: null)); |
| properties.add(new DiagnosticsProperty<double>('disabledElevation', disabledElevation, defaultValue: null)); |
| properties.add(new DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: null)); |
| properties.add(new DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null)); |
| } |
| } |