"Fossies" - the Fresh Open Source Software Archive

Member "flutter-1.22.4/packages/flutter/lib/src/material/text_button.dart" (13 Nov 2020, 13930 Bytes) of package /linux/misc/flutter-1.22.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Dart source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 // Copyright 2014 The Flutter Authors. All rights reserved.
    2 // Use of this source code is governed by a BSD-style license that can be
    3 // found in the LICENSE file.
    4 
    5 // @dart = 2.8
    6 
    7 import 'dart:math' as math;
    8 import 'dart:ui' show lerpDouble;
    9 
   10 import 'package:flutter/foundation.dart';
   11 import 'package:flutter/rendering.dart';
   12 import 'package:flutter/widgets.dart';
   13 
   14 import 'button_style.dart';
   15 import 'button_style_button.dart';
   16 import 'color_scheme.dart';
   17 import 'colors.dart';
   18 import 'constants.dart';
   19 import 'material_state.dart';
   20 import 'text_button_theme.dart';
   21 import 'theme.dart';
   22 import 'theme_data.dart';
   23 
   24 /// A Material Design "Text Button".
   25 ///
   26 /// Use text buttons on toolbars, in dialogs, or inline with other
   27 /// content but offset from that content with padding so that the
   28 /// button's presence is obvious. Text buttons do not have visible
   29 /// borders and must therefore rely on their position relative to
   30 /// other content for context. In dialogs and cards, they should be
   31 /// grouped together in one of the bottom corners. Avoid using text
   32 /// buttons where they would blend in with other content, for example
   33 /// in the middle of lists.
   34 ///
   35 /// A text button is a label [child] displayed on a (zero elevation)
   36 /// [Material] widget. The label's [Text] and [Icon] widgets are
   37 /// displayed in the [style]'s [ButtonStyle.foregroundColor]. The
   38 /// button reacts to touches by filling with the [style]'s
   39 /// [ButtonStyle.backgroundColor].
   40 ///
   41 /// The text button's default style is defined by [defaultStyleOf].
   42 /// The style of this text button can be overridden with its [style]
   43 /// parameter. The style of all text buttons in a subtree can be
   44 /// overridden with the [TextButtonTheme] and the style of all of the
   45 /// text buttons in an app can be overridden with the [Theme]'s
   46 /// [ThemeData.textButtonTheme] property.
   47 ///
   48 /// The static [styleFrom] method is a convenient way to create a
   49 /// text button [ButtonStyle] from simple values.
   50 ///
   51 /// If the [onPressed] and [onLongPress] callbacks are null, then this
   52 /// button will be disabled, it will not react to touch.
   53 ///
   54 /// See also:
   55 ///
   56 ///  * [OutlinedButton], a [TextButton] with a border outline.
   57 ///  * [ElevatedButton], a filled button whose material elevates when pressed.
   58 ///  * <https://material.io/design/components/buttons.html>
   59 class TextButton extends ButtonStyleButton {
   60   /// Create a TextButton.
   61   ///
   62   /// The [autofocus] and [clipBehavior] arguments must not be null.
   63   const TextButton({
   64     Key key,
   65     @required VoidCallback onPressed,
   66     VoidCallback onLongPress,
   67     ButtonStyle style,
   68     FocusNode focusNode,
   69     bool autofocus = false,
   70     Clip clipBehavior = Clip.none,
   71     @required Widget child,
   72   }) : super(
   73     key: key,
   74     onPressed: onPressed,
   75     onLongPress: onLongPress,
   76     style: style,
   77     focusNode: focusNode,
   78     autofocus: autofocus,
   79     clipBehavior: clipBehavior,
   80     child: child,
   81   );
   82 
   83   /// Create a text button from a pair of widgets that serve as the button's
   84   /// [icon] and [label].
   85   ///
   86   /// The icon and label are arranged in a row and padded by 8 logical pixels
   87   /// at the ends, with an 8 pixel gap in between.
   88   ///
   89   /// The [icon] and [label] arguments must not be null.
   90   factory TextButton.icon({
   91     Key key,
   92     @required VoidCallback onPressed,
   93     VoidCallback onLongPress,
   94     ButtonStyle style,
   95     FocusNode focusNode,
   96     bool autofocus,
   97     Clip clipBehavior,
   98     @required Widget icon,
   99     @required Widget label,
  100   }) = _TextButtonWithIcon;
  101 
  102   /// A static convenience method that constructs a text button
  103   /// [ButtonStyle] given simple values.
  104   ///
  105   /// The [primary], and [onSurface] colors are used to to create a
  106   /// [MaterialStateProperty] [ButtonStyle.foregroundColor] value in the same
  107   /// way that [defaultStyleOf] uses the [ColorScheme] colors with the same
  108   /// names. Specify a value for [primary] to specify the color of the button's
  109   /// text and icons as well as the overlay colors used to indicate the hover,
  110   /// focus, and pressed states. Use [onSurface] to specify the button's
  111   /// disabled text and icon color.
  112   ///
  113   /// Similarly, the [enabledMouseCursor] and [disabledMouseCursor]
  114   /// parameters are used to construct [ButtonStyle.mouseCursor].
  115   ///
  116   /// All of the other parameters are either used directly or used to
  117   /// create a [MaterialStateProperty] with a single value for all
  118   /// states.
  119   ///
  120   /// All parameters default to null. By default this method returns
  121   /// a [ButtonStyle] that doesn't override anything.
  122   ///
  123   /// For example, to override the default text and icon colors for a
  124   /// [TextButton], as well as its overlay color, with all of the
  125   /// standard opacity adjustments for the pressed, focused, and
  126   /// hovered states, one could write:
  127   ///
  128   /// ```dart
  129   /// TextButton(
  130   ///   style: TextButton.styleFrom(primary: Colors.green),
  131   /// )
  132   /// ```
  133   static ButtonStyle styleFrom({
  134     Color primary,
  135     Color onSurface,
  136     Color backgroundColor,
  137     Color shadowColor,
  138     double elevation,
  139     TextStyle textStyle,
  140     EdgeInsetsGeometry padding,
  141     Size minimumSize,
  142     BorderSide side,
  143     OutlinedBorder shape,
  144     MouseCursor enabledMouseCursor,
  145     MouseCursor disabledMouseCursor,
  146     VisualDensity visualDensity,
  147     MaterialTapTargetSize tapTargetSize,
  148     Duration animationDuration,
  149     bool enableFeedback,
  150   }) {
  151     final MaterialStateProperty<Color> foregroundColor = (onSurface == null && primary == null)
  152       ? null
  153       : _TextButtonDefaultForeground(primary, onSurface);
  154     final MaterialStateProperty<Color> overlayColor = (primary == null)
  155       ? null
  156       : _TextButtonDefaultOverlay(primary);
  157     final MaterialStateProperty<MouseCursor> mouseCursor = (enabledMouseCursor == null && disabledMouseCursor == null)
  158       ? null
  159       : _TextButtonDefaultMouseCursor(enabledMouseCursor, disabledMouseCursor);
  160 
  161     return ButtonStyle(
  162       textStyle: ButtonStyleButton.allOrNull<TextStyle>(textStyle),
  163       backgroundColor: ButtonStyleButton.allOrNull<Color>(backgroundColor),
  164       foregroundColor: foregroundColor,
  165       overlayColor: overlayColor,
  166       shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
  167       elevation: ButtonStyleButton.allOrNull<double>(elevation),
  168       padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
  169       minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
  170       side: ButtonStyleButton.allOrNull<BorderSide>(side),
  171       shape: ButtonStyleButton.allOrNull<OutlinedBorder>(shape),
  172       mouseCursor: mouseCursor,
  173       visualDensity: visualDensity,
  174       tapTargetSize: tapTargetSize,
  175       animationDuration: animationDuration,
  176       enableFeedback: enableFeedback,
  177     );
  178   }
  179 
  180   /// Defines the button's default appearance.
  181   ///
  182   /// The button [child]'s [Text] and [Icon] widgets are rendered with
  183   /// the [ButtonStyle]'s foreground color. The button's [InkWell] adds
  184   /// the style's overlay color when the button is focused, hovered
  185   /// or pressed. The button's background color becomes its [Material]
  186   /// color and is transparent by default.
  187   ///
  188   /// All of the ButtonStyle's defaults appear below.
  189   ///
  190   /// In this list "Theme.foo" is shorthand for
  191   /// `Theme.of(context).foo`. Color scheme values like
  192   /// "onSurface(0.38)" are shorthand for
  193   /// `onSurface.withOpacity(0.38)`. [MaterialStateProperty] valued
  194   /// properties that are not followed by by a sublist have the same
  195   /// value for all states, otherwise the values are as specified for
  196   /// each state and "others" means all other states.
  197   ///
  198   /// The `textScaleFactor` is the value of
  199   /// `MediaQuery.of(context).textScaleFactor` and the names of the
  200   /// EdgeInsets constructors and `EdgeInsetsGeometry.lerp` have been
  201   /// abbreviated for readability.
  202   ///
  203   /// The color of the [ButtonStyle.textStyle] is not used, the
  204   /// [ButtonStyle.foregroundColor] color is used instead.
  205   ///
  206   /// * `textStyle` - Theme.textTheme.button
  207   /// * `backgroundColor` - transparent
  208   /// * `foregroundColor`
  209   ///   * disabled - Theme.colorScheme.onSurface(0.38)
  210   ///   * others - Theme.colorScheme.primary
  211   /// * `overlayColor`
  212   ///   * hovered - Theme.colorScheme.primary(0.04)
  213   ///   * focused or pressed - Theme.colorScheme.primary(0.12)
  214   /// * `shadowColor` - Theme.shadowColor
  215   /// * `elevation` - 0
  216   /// * `padding`
  217   ///   * `textScaleFactor <= 1` - all(8)
  218   ///   * `1 < textScaleFactor <= 2` - lerp(all(8), horizontal(8))
  219   ///   * `2 < textScaleFactor <= 3` - lerp(horizontal(8), horizontal(4))
  220   ///   * `3 < textScaleFactor` - horizontal(4)
  221   /// * `minimumSize` - Size(64, 36)
  222   /// * `side` - BorderSide.none
  223   /// * `shape` - RoundedRectangleBorder(borderRadius: BorderRadius.circular(4))
  224   /// * `mouseCursor`
  225   ///   * disabled - SystemMouseCursors.forbidden
  226   ///   * others - SystemMouseCursors.click
  227   /// * `visualDensity` - theme.visualDensity
  228   /// * `tapTargetSize` - theme.materialTapTargetSize
  229   /// * `animationDuration` - kThemeChangeDuration
  230   /// * `enableFeedback` - true
  231   ///
  232   /// The default padding values for the [TextButton.icon] factory are slightly different:
  233   ///
  234   /// * `padding`
  235   ///   * `textScaleFactor <= 1` - all(8)
  236   ///   * `1 < textScaleFactor <= 2 `- lerp(all(8), horizontal(4))
  237   ///   * `2 < textScaleFactor` - horizontal(4)
  238   @override
  239   ButtonStyle defaultStyleOf(BuildContext context) {
  240     final ThemeData theme = Theme.of(context);
  241     final ColorScheme colorScheme = theme.colorScheme;
  242 
  243     final EdgeInsetsGeometry scaledPadding = ButtonStyleButton.scaledPadding(
  244       const EdgeInsets.all(8),
  245       const EdgeInsets.symmetric(horizontal: 8),
  246       const EdgeInsets.symmetric(horizontal: 4),
  247       MediaQuery.of(context, nullOk: true)?.textScaleFactor ?? 1,
  248     );
  249 
  250     return styleFrom(
  251       primary: colorScheme.primary,
  252       onSurface: colorScheme.onSurface,
  253       backgroundColor: Colors.transparent,
  254       shadowColor: theme.shadowColor,
  255       elevation: 0,
  256       textStyle: theme.textTheme.button,
  257       padding: scaledPadding,
  258       minimumSize: const Size(64, 36),
  259       side: BorderSide.none,
  260       shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))),
  261       enabledMouseCursor: SystemMouseCursors.click,
  262       disabledMouseCursor: SystemMouseCursors.forbidden,
  263       visualDensity: theme.visualDensity,
  264       tapTargetSize: theme.materialTapTargetSize,
  265       animationDuration: kThemeChangeDuration,
  266       enableFeedback: true,
  267     );
  268   }
  269 
  270   /// Returns the [TextButtonThemeData.style] of the closest
  271   /// [TextButtonTheme] ancestor.
  272   @override
  273   ButtonStyle themeStyleOf(BuildContext context) {
  274     return TextButtonTheme.of(context)?.style;
  275   }
  276 }
  277 
  278 @immutable
  279 class _TextButtonDefaultForeground extends MaterialStateProperty<Color> {
  280   _TextButtonDefaultForeground(this.primary, this.onSurface);
  281 
  282   final Color primary;
  283   final Color onSurface;
  284 
  285   @override
  286   Color resolve(Set<MaterialState> states) {
  287     if (states.contains(MaterialState.disabled))
  288       return onSurface?.withOpacity(0.38);
  289     return primary;
  290   }
  291 
  292   @override
  293   String toString() {
  294     return '{disabled: ${onSurface?.withOpacity(0.38)}, otherwise: $primary}';
  295   }
  296 }
  297 
  298 @immutable
  299 class _TextButtonDefaultOverlay extends MaterialStateProperty<Color> {
  300   _TextButtonDefaultOverlay(this.primary);
  301 
  302   final Color primary;
  303 
  304   @override
  305   Color resolve(Set<MaterialState> states) {
  306     if (states.contains(MaterialState.hovered))
  307       return primary?.withOpacity(0.04);
  308     if (states.contains(MaterialState.focused) || states.contains(MaterialState.pressed))
  309       return primary?.withOpacity(0.12);
  310     return null;
  311   }
  312 
  313   @override
  314   String toString() {
  315     return '{hovered: ${primary?.withOpacity(0.04)}, focused,pressed: ${primary?.withOpacity(0.12)}, otherwise: null}';
  316   }
  317 }
  318 
  319 @immutable
  320 class _TextButtonDefaultMouseCursor extends MaterialStateProperty<MouseCursor> with Diagnosticable {
  321   _TextButtonDefaultMouseCursor(this.enabledCursor, this.disabledCursor);
  322 
  323   final MouseCursor enabledCursor;
  324   final MouseCursor disabledCursor;
  325 
  326   @override
  327   MouseCursor resolve(Set<MaterialState> states) {
  328     if (states.contains(MaterialState.disabled))
  329       return disabledCursor;
  330     return enabledCursor;
  331   }
  332 }
  333 
  334 class _TextButtonWithIcon extends TextButton {
  335   _TextButtonWithIcon({
  336     Key key,
  337     @required VoidCallback onPressed,
  338     VoidCallback onLongPress,
  339     ButtonStyle style,
  340     FocusNode focusNode,
  341     bool autofocus,
  342     Clip clipBehavior,
  343     @required Widget icon,
  344     @required Widget label,
  345   }) : assert(icon != null),
  346        assert(label != null),
  347        super(
  348          key: key,
  349          onPressed: onPressed,
  350          onLongPress: onLongPress,
  351          style: style,
  352          focusNode: focusNode,
  353          autofocus: autofocus ?? false,
  354          clipBehavior: clipBehavior ?? Clip.none,
  355          child: _TextButtonWithIconChild(icon: icon, label: label),
  356       );
  357 
  358   @override
  359   ButtonStyle defaultStyleOf(BuildContext context) {
  360     final EdgeInsetsGeometry scaledPadding = ButtonStyleButton.scaledPadding(
  361       const EdgeInsets.all(8),
  362       const EdgeInsets.symmetric(horizontal: 4),
  363       const EdgeInsets.symmetric(horizontal: 4),
  364       MediaQuery.of(context, nullOk: true)?.textScaleFactor ?? 1,
  365     );
  366     return super.defaultStyleOf(context).copyWith(
  367       padding: MaterialStateProperty.all<EdgeInsetsGeometry>(scaledPadding)
  368     );
  369   }
  370 }
  371 
  372 class _TextButtonWithIconChild extends StatelessWidget {
  373   const _TextButtonWithIconChild({ Key key, this.label, this.icon }) : super(key: key);
  374 
  375   final Widget label;
  376   final Widget icon;
  377 
  378   @override
  379   Widget build(BuildContext context) {
  380     final double scale = MediaQuery.of(context, nullOk: true)?.textScaleFactor ?? 1;
  381     final double gap = scale <= 1 ? 8 : lerpDouble(8, 4, math.min(scale - 1, 1));
  382     return Row(
  383       mainAxisSize: MainAxisSize.min,
  384       children: <Widget>[icon, SizedBox(width: gap), label],
  385     );
  386   }
  387 }