text_button.dart 20.8 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 91
    required Widget super.child,
  });
92 93 94 95 96 97 98 99 100

  /// 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({
101 102 103
    Key? key,
    required VoidCallback? onPressed,
    VoidCallback? onLongPress,
104 105
    ValueChanged<bool>? onHover,
    ValueChanged<bool>? onFocusChange,
106 107 108 109
    ButtonStyle? style,
    FocusNode? focusNode,
    bool? autofocus,
    Clip? clipBehavior,
110 111
    required Widget icon,
    required Widget label,
112 113 114 115 116
  }) = _TextButtonWithIcon;

  /// A static convenience method that constructs a text button
  /// [ButtonStyle] given simple values.
  ///
117 118 119 120 121 122
  /// 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].
123 124
  ///
  /// Similarly, the [enabledMouseCursor] and [disabledMouseCursor]
125
  /// parameters are used to construct [ButtonStyle.mouseCursor].
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
  ///
  /// 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(
141
  ///   style: TextButton.styleFrom(foregroundColor: Colors.green),
142 143 144 145 146
  ///   child: const Text('Give Kate a mix tape'),
  ///   onPressed: () {
  ///     // ...
  ///   },
  /// ),
147 148
  /// ```
  static ButtonStyle styleFrom({
149
    Color? foregroundColor,
150
    Color? backgroundColor,
151 152
    Color? disabledForegroundColor,
    Color? disabledBackgroundColor,
153
    Color? shadowColor,
154
    Color? surfaceTintColor,
155 156 157 158
    double? elevation,
    TextStyle? textStyle,
    EdgeInsetsGeometry? padding,
    Size? minimumSize,
159
    Size? fixedSize,
160
    Size? maximumSize,
161 162 163 164 165 166 167 168
    BorderSide? side,
    OutlinedBorder? shape,
    MouseCursor? enabledMouseCursor,
    MouseCursor? disabledMouseCursor,
    VisualDensity? visualDensity,
    MaterialTapTargetSize? tapTargetSize,
    Duration? animationDuration,
    bool? enableFeedback,
169
    AlignmentGeometry? alignment,
170
    InteractiveInkFeatureFactory? splashFactory,
171 172 173 174 175 176
    @Deprecated(
      'Use foregroundColor instead. '
      'This feature was deprecated after v3.1.0.'
    )
    Color? primary,
    @Deprecated(
177
      'Use disabledForegroundColor instead. '
178 179 180
      'This feature was deprecated after v3.1.0.'
    )
    Color? onSurface,
181
  }) {
182 183 184 185 186 187
    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)
188
      ? null
189 190 191 192
      : disabledBackgroundColor == null
        ? ButtonStyleButton.allOrNull<Color?>(backgroundColor)
        : _TextButtonDefaultColor(backgroundColor, disabledBackgroundColor);
    final MaterialStateProperty<Color?>? overlayColor = (foreground == null)
193
      ? null
194
      : _TextButtonDefaultOverlay(foreground);
195
    final MaterialStateProperty<MouseCursor>? mouseCursor = (enabledMouseCursor == null && disabledMouseCursor == null)
196
      ? null
197
      : _TextButtonDefaultMouseCursor(enabledMouseCursor!, disabledMouseCursor!);
198 199 200

    return ButtonStyle(
      textStyle: ButtonStyleButton.allOrNull<TextStyle>(textStyle),
201 202
      foregroundColor: foregroundColorProp,
      backgroundColor: backgroundColorProp,
203 204
      overlayColor: overlayColor,
      shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
205
      surfaceTintColor: ButtonStyleButton.allOrNull<Color>(surfaceTintColor),
206 207 208
      elevation: ButtonStyleButton.allOrNull<double>(elevation),
      padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
      minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
209
      fixedSize: ButtonStyleButton.allOrNull<Size>(fixedSize),
210
      maximumSize: ButtonStyleButton.allOrNull<Size>(maximumSize),
211 212 213 214 215 216 217
      side: ButtonStyleButton.allOrNull<BorderSide>(side),
      shape: ButtonStyleButton.allOrNull<OutlinedBorder>(shape),
      mouseCursor: mouseCursor,
      visualDensity: visualDensity,
      tapTargetSize: tapTargetSize,
      animationDuration: animationDuration,
      enableFeedback: enableFeedback,
218
      alignment: alignment,
219
      splashFactory: splashFactory,
220 221 222 223 224
    );
  }

  /// Defines the button's default appearance.
  ///
225
  /// {@template flutter.material.text_button.default_style_of}
226 227 228 229 230 231
  /// 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.
  ///
232
  /// All of the [ButtonStyle]'s defaults appear below.
233 234 235 236 237
  ///
  /// 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
238
  /// properties that are not followed by a sublist have the same
239 240 241 242 243 244 245 246
  /// 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
  /// `MediaQuery.of(context).textScaleFactor` and the names of the
  /// EdgeInsets constructors and `EdgeInsetsGeometry.lerp` have been
  /// abbreviated for readability.
  ///
247 248
  /// The color of the [ButtonStyle.textStyle] is not used, the
  /// [ButtonStyle.foregroundColor] color is used instead.
249
  /// {@endtemplate}
250
  ///
251 252
  /// ## Material 2 defaults
  ///
253 254 255 256 257 258 259 260
  /// * `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)
261
  /// * `shadowColor` - Theme.shadowColor
262 263 264 265 266 267 268
  /// * `elevation` - 0
  /// * `padding`
  ///   * `textScaleFactor <= 1` - all(8)
  ///   * `1 < textScaleFactor <= 2` - lerp(all(8), horizontal(8))
  ///   * `2 < textScaleFactor <= 3` - lerp(horizontal(8), horizontal(4))
  ///   * `3 < textScaleFactor` - horizontal(4)
  /// * `minimumSize` - Size(64, 36)
269
  /// * `fixedSize` - null
270
  /// * `maximumSize` - Size.infinite
271
  /// * `side` - null
272 273
  /// * `shape` - RoundedRectangleBorder(borderRadius: BorderRadius.circular(4))
  /// * `mouseCursor`
274
  ///   * disabled - SystemMouseCursors.basic
275 276 277 278 279
  ///   * others - SystemMouseCursors.click
  /// * `visualDensity` - theme.visualDensity
  /// * `tapTargetSize` - theme.materialTapTargetSize
  /// * `animationDuration` - kThemeChangeDuration
  /// * `enableFeedback` - true
280
  /// * `alignment` - Alignment.center
281
  /// * `splashFactory` - InkRipple.splashFactory
282 283 284 285 286 287 288
  ///
  /// 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)
289 290 291 292 293
  ///
  /// 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.
294 295 296 297 298 299
  ///
  /// ## Material 3 defaults
  ///
  /// If [ThemeData.useMaterial3] is set to true the following defaults will
  /// be used:
  ///
300
  /// {@template flutter.material.text_button.material3_defaults}
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
  /// * `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
  /// * `shadowColor` - null
  /// * `surfaceTintColor` - null
  /// * `elevation` - 0
  /// * `padding`
  ///   * `textScaleFactor <= 1` - all(8)
  ///   * `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
332
  /// {@endtemplate}
333 334
  @override
  ButtonStyle defaultStyleOf(BuildContext context) {
335
    final ThemeData theme = Theme.of(context);
336 337
    final ColorScheme colorScheme = theme.colorScheme;

338
    return Theme.of(context).useMaterial3
339
      ? _TextButtonDefaultsM3(context)
340
      : styleFrom(
341 342
          foregroundColor: colorScheme.primary,
          disabledForegroundColor: colorScheme.onSurface.withOpacity(0.38),
343
          backgroundColor: Colors.transparent,
344
          disabledBackgroundColor: Colors.transparent,
345 346
          shadowColor: theme.shadowColor,
          elevation: 0,
347
          textStyle: theme.textTheme.labelLarge,
348 349 350 351 352 353 354 355 356 357 358 359 360
          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,
        );
361 362 363 364 365
  }

  /// Returns the [TextButtonThemeData.style] of the closest
  /// [TextButtonTheme] ancestor.
  @override
366 367
  ButtonStyle? themeStyleOf(BuildContext context) {
    return TextButtonTheme.of(context).style;
368 369 370
  }
}

371 372 373 374 375 376 377 378 379
EdgeInsetsGeometry _scaledPadding(BuildContext context) {
  return ButtonStyleButton.scaledPadding(
    const EdgeInsets.all(8),
    const EdgeInsets.symmetric(horizontal: 8),
    const EdgeInsets.symmetric(horizontal: 4),
    MediaQuery.maybeOf(context)?.textScaleFactor ?? 1,
  );
}

380
@immutable
381 382
class _TextButtonDefaultColor extends MaterialStateProperty<Color?> {
  _TextButtonDefaultColor(this.color, this.disabled);
383

384 385
  final Color? color;
  final Color? disabled;
386 387

  @override
388
  Color? resolve(Set<MaterialState> states) {
389
    if (states.contains(MaterialState.disabled)) {
390
      return disabled;
391
    }
392
    return color;
393 394 395 396
  }

  @override
  String toString() {
397
    return '{disabled: $disabled, otherwise: $color}';
398 399 400 401
  }
}

@immutable
402
class _TextButtonDefaultOverlay extends MaterialStateProperty<Color?> {
403 404 405 406 407
  _TextButtonDefaultOverlay(this.primary);

  final Color primary;

  @override
408
  Color? resolve(Set<MaterialState> states) {
409
    if (states.contains(MaterialState.hovered)) {
410
      return primary.withOpacity(0.04);
411 412
    }
    if (states.contains(MaterialState.focused) || states.contains(MaterialState.pressed)) {
413
      return primary.withOpacity(0.12);
414
    }
415 416 417 418 419
    return null;
  }

  @override
  String toString() {
420
    return '{hovered: ${primary.withOpacity(0.04)}, focused,pressed: ${primary.withOpacity(0.12)}, otherwise: null}';
421 422 423 424 425 426 427 428 429 430 431 432
  }
}

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

  final MouseCursor enabledCursor;
  final MouseCursor disabledCursor;

  @override
  MouseCursor resolve(Set<MaterialState> states) {
433
    if (states.contains(MaterialState.disabled)) {
434
      return disabledCursor;
435
    }
436 437 438 439 440 441
    return enabledCursor;
  }
}

class _TextButtonWithIcon extends TextButton {
  _TextButtonWithIcon({
442 443 444 445 446 447 448
    super.key,
    required super.onPressed,
    super.onLongPress,
    super.onHover,
    super.onFocusChange,
    super.style,
    super.focusNode,
449 450 451 452
    bool? autofocus,
    Clip? clipBehavior,
    required Widget icon,
    required Widget label,
453 454 455 456 457 458 459 460 461 462 463 464 465 466
  }) : assert(icon != null),
       assert(label != null),
       super(
         autofocus: autofocus ?? false,
         clipBehavior: clipBehavior ?? Clip.none,
         child: _TextButtonWithIconChild(icon: icon, label: label),
      );

  @override
  ButtonStyle defaultStyleOf(BuildContext context) {
    final EdgeInsetsGeometry scaledPadding = ButtonStyleButton.scaledPadding(
      const EdgeInsets.all(8),
      const EdgeInsets.symmetric(horizontal: 4),
      const EdgeInsets.symmetric(horizontal: 4),
467
      MediaQuery.maybeOf(context)?.textScaleFactor ?? 1,
468 469
    );
    return super.defaultStyleOf(context).copyWith(
470
      padding: MaterialStatePropertyAll<EdgeInsetsGeometry>(scaledPadding),
471 472 473 474 475
    );
  }
}

class _TextButtonWithIconChild extends StatelessWidget {
476 477 478
  const _TextButtonWithIconChild({
    required this.label,
    required this.icon,
479
  });
480 481 482 483 484 485

  final Widget label;
  final Widget icon;

  @override
  Widget build(BuildContext context) {
486
    final double scale = MediaQuery.maybeOf(context)?.textScaleFactor ?? 1;
487
    final double gap = scale <= 1 ? 8 : lerpDouble(8, 4, math.min(scale - 1, 1))!;
488 489
    return Row(
      mainAxisSize: MainAxisSize.min,
490
      children: <Widget>[icon, SizedBox(width: gap), Flexible(child: label)],
491 492 493
    );
  }
}
494

495
// BEGIN GENERATED TOKEN PROPERTIES - TextButton
496

497 498 499 500
// 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.
501

502
// Token database version: v0_132
503 504 505

class _TextButtonDefaultsM3 extends ButtonStyle {
  _TextButtonDefaultsM3(this.context)
506 507 508 509 510 511 512 513 514 515 516
   : super(
       animationDuration: kThemeChangeDuration,
       enableFeedback: true,
       alignment: Alignment.center,
     );

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

  @override
  MaterialStateProperty<TextStyle?> get textStyle =>
517
    MaterialStatePropertyAll<TextStyle?>(Theme.of(context).textTheme.labelLarge);
518 519 520

  @override
  MaterialStateProperty<Color?>? get backgroundColor =>
521
    const MaterialStatePropertyAll<Color>(Colors.transparent);
522 523 524 525

  @override
  MaterialStateProperty<Color?>? get foregroundColor =>
    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
526
      if (states.contains(MaterialState.disabled)) {
527
        return _colors.onSurface.withOpacity(0.38);
528
      }
529 530 531 532 533 534
      return _colors.primary;
    });

  @override
  MaterialStateProperty<Color?>? get overlayColor =>
    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
535
      if (states.contains(MaterialState.hovered)) {
536
        return _colors.primary.withOpacity(0.08);
537 538
      }
      if (states.contains(MaterialState.focused)) {
539
        return _colors.primary.withOpacity(0.12);
540 541
      }
      if (states.contains(MaterialState.pressed)) {
542
        return _colors.primary.withOpacity(0.12);
543
      }
544 545 546
      return null;
    });

547 548 549
  @override
  MaterialStateProperty<Color>? get shadowColor =>
    const MaterialStatePropertyAll<Color>(Colors.transparent);
550

551 552 553
  @override
  MaterialStateProperty<Color>? get surfaceTintColor =>
    const MaterialStatePropertyAll<Color>(Colors.transparent);
554 555 556

  @override
  MaterialStateProperty<double>? get elevation =>
557
    const MaterialStatePropertyAll<double>(0.0);
558 559 560

  @override
  MaterialStateProperty<EdgeInsetsGeometry>? get padding =>
561
    MaterialStatePropertyAll<EdgeInsetsGeometry>(_scaledPadding(context));
562 563 564

  @override
  MaterialStateProperty<Size>? get minimumSize =>
565
    const MaterialStatePropertyAll<Size>(Size(64.0, 40.0));
566 567 568 569 570

  // No default fixedSize

  @override
  MaterialStateProperty<Size>? get maximumSize =>
571
    const MaterialStatePropertyAll<Size>(Size.infinite);
572 573 574 575 576

  // No default side

  @override
  MaterialStateProperty<OutlinedBorder>? get shape =>
577
    const MaterialStatePropertyAll<OutlinedBorder>(StadiumBorder());
578 579 580 581

  @override
  MaterialStateProperty<MouseCursor?>? get mouseCursor =>
    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
582
      if (states.contains(MaterialState.disabled)) {
583
        return SystemMouseCursors.basic;
584
      }
585 586 587 588 589 590 591 592 593 594 595 596 597
      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;
}

598
// END GENERATED TOKEN PROPERTIES - TextButton