| // 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 'theme.dart'; |
| |
| /// 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], which styles itself based on the ambient [ButtonTheme]. |
| /// * [FlatButton], which styles itself 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 [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 Material |
| /// theme. |
| /// |
| /// See also: |
| /// |
| /// * [FlatButton] and [RaisedButton], which are styled based on the |
| /// ambient button theme. |
| /// * [ThemeData.textTheme], `button` is the default text style for button labels. |
| /// * [ThemeData.buttonColor], the fill color for [RaisedButton]s unless the |
| /// button theme's text theme is [ButtonTextTheme.primary]. |
| /// * [ThemeData.primaryColor], the fill or text color if a button theme's text |
| /// theme is [ButtonTextTheme.primary]. |
| /// * [ThemeData.accentColor], the text color for buttons when button theme's |
| /// text theme is [ButtonTextTheme.accent]. |
| /// * [ThemeData.disabled], the default text color for disabled buttons. |
| /// * [ThemeData.brightness], used to select contrasting text and fill colors. |
| /// * [ThemeData.highlightColor], a button [InkWell]'s default highlight color. |
| /// * [ThemeData.splashColor], a button [InkWell]'s default splash color. |
| /// * [RawMaterialButton], which can be used to configure a button that doesn't |
| /// depend on any inherited themes. |
| class ButtonTheme extends InheritedWidget { |
| /// Creates a button theme. |
| /// |
| /// The [textTheme], [minWidth], and [height] arguments must not be null. |
| ButtonTheme({ |
| Key key, |
| ButtonTextTheme textTheme = ButtonTextTheme.normal, |
| double minWidth = 88.0, |
| double height = 36.0, |
| EdgeInsetsGeometry padding, |
| ShapeBorder shape, |
| bool alignedDropdown = false, |
| Widget child, |
| }) : assert(textTheme != null), |
| assert(minWidth != null && minWidth >= 0.0), |
| assert(height != null && height >= 0.0), |
| assert(alignedDropdown != null), |
| data = new ButtonThemeData( |
| textTheme: textTheme, |
| minWidth: minWidth, |
| height: height, |
| padding: padding, |
| shape: shape, |
| alignedDropdown: alignedDropdown |
| ), |
| 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); |
| |
| /// Creates a button theme that is appropriate for button bars, as used in |
| /// dialog footers and in the headers of data tables. |
| /// |
| /// This theme is denser, with a smaller [minWidth] and [padding], than the |
| /// default theme. Also, this theme uses [ButtonTextTheme.accent] rather than |
| /// [ButtonTextTheme.normal]. |
| /// |
| /// For best effect, the label of the button at the edge of the container |
| /// should have text that ends up wider than 64.0 pixels. This ensures that |
| /// the alignment of the text matches the alignment of the edge of the |
| /// container. |
| /// |
| /// For example, buttons at the bottom of [Dialog] or [Card] widgets use this |
| /// button theme. |
| 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, |
| Widget child, |
| }) : assert(textTheme != null), |
| assert(minWidth != null && minWidth >= 0.0), |
| assert(height != null && height >= 0.0), |
| assert(alignedDropdown != null), |
| data = new ButtonThemeData( |
| textTheme: textTheme, |
| minWidth: minWidth, |
| height: height, |
| padding: padding, |
| shape: shape, |
| alignedDropdown: alignedDropdown, |
| ), |
| 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 result = context.inheritFromWidgetOfExactType(ButtonTheme); |
| return result?.data ?? Theme.of(context).buttonTheme; |
| } |
| |
| @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 extends Diagnosticable { |
| /// Create a button theme object that can be used with [ButtonTheme] |
| /// or [ThemeData]. |
| /// |
| /// The [textTheme], [minWidth], and [height] parameters must not be null. |
| const ButtonThemeData({ |
| this.textTheme = ButtonTextTheme.normal, |
| this.minWidth = 88.0, |
| this.height = 36.0, |
| EdgeInsetsGeometry padding, |
| ShapeBorder shape, |
| this.alignedDropdown = false, |
| }) : assert(textTheme != null), |
| assert(minWidth != null && minWidth >= 0.0), |
| assert(height != null && height >= 0.0), |
| assert(alignedDropdown != null), |
| _padding = padding, |
| _shape = shape; |
| |
| /// 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. |
| final ButtonTextTheme textTheme; |
| |
| /// Simply a convenience that returns [minWidth] and [height] as a |
| /// [BoxConstraints] object: |
| /// ```dart |
| /// return new BoxConstraints( |
| /// minWidth: minWidth, |
| /// minHeight: height, |
| /// ); |
| /// ``` |
| BoxConstraints get constraints { |
| return new 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. |
| 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); |
| } |
| 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. |
| ShapeBorder get shape { |
| if (_shape != null) |
| return _shape; |
| switch (textTheme) { |
| case ButtonTextTheme.normal: |
| case ButtonTextTheme.accent: |
| return const RoundedRectangleBorder( |
| borderRadius: const BorderRadius.all(const Radius.circular(2.0)), |
| ); |
| case ButtonTextTheme.primary: |
| return const RoundedRectangleBorder( |
| borderRadius: const BorderRadius.all(const 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; |
| |
| /// Creates a copy of this button theme data object with the matching fields |
| /// replaced with the non-null parameter values. |
| ButtonThemeData copyWith({ |
| ButtonTextTheme textTheme, |
| double minWidth, |
| double height, |
| EdgeInsetsGeometry padding, |
| ShapeBorder shape, |
| bool alignedDropdown, |
| }) { |
| return new ButtonThemeData( |
| textTheme: textTheme ?? this.textTheme, |
| minWidth: minWidth ?? this.minWidth, |
| height: height ?? this.height, |
| padding: padding ?? this.padding, |
| shape: shape ?? this.shape, |
| alignedDropdown: alignedDropdown ?? this.alignedDropdown, |
| ); |
| } |
| |
| @override |
| bool operator ==(dynamic other) { |
| if (other.runtimeType != runtimeType) |
| return false; |
| final ButtonThemeData typedOther = other; |
| return textTheme == typedOther.textTheme |
| && minWidth == typedOther.minWidth |
| && height == typedOther.height |
| && padding == typedOther.padding |
| && shape == typedOther.shape |
| && alignedDropdown == typedOther.alignedDropdown; |
| } |
| |
| @override |
| int get hashCode { |
| return hashValues( |
| textTheme, |
| minWidth, |
| height, |
| padding, |
| shape, |
| alignedDropdown, |
| ); |
| } |
| |
| @override |
| void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
| super.debugFillProperties(properties); |
| const ButtonThemeData defaultTheme = const ButtonThemeData(); |
| properties.add(new EnumProperty<ButtonTextTheme>('textTheme', textTheme, defaultValue: defaultTheme.textTheme)); |
| properties.add(new DoubleProperty('minWidth', minWidth, defaultValue: defaultTheme.minWidth)); |
| properties.add(new DoubleProperty('height', height, defaultValue: defaultTheme.height)); |
| properties.add(new DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: defaultTheme.padding)); |
| properties.add(new DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: defaultTheme.shape)); |
| properties.add(new FlagProperty('alignedDropdown', |
| value: alignedDropdown, |
| defaultValue: defaultTheme.alignedDropdown, |
| ifTrue: 'dropdown width matches button', |
| )); |
| } |
| } |