LCOV - code coverage report
Current view: top level - button - mongol_elevated_button.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 74 0.0 %
Date: 2021-08-02 17:55:49 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright 2014 The Flutter Authors.
       2             : // Copyright 2021 Suragch.
       3             : // All rights reserved.
       4             : // Use of this source code is governed by a BSD-style license that can be
       5             : // found in the LICENSE file.
       6             : 
       7             : import 'dart:math' as math;
       8             : import 'dart:ui' show lerpDouble;
       9             : 
      10             : import 'package:flutter/foundation.dart';
      11             : import 'package:flutter/widgets.dart';
      12             : import 'package:flutter/material.dart'
      13             :     show
      14             :         ButtonStyle,
      15             :         ButtonStyleButton,
      16             :         ColorScheme,
      17             :         ElevatedButtonTheme,
      18             :         InkRipple,
      19             :         InteractiveInkFeatureFactory,
      20             :         MaterialState,
      21             :         MaterialStateProperty,
      22             :         MaterialTapTargetSize,
      23             :         Theme,
      24             :         ThemeData,
      25             :         VisualDensity,
      26             :         kThemeChangeDuration;
      27             : 
      28             : /// A vertical Material Design "elevated button".
      29             : ///
      30             : /// Use elevated buttons to add dimension to otherwise mostly flat
      31             : /// layouts, e.g.  in long busy lists of content, or in tall
      32             : /// spaces. Avoid using elevated buttons on already-elevated content
      33             : /// such as dialogs or cards.
      34             : ///
      35             : /// An elevated button is a label [child] displayed on a [Material]
      36             : /// widget whose [Material.elevation] increases when the button is
      37             : /// pressed. The label's [MongolText] and [Icon] widgets are displayed in
      38             : /// [style]'s [ButtonStyle.foregroundColor] and the button's filled
      39             : /// background is the [ButtonStyle.backgroundColor].
      40             : ///
      41             : /// The elevated button's default style is defined by
      42             : /// [defaultStyleOf].  The style of this elevated button can be
      43             : /// overridden with its [style] parameter. The style of all elevated
      44             : /// buttons in a subtree can be overridden with the
      45             : /// [ElevatedButtonTheme], and the style of all of the elevated
      46             : /// buttons in an app can be overridden with the [Theme]'s
      47             : /// [ThemeData.elevatedButtonTheme] property.
      48             : ///
      49             : /// The static [styleFrom] method is a convenient way to create a
      50             : /// elevated button [ButtonStyle] from simple values.
      51             : ///
      52             : /// If [onPressed] and [onLongPress] callbacks are null, then the
      53             : /// button will be disabled.
      54             : ///
      55             : /// {@tool dartpad --template=stateful_widget_scaffold}
      56             : ///
      57             : /// This sample produces an enabled and a disabled ElevatedButton.
      58             : ///
      59             : /// ```dart
      60             : /// @override
      61             : /// Widget build(BuildContext context) {
      62             : ///   final ButtonStyle style =
      63             : ///     MongolElevatedButton.styleFrom(textStyle: const TextStyle(fontSize: 20));
      64             : ///
      65             : ///   return Center(
      66             : ///     child: Column(
      67             : ///       mainAxisSize: MainAxisSize.min,
      68             : ///       children: <Widget>[
      69             : ///         MongolElevatedButton(
      70             : ///            style: style,
      71             : ///            onPressed: null,
      72             : ///            child: const Text('Disabled'),
      73             : ///         ),
      74             : ///         const SizedBox(height: 30),
      75             : ///         MongolElevatedButton(
      76             : ///           style: style,
      77             : ///           onPressed: () {},
      78             : ///           child: const Text('Enabled'),
      79             : ///         ),
      80             : ///       ],
      81             : ///     ),
      82             : ///   );
      83             : /// }
      84             : ///
      85             : /// ```
      86             : /// {@end-tool}
      87             : ///
      88             : /// See also:
      89             : ///
      90             : ///  * [MongolTextButton], a simple flat button without a shadow.
      91             : ///  * [MongolOutlinedButton], a [MongolTextButton] with a border outline.
      92             : ///  * <https://material.io/design/components/buttons.html>
      93             : class MongolElevatedButton extends ButtonStyleButton {
      94             :   /// Create a MongolElevatedButton.
      95             :   ///
      96             :   /// The [autofocus] and [clipBehavior] arguments must not be null.
      97           0 :   const MongolElevatedButton({
      98             :     Key? key,
      99             :     required VoidCallback? onPressed,
     100             :     VoidCallback? onLongPress,
     101             :     ButtonStyle? style,
     102             :     FocusNode? focusNode,
     103             :     bool autofocus = false,
     104             :     Clip clipBehavior = Clip.none,
     105             :     required Widget? child,
     106           0 :   }) : super(
     107             :           key: key,
     108             :           onPressed: onPressed,
     109             :           onLongPress: onLongPress,
     110             :           style: style,
     111             :           focusNode: focusNode,
     112             :           autofocus: autofocus,
     113             :           clipBehavior: clipBehavior,
     114             :           child: child,
     115             :         );
     116             : 
     117             :   /// Create an elevated button from a pair of widgets that serve as the button's
     118             :   /// [icon] and [label].
     119             :   ///
     120             :   /// The icon and label are arranged in a column and padded by 12 logical pixels
     121             :   /// at the start, and 16 at the end, with an 8 pixel gap in between.
     122             :   ///
     123             :   /// The [icon] and [label] arguments must not be null.
     124             :   factory MongolElevatedButton.icon({
     125             :     Key? key,
     126             :     required VoidCallback? onPressed,
     127             :     VoidCallback? onLongPress,
     128             :     ButtonStyle? style,
     129             :     FocusNode? focusNode,
     130             :     bool? autofocus,
     131             :     Clip? clipBehavior,
     132             :     required Widget icon,
     133             :     required Widget label,
     134             :   }) = _MongolElevatedButtonWithIcon;
     135             : 
     136             :   /// A static convenience method that constructs an elevated button
     137             :   /// [ButtonStyle] given simple values.
     138             :   ///
     139             :   /// The [onPrimary], and [onSurface] colors are used to create a
     140             :   /// [MaterialStateProperty] [ButtonStyle.foregroundColor] value in the same
     141             :   /// way that [defaultStyleOf] uses the [ColorScheme] colors with the same
     142             :   /// names. Specify a value for [onPrimary] to specify the color of the
     143             :   /// button's text and icons as well as the overlay colors used to indicate the
     144             :   /// hover, focus, and pressed states. Use [primary] for the button's background
     145             :   /// fill color and [onSurface] to specify the button's disabled text, icon,
     146             :   /// and fill color.
     147             :   ///
     148             :   /// The button's elevations are defined relative to the [elevation]
     149             :   /// parameter. The disabled elevation is the same as the parameter
     150             :   /// value, [elevation] + 2 is used when the button is hovered
     151             :   /// or focused, and elevation + 6 is used when the button is pressed.
     152             :   ///
     153             :   /// Similarly, the [enabledMouseCursor] and [disabledMouseCursor]
     154             :   /// parameters are used to construct [ButtonStyle].mouseCursor.
     155             :   ///
     156             :   /// All of the other parameters are either used directly or used to
     157             :   /// create a [MaterialStateProperty] with a single value for all
     158             :   /// states.
     159             :   ///
     160             :   /// All parameters default to null, by default this method returns
     161             :   /// a [ButtonStyle] that doesn't override anything.
     162             :   ///
     163             :   /// For example, to override the default text and icon colors for a
     164             :   /// [MongolElevatedButton], as well as its overlay color, with all of the
     165             :   /// standard opacity adjustments for the pressed, focused, and
     166             :   /// hovered states, one could write:
     167             :   ///
     168             :   /// ```dart
     169             :   /// MongolElevatedButton(
     170             :   ///   style: MongolElevatedButton.styleFrom(primary: Colors.green),
     171             :   /// )
     172             :   /// ```
     173           0 :   static ButtonStyle styleFrom({
     174             :     Color? primary,
     175             :     Color? onPrimary,
     176             :     Color? onSurface,
     177             :     Color? shadowColor,
     178             :     double? elevation,
     179             :     TextStyle? textStyle,
     180             :     EdgeInsetsGeometry? padding,
     181             :     Size? minimumSize,
     182             :     Size? fixedSize,
     183             :     Size? maximumSize,
     184             :     BorderSide? side,
     185             :     OutlinedBorder? shape,
     186             :     MouseCursor? enabledMouseCursor,
     187             :     MouseCursor? disabledMouseCursor,
     188             :     VisualDensity? visualDensity,
     189             :     MaterialTapTargetSize? tapTargetSize,
     190             :     Duration? animationDuration,
     191             :     bool? enableFeedback,
     192             :     AlignmentGeometry? alignment,
     193             :     InteractiveInkFeatureFactory? splashFactory,
     194             :   }) {
     195             :     final MaterialStateProperty<Color?>? backgroundColor =
     196             :         (onSurface == null && primary == null)
     197             :             ? null
     198           0 :             : _ElevatedButtonDefaultBackground(primary, onSurface);
     199             :     final MaterialStateProperty<Color?>? foregroundColor =
     200             :         (onSurface == null && onPrimary == null)
     201             :             ? null
     202           0 :             : _ElevatedButtonDefaultForeground(onPrimary, onSurface);
     203             :     final MaterialStateProperty<Color?>? overlayColor =
     204           0 :         (onPrimary == null) ? null : _ElevatedButtonDefaultOverlay(onPrimary);
     205             :     final MaterialStateProperty<double>? elevationValue =
     206           0 :         (elevation == null) ? null : _ElevatedButtonDefaultElevation(elevation);
     207             :     final MaterialStateProperty<MouseCursor?>? mouseCursor =
     208             :         (enabledMouseCursor == null && disabledMouseCursor == null)
     209             :             ? null
     210           0 :             : _ElevatedButtonDefaultMouseCursor(
     211             :                 enabledMouseCursor, disabledMouseCursor);
     212             : 
     213           0 :     return ButtonStyle(
     214           0 :       textStyle: MaterialStateProperty.all<TextStyle?>(textStyle),
     215             :       backgroundColor: backgroundColor,
     216             :       foregroundColor: foregroundColor,
     217             :       overlayColor: overlayColor,
     218           0 :       shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
     219             :       elevation: elevationValue,
     220           0 :       padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
     221           0 :       minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
     222           0 :       fixedSize: ButtonStyleButton.allOrNull<Size>(fixedSize),
     223             :       // TODO: add when becomes available in stable channel
     224             :       //maximumSize: ButtonStyleButton.allOrNull<Size>(maximumSize),
     225           0 :       side: ButtonStyleButton.allOrNull<BorderSide>(side),
     226           0 :       shape: ButtonStyleButton.allOrNull<OutlinedBorder>(shape),
     227             :       mouseCursor: mouseCursor,
     228             :       visualDensity: visualDensity,
     229             :       tapTargetSize: tapTargetSize,
     230             :       animationDuration: animationDuration,
     231             :       enableFeedback: enableFeedback,
     232             :       alignment: alignment,
     233             :       splashFactory: splashFactory,
     234             :     );
     235             :   }
     236             : 
     237             :   /// Defines the button's default appearance.
     238             :   ///
     239             :   /// The button [child]'s [MongolText] and [Icon] widgets are rendered with
     240             :   /// the [ButtonStyle]'s foreground color. The button's [InkWell] adds
     241             :   /// the style's overlay color when the button is focused, hovered
     242             :   /// or pressed. The button's background color becomes its [Material]
     243             :   /// color.
     244             :   ///
     245             :   /// All of the ButtonStyle's defaults appear below. In this list
     246             :   /// "Theme.foo" is shorthand for `Theme.of(context).foo`. Color
     247             :   /// scheme values like "onSurface(0.38)" are shorthand for
     248             :   /// `onSurface.withOpacity(0.38)`. [MaterialStateProperty] valued
     249             :   /// properties that are not followed by a sublist have the same
     250             :   /// value for all states, otherwise the values are as specified for
     251             :   /// each state, and "others" means all other states.
     252             :   ///
     253             :   /// The `textScaleFactor` is the value of
     254             :   /// `MediaQuery.of(context).textScaleFactor` and the names of the
     255             :   /// EdgeInsets constructors and `EdgeInsetsGeometry.lerp` have been
     256             :   /// abbreviated for readability.
     257             :   ///
     258             :   /// The color of the [ButtonStyle.textStyle] is not used, the
     259             :   /// [ButtonStyle.foregroundColor] color is used instead.
     260             :   ///
     261             :   /// * `textStyle` - Theme.textTheme.button
     262             :   /// * `backgroundColor`
     263             :   ///   * disabled - Theme.colorScheme.onSurface(0.12)
     264             :   ///   * others - Theme.colorScheme.primary
     265             :   /// * `foregroundColor`
     266             :   ///   * disabled - Theme.colorScheme.onSurface(0.38)
     267             :   ///   * others - Theme.colorScheme.onPrimary
     268             :   /// * `overlayColor`
     269             :   ///   * hovered - Theme.colorScheme.onPrimary(0.08)
     270             :   ///   * focused or pressed - Theme.colorScheme.onPrimary(0.24)
     271             :   /// * `shadowColor` - Theme.shadowColor
     272             :   /// * `elevation`
     273             :   ///   * disabled - 0
     274             :   ///   * default - 2
     275             :   ///   * hovered or focused - 4
     276             :   ///   * pressed - 8
     277             :   /// * `padding`
     278             :   ///   * textScaleFactor <= 1 - vertical(16)
     279             :   ///   * `1 < textScaleFactor <= 2` - lerp(vertical(16), vertical(8))
     280             :   ///   * `2 < textScaleFactor <= 3` - lerp(vertical(8), vertical(4))
     281             :   ///   * `3 < textScaleFactor` - vertical(4)
     282             :   /// * `minimumSize` - Size(36, 64)
     283             :   /// * `fixedSize` - null
     284             :   /// * `maximumSize` - Size.infinite
     285             :   /// * `side` - null
     286             :   /// * `shape` - RoundedRectangleBorder(borderRadius: BorderRadius.circular(4))
     287             :   /// * `mouseCursor`
     288             :   ///   * disabled - SystemMouseCursors.forbidden
     289             :   ///   * others - SystemMouseCursors.click
     290             :   /// * `visualDensity` - theme.visualDensity
     291             :   /// * `tapTargetSize` - theme.materialTapTargetSize
     292             :   /// * `animationDuration` - kThemeChangeDuration
     293             :   /// * `enableFeedback` - true
     294             :   /// * `alignment` - Alignment.center
     295             :   /// * `splashFactory` - InkRipple.splashFactory
     296             :   ///
     297             :   /// The default padding values for the [ElevatedButton.icon] factory are slightly different:
     298             :   ///
     299             :   /// * `padding`
     300             :   ///   * `textScaleFactor <= 1` - start(12) end(16)
     301             :   ///   * `1 < textScaleFactor <= 2` - lerp(start(12) end(16), vertical(8))
     302             :   ///   * `2 < textScaleFactor <= 3` - lerp(vertical(8), vertical(4))
     303             :   ///   * `3 < textScaleFactor` - vertical(4)
     304             :   ///
     305             :   /// The default value for `side`, which defines the appearance of the button's
     306             :   /// outline, is null. That means that the outline is defined by the button
     307             :   /// shape's [OutlinedBorder.side]. Typically the default value of an
     308             :   /// [OutlinedBorder]'s side is [BorderSide.none], so an outline is not drawn.
     309           0 :   @override
     310             :   ButtonStyle defaultStyleOf(BuildContext context) {
     311           0 :     final ThemeData theme = Theme.of(context);
     312           0 :     final ColorScheme colorScheme = theme.colorScheme;
     313             : 
     314           0 :     final EdgeInsetsGeometry scaledPadding = ButtonStyleButton.scaledPadding(
     315             :       const EdgeInsets.symmetric(vertical: 16),
     316             :       const EdgeInsets.symmetric(vertical: 8),
     317             :       const EdgeInsets.symmetric(vertical: 4),
     318           0 :       MediaQuery.maybeOf(context)?.textScaleFactor ?? 1,
     319             :     );
     320             : 
     321           0 :     return styleFrom(
     322           0 :       primary: colorScheme.primary,
     323           0 :       onPrimary: colorScheme.onPrimary,
     324           0 :       onSurface: colorScheme.onSurface,
     325           0 :       shadowColor: theme.shadowColor,
     326             :       elevation: 2,
     327           0 :       textStyle: theme.textTheme.button,
     328             :       padding: scaledPadding,
     329             :       minimumSize: const Size(36, 64),
     330             :       maximumSize: Size.infinite,
     331             :       side: null,
     332             :       shape: const RoundedRectangleBorder(
     333             :           borderRadius: BorderRadius.all(Radius.circular(4))),
     334             :       enabledMouseCursor: SystemMouseCursors.click,
     335             :       disabledMouseCursor: SystemMouseCursors.forbidden,
     336           0 :       visualDensity: theme.visualDensity,
     337           0 :       tapTargetSize: theme.materialTapTargetSize,
     338             :       animationDuration: kThemeChangeDuration,
     339             :       enableFeedback: true,
     340             :       alignment: Alignment.center,
     341             :       splashFactory: InkRipple.splashFactory,
     342             :     );
     343             :   }
     344             : 
     345             :   /// Returns the [ElevatedButtonThemeData.style] of the closest
     346             :   /// [ElevatedButtonTheme] ancestor.
     347           0 :   @override
     348             :   ButtonStyle? themeStyleOf(BuildContext context) {
     349           0 :     return ElevatedButtonTheme.of(context).style;
     350             :   }
     351             : }
     352             : 
     353             : @immutable
     354             : class _ElevatedButtonDefaultBackground extends MaterialStateProperty<Color?>
     355             :     with Diagnosticable {
     356           0 :   _ElevatedButtonDefaultBackground(this.primary, this.onSurface);
     357             : 
     358             :   final Color? primary;
     359             :   final Color? onSurface;
     360             : 
     361           0 :   @override
     362             :   Color? resolve(Set<MaterialState> states) {
     363           0 :     if (states.contains(MaterialState.disabled)) {
     364           0 :       return onSurface?.withOpacity(0.12);
     365             :     }
     366           0 :     return primary;
     367             :   }
     368             : }
     369             : 
     370             : @immutable
     371             : class _ElevatedButtonDefaultForeground extends MaterialStateProperty<Color?>
     372             :     with Diagnosticable {
     373           0 :   _ElevatedButtonDefaultForeground(this.onPrimary, this.onSurface);
     374             : 
     375             :   final Color? onPrimary;
     376             :   final Color? onSurface;
     377             : 
     378           0 :   @override
     379             :   Color? resolve(Set<MaterialState> states) {
     380           0 :     if (states.contains(MaterialState.disabled)) {
     381           0 :       return onSurface?.withOpacity(0.38);
     382             :     }
     383           0 :     return onPrimary;
     384             :   }
     385             : }
     386             : 
     387             : @immutable
     388             : class _ElevatedButtonDefaultOverlay extends MaterialStateProperty<Color?>
     389             :     with Diagnosticable {
     390           0 :   _ElevatedButtonDefaultOverlay(this.onPrimary);
     391             : 
     392             :   final Color onPrimary;
     393             : 
     394           0 :   @override
     395             :   Color? resolve(Set<MaterialState> states) {
     396           0 :     if (states.contains(MaterialState.hovered)) {
     397           0 :       return onPrimary.withOpacity(0.08);
     398             :     }
     399           0 :     if (states.contains(MaterialState.focused) ||
     400           0 :         states.contains(MaterialState.pressed)) {
     401           0 :       return onPrimary.withOpacity(0.24);
     402             :     }
     403             :     return null;
     404             :   }
     405             : }
     406             : 
     407             : @immutable
     408             : class _ElevatedButtonDefaultElevation extends MaterialStateProperty<double>
     409             :     with Diagnosticable {
     410           0 :   _ElevatedButtonDefaultElevation(this.elevation);
     411             : 
     412             :   final double elevation;
     413             : 
     414           0 :   @override
     415             :   double resolve(Set<MaterialState> states) {
     416           0 :     if (states.contains(MaterialState.disabled)) return 0;
     417           0 :     if (states.contains(MaterialState.hovered)) return elevation + 2;
     418           0 :     if (states.contains(MaterialState.focused)) return elevation + 2;
     419           0 :     if (states.contains(MaterialState.pressed)) return elevation + 6;
     420           0 :     return elevation;
     421             :   }
     422             : }
     423             : 
     424             : @immutable
     425             : class _ElevatedButtonDefaultMouseCursor
     426             :     extends MaterialStateProperty<MouseCursor?> with Diagnosticable {
     427           0 :   _ElevatedButtonDefaultMouseCursor(this.enabledCursor, this.disabledCursor);
     428             : 
     429             :   final MouseCursor? enabledCursor;
     430             :   final MouseCursor? disabledCursor;
     431             : 
     432           0 :   @override
     433             :   MouseCursor? resolve(Set<MaterialState> states) {
     434           0 :     if (states.contains(MaterialState.disabled)) return disabledCursor;
     435           0 :     return enabledCursor;
     436             :   }
     437             : }
     438             : 
     439             : class _MongolElevatedButtonWithIcon extends MongolElevatedButton {
     440           0 :   _MongolElevatedButtonWithIcon({
     441             :     Key? key,
     442             :     required VoidCallback? onPressed,
     443             :     VoidCallback? onLongPress,
     444             :     ButtonStyle? style,
     445             :     FocusNode? focusNode,
     446             :     bool? autofocus,
     447             :     Clip? clipBehavior,
     448             :     required Widget icon,
     449             :     required Widget label,
     450           0 :   }) : super(
     451             :           key: key,
     452             :           onPressed: onPressed,
     453             :           onLongPress: onLongPress,
     454             :           style: style,
     455             :           focusNode: focusNode,
     456             :           autofocus: autofocus ?? false,
     457             :           clipBehavior: clipBehavior ?? Clip.none,
     458           0 :           child: _MongolElevatedButtonWithIconChild(icon: icon, label: label),
     459             :         );
     460             : 
     461           0 :   @override
     462             :   ButtonStyle defaultStyleOf(BuildContext context) {
     463           0 :     final EdgeInsetsGeometry scaledPadding = ButtonStyleButton.scaledPadding(
     464             :       const EdgeInsets.fromLTRB(0, 12, 0, 16),
     465             :       const EdgeInsets.symmetric(vertical: 8),
     466             :       const EdgeInsets.fromLTRB(0, 8, 0, 4),
     467           0 :       MediaQuery.maybeOf(context)?.textScaleFactor ?? 1,
     468             :     );
     469           0 :     return super.defaultStyleOf(context).copyWith(
     470           0 :           padding: MaterialStateProperty.all<EdgeInsetsGeometry>(scaledPadding),
     471             :         );
     472             :   }
     473             : }
     474             : 
     475             : class _MongolElevatedButtonWithIconChild extends StatelessWidget {
     476           0 :   const _MongolElevatedButtonWithIconChild(
     477             :       {Key? key, required this.label, required this.icon})
     478           0 :       : super(key: key);
     479             : 
     480             :   final Widget label;
     481             :   final Widget icon;
     482             : 
     483           0 :   @override
     484             :   Widget build(BuildContext context) {
     485           0 :     final double scale = MediaQuery.maybeOf(context)?.textScaleFactor ?? 1;
     486             :     final double gap =
     487           0 :         scale <= 1 ? 8 : lerpDouble(8, 4, math.min(scale - 1, 1))!;
     488           0 :     return Column(
     489             :       mainAxisSize: MainAxisSize.min,
     490           0 :       children: <Widget>[icon, SizedBox(height: gap), Flexible(child: label)],
     491             :     );
     492             :   }
     493             : }

Generated by: LCOV version 1.15