text_button.dart 22.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// 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:math' as math;
import 'dart:ui' show lerpDouble;

import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';

import 'button_style.dart';
import 'button_style_button.dart';
import 'color_scheme.dart';
import 'colors.dart';
import 'constants.dart';
16 17
import 'ink_ripple.dart';
import 'ink_well.dart';
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
import 'material_state.dart';
import 'text_button_theme.dart';
import 'theme.dart';
import 'theme_data.dart';

/// A Material Design "Text Button".
///
/// Use text buttons on toolbars, in dialogs, or inline with other
/// content but offset from that content with padding so that the
/// button's presence is obvious. Text buttons do not have visible
/// borders and must therefore rely on their position relative to
/// other content for context. In dialogs and cards, they should be
/// grouped together in one of the bottom corners. Avoid using text
/// buttons where they would blend in with other content, for example
/// in the middle of lists.
///
/// A text button is a label [child] displayed on a (zero elevation)
/// [Material] widget. The label's [Text] and [Icon] widgets are
/// displayed in the [style]'s [ButtonStyle.foregroundColor]. The
/// button reacts to touches by filling with the [style]'s
/// [ButtonStyle.backgroundColor].
///
/// The text button's default style is defined by [defaultStyleOf].
/// The style of this text button can be overridden with its [style]
/// parameter. The style of all text buttons in a subtree can be
/// overridden with the [TextButtonTheme] and the style of all of the
/// text buttons in an app can be overridden with the [Theme]'s
/// [ThemeData.textButtonTheme] property.
///
/// The static [styleFrom] method is a convenient way to create a
/// text button [ButtonStyle] from simple values.
///
/// If the [onPressed] and [onLongPress] callbacks are null, then this
/// button will be disabled, it will not react to touch.
///
53
/// {@tool dartpad}
54 55 56
/// This sample shows how to render a disabled TextButton, an enabled TextButton
/// and lastly a TextButton with gradient background.
///
57
/// ** See code in examples/api/lib/material/text_button/text_button.0.dart **
58 59
/// {@end-tool}
///
60 61 62 63 64 65 66
/// {@tool dartpad}
/// This sample demonstrates using the [statesController] parameter to create a button
/// that adds support for [MaterialState.selected].
///
/// ** See code in examples/api/lib/material/text_button/text_button.1.dart **
/// {@end-tool}
///
67 68
/// See also:
///
69
///  * [ElevatedButton], a filled button whose material elevates when pressed.
70 71 72
///  * [FilledButton], a filled button that doesn't elevate when pressed.
///  * [FilledButton.tonal], a filled button variant that uses a secondary fill color.
///  * [OutlinedButton], a button with an outlined border and no fill color.
73
///  * <https://material.io/design/components/buttons.html>
74
///  * <https://m3.material.io/components/buttons>
75 76 77 78 79
class TextButton extends ButtonStyleButton {
  /// Create a TextButton.
  ///
  /// The [autofocus] and [clipBehavior] arguments must not be null.
  const TextButton({
80 81 82 83 84 85 86 87 88
    super.key,
    required super.onPressed,
    super.onLongPress,
    super.onHover,
    super.onFocusChange,
    super.style,
    super.focusNode,
    super.autofocus = false,
    super.clipBehavior = Clip.none,
89
    super.statesController,
90
    super.isSemanticButton,
91 92
    required Widget super.child,
  });
93 94 95 96 97 98 99 100 101

  /// Create a text 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 8 logical pixels
  /// at the ends, with an 8 pixel gap in between.
  ///
  /// The [icon] and [label] arguments must not be null.
  factory TextButton.icon({
102 103 104
    Key? key,
    required VoidCallback? onPressed,
    VoidCallback? onLongPress,
105 106
    ValueChanged<bool>? onHover,
    ValueChanged<bool>? onFocusChange,
107 108 109 110
    ButtonStyle? style,
    FocusNode? focusNode,
    bool? autofocus,
    Clip? clipBehavior,
111
    MaterialStatesController? statesController,
112 113
    required Widget icon,
    required Widget label,
114 115 116 117 118
  }) = _TextButtonWithIcon;

  /// A static convenience method that constructs a text button
  /// [ButtonStyle] given simple values.
  ///
119 120 121 122 123 124
  /// The [foregroundColor] and [disabledForegroundColor] colors are used
  /// to create a [MaterialStateProperty] [ButtonStyle.foregroundColor], and
  /// a derived [ButtonStyle.overlayColor].
  ///
  /// The [backgroundColor] and [disabledBackgroundColor] colors are
  /// used to create a [MaterialStateProperty] [ButtonStyle.backgroundColor].
125 126
  ///
  /// Similarly, the [enabledMouseCursor] and [disabledMouseCursor]
127
  /// parameters are used to construct [ButtonStyle.mouseCursor].
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
  ///
  /// All of the other parameters are either used directly or used to
  /// create a [MaterialStateProperty] with a single value for all
  /// states.
  ///
  /// All parameters default to null. By default this method returns
  /// a [ButtonStyle] that doesn't override anything.
  ///
  /// For example, to override the default text and icon colors for a
  /// [TextButton], as well as its overlay color, with all of the
  /// standard opacity adjustments for the pressed, focused, and
  /// hovered states, one could write:
  ///
  /// ```dart
  /// TextButton(
143
  ///   style: TextButton.styleFrom(foregroundColor: Colors.green),
144 145 146 147 148
  ///   child: const Text('Give Kate a mix tape'),
  ///   onPressed: () {
  ///     // ...
  ///   },
  /// ),
149 150
  /// ```
  static ButtonStyle styleFrom({
151
    Color? foregroundColor,
152
    Color? backgroundColor,
153 154
    Color? disabledForegroundColor,
    Color? disabledBackgroundColor,
155
    Color? shadowColor,
156
    Color? surfaceTintColor,
157 158
    Color? iconColor,
    Color? disabledIconColor,
159 160 161 162
    double? elevation,
    TextStyle? textStyle,
    EdgeInsetsGeometry? padding,
    Size? minimumSize,
163
    Size? fixedSize,
164
    Size? maximumSize,
165 166 167 168 169 170 171 172
    BorderSide? side,
    OutlinedBorder? shape,
    MouseCursor? enabledMouseCursor,
    MouseCursor? disabledMouseCursor,
    VisualDensity? visualDensity,
    MaterialTapTargetSize? tapTargetSize,
    Duration? animationDuration,
    bool? enableFeedback,
173
    AlignmentGeometry? alignment,
174
    InteractiveInkFeatureFactory? splashFactory,
175 176 177 178 179 180
    @Deprecated(
      'Use foregroundColor instead. '
      'This feature was deprecated after v3.1.0.'
    )
    Color? primary,
    @Deprecated(
181
      'Use disabledForegroundColor instead. '
182 183 184
      'This feature was deprecated after v3.1.0.'
    )
    Color? onSurface,
185
  }) {
186 187 188 189 190 191
    final Color? foreground = foregroundColor ?? primary;
    final Color? disabledForeground = disabledForegroundColor ?? onSurface?.withOpacity(0.38);
    final MaterialStateProperty<Color?>? foregroundColorProp = (foreground == null && disabledForeground == null)
      ? null
      : _TextButtonDefaultColor(foreground, disabledForeground);
    final MaterialStateProperty<Color?>? backgroundColorProp = (backgroundColor == null && disabledBackgroundColor == null)
192
      ? null
193 194 195 196
      : disabledBackgroundColor == null
        ? ButtonStyleButton.allOrNull<Color?>(backgroundColor)
        : _TextButtonDefaultColor(backgroundColor, disabledBackgroundColor);
    final MaterialStateProperty<Color?>? overlayColor = (foreground == null)
197
      ? null
198
      : _TextButtonDefaultOverlay(foreground);
199 200 201 202 203
    final MaterialStateProperty<Color?>? iconColorProp = (iconColor == null && disabledIconColor == null)
      ? null
      : disabledIconColor == null
        ? ButtonStyleButton.allOrNull<Color?>(iconColor)
        : _TextButtonDefaultIconColor(iconColor, disabledIconColor);
204
    final MaterialStateProperty<MouseCursor?> mouseCursor = _TextButtonDefaultMouseCursor(enabledMouseCursor, disabledMouseCursor);
205 206 207

    return ButtonStyle(
      textStyle: ButtonStyleButton.allOrNull<TextStyle>(textStyle),
208 209
      foregroundColor: foregroundColorProp,
      backgroundColor: backgroundColorProp,
210 211
      overlayColor: overlayColor,
      shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
212
      surfaceTintColor: ButtonStyleButton.allOrNull<Color>(surfaceTintColor),
213
      iconColor: iconColorProp,
214 215 216
      elevation: ButtonStyleButton.allOrNull<double>(elevation),
      padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
      minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
217
      fixedSize: ButtonStyleButton.allOrNull<Size>(fixedSize),
218
      maximumSize: ButtonStyleButton.allOrNull<Size>(maximumSize),
219 220 221 222 223 224 225
      side: ButtonStyleButton.allOrNull<BorderSide>(side),
      shape: ButtonStyleButton.allOrNull<OutlinedBorder>(shape),
      mouseCursor: mouseCursor,
      visualDensity: visualDensity,
      tapTargetSize: tapTargetSize,
      animationDuration: animationDuration,
      enableFeedback: enableFeedback,
226
      alignment: alignment,
227
      splashFactory: splashFactory,
228 229 230 231 232
    );
  }

  /// Defines the button's default appearance.
  ///
233
  /// {@template flutter.material.text_button.default_style_of}
234 235 236 237 238 239
  /// The button [child]'s [Text] and [Icon] widgets are rendered with
  /// the [ButtonStyle]'s foreground color. The button's [InkWell] adds
  /// the style's overlay color when the button is focused, hovered
  /// or pressed. The button's background color becomes its [Material]
  /// color and is transparent by default.
  ///
240
  /// All of the [ButtonStyle]'s defaults appear below.
241 242 243 244 245
  ///
  /// In this list "Theme.foo" is shorthand for
  /// `Theme.of(context).foo`. Color scheme values like
  /// "onSurface(0.38)" are shorthand for
  /// `onSurface.withOpacity(0.38)`. [MaterialStateProperty] valued
nt4f04uNd's avatar
nt4f04uNd committed
246
  /// properties that are not followed by a sublist have the same
247 248 249 250
  /// value for all states, otherwise the values are as specified for
  /// each state and "others" means all other states.
  ///
  /// The `textScaleFactor` is the value of
251
  /// `MediaQuery.textScalerOf(context).textScaleFactor` and the names of the
252 253 254
  /// EdgeInsets constructors and `EdgeInsetsGeometry.lerp` have been
  /// abbreviated for readability.
  ///
255 256
  /// The color of the [ButtonStyle.textStyle] is not used, the
  /// [ButtonStyle.foregroundColor] color is used instead.
257
  /// {@endtemplate}
258
  ///
259 260
  /// ## Material 2 defaults
  ///
261 262 263 264 265 266 267 268
  /// * `textStyle` - Theme.textTheme.button
  /// * `backgroundColor` - transparent
  /// * `foregroundColor`
  ///   * disabled - Theme.colorScheme.onSurface(0.38)
  ///   * others - Theme.colorScheme.primary
  /// * `overlayColor`
  ///   * hovered - Theme.colorScheme.primary(0.04)
  ///   * focused or pressed - Theme.colorScheme.primary(0.12)
269
  /// * `shadowColor` - Theme.shadowColor
270 271
  /// * `elevation` - 0
  /// * `padding`
272
  ///   * `textScaleFactor <= 1` - (horizontal(12), vertical(8))
273 274 275 276
  ///   * `1 < textScaleFactor <= 2` - lerp(all(8), horizontal(8))
  ///   * `2 < textScaleFactor <= 3` - lerp(horizontal(8), horizontal(4))
  ///   * `3 < textScaleFactor` - horizontal(4)
  /// * `minimumSize` - Size(64, 36)
277
  /// * `fixedSize` - null
278
  /// * `maximumSize` - Size.infinite
279
  /// * `side` - null
280 281
  /// * `shape` - RoundedRectangleBorder(borderRadius: BorderRadius.circular(4))
  /// * `mouseCursor`
282
  ///   * disabled - SystemMouseCursors.basic
283 284 285 286 287
  ///   * others - SystemMouseCursors.click
  /// * `visualDensity` - theme.visualDensity
  /// * `tapTargetSize` - theme.materialTapTargetSize
  /// * `animationDuration` - kThemeChangeDuration
  /// * `enableFeedback` - true
288
  /// * `alignment` - Alignment.center
289
  /// * `splashFactory` - InkRipple.splashFactory
290 291 292 293 294 295 296
  ///
  /// The default padding values for the [TextButton.icon] factory are slightly different:
  ///
  /// * `padding`
  ///   * `textScaleFactor <= 1` - all(8)
  ///   * `1 < textScaleFactor <= 2 `- lerp(all(8), horizontal(4))
  ///   * `2 < textScaleFactor` - horizontal(4)
297 298 299 300 301
  ///
  /// The default value for `side`, which defines the appearance of the button's
  /// outline, is null. That means that the outline is defined by the button
  /// shape's [OutlinedBorder.side]. Typically the default value of an
  /// [OutlinedBorder]'s side is [BorderSide.none], so an outline is not drawn.
302 303 304 305 306 307
  ///
  /// ## Material 3 defaults
  ///
  /// If [ThemeData.useMaterial3] is set to true the following defaults will
  /// be used:
  ///
308
  /// {@template flutter.material.text_button.material3_defaults}
309 310 311 312 313 314 315 316 317
  /// * `textStyle` - Theme.textTheme.labelLarge
  /// * `backgroundColor` - transparent
  /// * `foregroundColor`
  ///   * disabled - Theme.colorScheme.onSurface(0.38)
  ///   * others - Theme.colorScheme.primary
  /// * `overlayColor`
  ///   * hovered - Theme.colorScheme.primary(0.08)
  ///   * focused or pressed - Theme.colorScheme.primary(0.12)
  ///   * others - null
318
  /// * `shadowColor` - Colors.transparent,
319 320 321
  /// * `surfaceTintColor` - null
  /// * `elevation` - 0
  /// * `padding`
322
  ///   * `textScaleFactor <= 1` - lerp(horizontal(12), horizontal(4))
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
  ///   * `1 < textScaleFactor <= 2` - lerp(all(8), horizontal(8))
  ///   * `2 < textScaleFactor <= 3` - lerp(horizontal(8), horizontal(4))
  ///   * `3 < textScaleFactor` - horizontal(4)
  /// * `minimumSize` - Size(64, 40)
  /// * `fixedSize` - null
  /// * `maximumSize` - Size.infinite
  /// * `side` - null
  /// * `shape` - StadiumBorder()
  /// * `mouseCursor`
  ///   * disabled - SystemMouseCursors.basic
  ///   * others - SystemMouseCursors.click
  /// * `visualDensity` - theme.visualDensity
  /// * `tapTargetSize` - theme.materialTapTargetSize
  /// * `animationDuration` - kThemeChangeDuration
  /// * `enableFeedback` - true
  /// * `alignment` - Alignment.center
  /// * `splashFactory` - Theme.splashFactory
340 341 342
  ///
  /// For the [TextButton.icon] factory, the end (generally the right) value of
  /// [padding] is increased from 12 to 16.
343
  /// {@endtemplate}
344 345
  @override
  ButtonStyle defaultStyleOf(BuildContext context) {
346
    final ThemeData theme = Theme.of(context);
347 348
    final ColorScheme colorScheme = theme.colorScheme;

349
    return Theme.of(context).useMaterial3
350
      ? _TextButtonDefaultsM3(context)
351
      : styleFrom(
352 353
          foregroundColor: colorScheme.primary,
          disabledForegroundColor: colorScheme.onSurface.withOpacity(0.38),
354
          backgroundColor: Colors.transparent,
355
          disabledBackgroundColor: Colors.transparent,
356 357
          shadowColor: theme.shadowColor,
          elevation: 0,
358
          textStyle: theme.textTheme.labelLarge,
359 360 361 362 363 364 365 366 367 368 369 370 371
          padding: _scaledPadding(context),
          minimumSize: const Size(64, 36),
          maximumSize: Size.infinite,
          shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))),
          enabledMouseCursor: SystemMouseCursors.click,
          disabledMouseCursor: SystemMouseCursors.basic,
          visualDensity: theme.visualDensity,
          tapTargetSize: theme.materialTapTargetSize,
          animationDuration: kThemeChangeDuration,
          enableFeedback: true,
          alignment: Alignment.center,
          splashFactory: InkRipple.splashFactory,
        );
372 373 374 375 376
  }

  /// Returns the [TextButtonThemeData.style] of the closest
  /// [TextButtonTheme] ancestor.
  @override
377 378
  ButtonStyle? themeStyleOf(BuildContext context) {
    return TextButtonTheme.of(context).style;
379 380 381
  }
}

382
EdgeInsetsGeometry _scaledPadding(BuildContext context) {
383
  final bool useMaterial3 = Theme.of(context).useMaterial3;
384
  return ButtonStyleButton.scaledPadding(
385
    useMaterial3 ? const EdgeInsets.symmetric(horizontal: 12, vertical: 8) :  const EdgeInsets.all(8),
386 387
    const EdgeInsets.symmetric(horizontal: 8),
    const EdgeInsets.symmetric(horizontal: 4),
388
    MediaQuery.textScalerOf(context).textScaleFactor,
389 390 391
  );
}

392
@immutable
393 394
class _TextButtonDefaultColor extends MaterialStateProperty<Color?> {
  _TextButtonDefaultColor(this.color, this.disabled);
395

396 397
  final Color? color;
  final Color? disabled;
398 399

  @override
400
  Color? resolve(Set<MaterialState> states) {
401
    if (states.contains(MaterialState.disabled)) {
402
      return disabled;
403
    }
404
    return color;
405 406 407 408
  }

  @override
  String toString() {
409
    return '{disabled: $disabled, otherwise: $color}';
410 411 412 413
  }
}

@immutable
414
class _TextButtonDefaultOverlay extends MaterialStateProperty<Color?> {
415 416 417 418 419
  _TextButtonDefaultOverlay(this.primary);

  final Color primary;

  @override
420
  Color? resolve(Set<MaterialState> states) {
421 422 423
    if (states.contains(MaterialState.pressed)) {
      return primary.withOpacity(0.12);
    }
424
    if (states.contains(MaterialState.hovered)) {
425
      return primary.withOpacity(0.04);
426
    }
427
    if (states.contains(MaterialState.focused)) {
428
      return primary.withOpacity(0.12);
429
    }
430 431 432 433 434
    return null;
  }

  @override
  String toString() {
435
    return '{hovered: ${primary.withOpacity(0.04)}, focused,pressed: ${primary.withOpacity(0.12)}, otherwise: null}';
436 437 438
  }
}

439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
@immutable
class _TextButtonDefaultIconColor extends MaterialStateProperty<Color?> {
  _TextButtonDefaultIconColor(this.iconColor, this.disabledIconColor);

  final Color? iconColor;
  final Color? disabledIconColor;

  @override
  Color? resolve(Set<MaterialState> states) {
    if (states.contains(MaterialState.disabled)) {
      return disabledIconColor;
    }
    return iconColor;
  }

  @override
  String toString() {
    return '{disabled: $disabledIconColor, color: $iconColor}';
  }
}

460
@immutable
461
class _TextButtonDefaultMouseCursor extends MaterialStateProperty<MouseCursor?> with Diagnosticable {
462 463
  _TextButtonDefaultMouseCursor(this.enabledCursor, this.disabledCursor);

464 465
  final MouseCursor? enabledCursor;
  final MouseCursor? disabledCursor;
466 467

  @override
468
  MouseCursor? resolve(Set<MaterialState> states) {
469
    if (states.contains(MaterialState.disabled)) {
470
      return disabledCursor;
471
    }
472 473 474 475 476 477
    return enabledCursor;
  }
}

class _TextButtonWithIcon extends TextButton {
  _TextButtonWithIcon({
478 479 480 481 482 483 484
    super.key,
    required super.onPressed,
    super.onLongPress,
    super.onHover,
    super.onFocusChange,
    super.style,
    super.focusNode,
485 486
    bool? autofocus,
    Clip? clipBehavior,
487
    super.statesController,
488 489
    required Widget icon,
    required Widget label,
490
  }) : super(
491 492 493 494 495 496 497
         autofocus: autofocus ?? false,
         clipBehavior: clipBehavior ?? Clip.none,
         child: _TextButtonWithIconChild(icon: icon, label: label),
      );

  @override
  ButtonStyle defaultStyleOf(BuildContext context) {
498
    final bool useMaterial3 = Theme.of(context).useMaterial3;
499
    final EdgeInsetsGeometry scaledPadding = ButtonStyleButton.scaledPadding(
500
      useMaterial3 ? const EdgeInsetsDirectional.fromSTEB(12, 8, 16, 8) : const EdgeInsets.all(8),
501 502
      const EdgeInsets.symmetric(horizontal: 4),
      const EdgeInsets.symmetric(horizontal: 4),
503
      MediaQuery.textScalerOf(context).textScaleFactor,
504 505
    );
    return super.defaultStyleOf(context).copyWith(
506
      padding: MaterialStatePropertyAll<EdgeInsetsGeometry>(scaledPadding),
507 508 509 510 511
    );
  }
}

class _TextButtonWithIconChild extends StatelessWidget {
512 513 514
  const _TextButtonWithIconChild({
    required this.label,
    required this.icon,
515
  });
516 517 518 519 520 521

  final Widget label;
  final Widget icon;

  @override
  Widget build(BuildContext context) {
522
    final double scale = MediaQuery.textScalerOf(context).textScaleFactor;
523
    final double gap = scale <= 1 ? 8 : lerpDouble(8, 4, math.min(scale - 1, 1))!;
524 525
    return Row(
      mainAxisSize: MainAxisSize.min,
526
      children: <Widget>[icon, SizedBox(width: gap), Flexible(child: label)],
527 528 529
    );
  }
}
530

531
// BEGIN GENERATED TOKEN PROPERTIES - TextButton
532

533 534 535 536
// Do not edit by hand. The code between the "BEGIN GENERATED" and
// "END GENERATED" comments are generated from data in the Material
// Design token database by the script:
//   dev/tools/gen_defaults/bin/gen_defaults.dart.
537

538 539
class _TextButtonDefaultsM3 extends ButtonStyle {
  _TextButtonDefaultsM3(this.context)
540 541 542 543 544 545 546 547 548 549 550
   : super(
       animationDuration: kThemeChangeDuration,
       enableFeedback: true,
       alignment: Alignment.center,
     );

  final BuildContext context;
  late final ColorScheme _colors = Theme.of(context).colorScheme;

  @override
  MaterialStateProperty<TextStyle?> get textStyle =>
551
    MaterialStatePropertyAll<TextStyle?>(Theme.of(context).textTheme.labelLarge);
552 553 554

  @override
  MaterialStateProperty<Color?>? get backgroundColor =>
555
    const MaterialStatePropertyAll<Color>(Colors.transparent);
556 557 558 559

  @override
  MaterialStateProperty<Color?>? get foregroundColor =>
    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
560
      if (states.contains(MaterialState.disabled)) {
561
        return _colors.onSurface.withOpacity(0.38);
562
      }
563 564 565 566 567 568
      return _colors.primary;
    });

  @override
  MaterialStateProperty<Color?>? get overlayColor =>
    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
569 570 571
      if (states.contains(MaterialState.pressed)) {
        return _colors.primary.withOpacity(0.12);
      }
572
      if (states.contains(MaterialState.hovered)) {
573
        return _colors.primary.withOpacity(0.08);
574 575
      }
      if (states.contains(MaterialState.focused)) {
576
        return _colors.primary.withOpacity(0.12);
577
      }
578 579 580
      return null;
    });

581 582 583
  @override
  MaterialStateProperty<Color>? get shadowColor =>
    const MaterialStatePropertyAll<Color>(Colors.transparent);
584

585 586 587
  @override
  MaterialStateProperty<Color>? get surfaceTintColor =>
    const MaterialStatePropertyAll<Color>(Colors.transparent);
588 589 590

  @override
  MaterialStateProperty<double>? get elevation =>
591
    const MaterialStatePropertyAll<double>(0.0);
592 593 594

  @override
  MaterialStateProperty<EdgeInsetsGeometry>? get padding =>
595
    MaterialStatePropertyAll<EdgeInsetsGeometry>(_scaledPadding(context));
596 597 598

  @override
  MaterialStateProperty<Size>? get minimumSize =>
599
    const MaterialStatePropertyAll<Size>(Size(64.0, 40.0));
600 601 602 603 604

  // No default fixedSize

  @override
  MaterialStateProperty<Size>? get maximumSize =>
605
    const MaterialStatePropertyAll<Size>(Size.infinite);
606 607 608 609 610

  // No default side

  @override
  MaterialStateProperty<OutlinedBorder>? get shape =>
611
    const MaterialStatePropertyAll<OutlinedBorder>(StadiumBorder());
612 613 614 615

  @override
  MaterialStateProperty<MouseCursor?>? get mouseCursor =>
    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
616
      if (states.contains(MaterialState.disabled)) {
617
        return SystemMouseCursors.basic;
618
      }
619 620 621 622 623 624 625 626 627 628 629 630 631
      return SystemMouseCursors.click;
    });

  @override
  VisualDensity? get visualDensity => Theme.of(context).visualDensity;

  @override
  MaterialTapTargetSize? get tapTargetSize => Theme.of(context).materialTapTargetSize;

  @override
  InteractiveInkFeatureFactory? get splashFactory => Theme.of(context).splashFactory;
}

632
// END GENERATED TOKEN PROPERTIES - TextButton