outlined_button.dart 20.6 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
import 'material_state.dart';
import 'outlined_button_theme.dart';
import 'theme.dart';
import 'theme_data.dart';

/// A Material Design "Outlined Button"; essentially a [TextButton]
/// with an outlined border.
///
/// Outlined buttons are medium-emphasis buttons. They contain actions
/// that are important, but they aren’t the primary action in an app.
///
/// An outlined 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] and the outline's weight and color
33
/// are defined by [ButtonStyle.side]. The button reacts to touches
34
/// by filling with the [style]'s [ButtonStyle.overlayColor].
35 36 37 38 39 40 41 42
///
/// The outlined button's default style is defined by [defaultStyleOf].
/// The style of this outline button can be overridden with its [style]
/// parameter. The style of all text buttons in a subtree can be
/// overridden with the [OutlinedButtonTheme] and the style of all of the
/// outlined buttons in an app can be overridden with the [Theme]'s
/// [ThemeData.outlinedButtonTheme] property.
///
43 44
/// Unlike [TextButton] or [ElevatedButton], outline buttons have a
/// default [ButtonStyle.side] which defines the appearance of the
45
/// outline. Because the default `side` is non-null, it
46 47 48 49 50
/// unconditionally overrides the shape's [OutlinedBorder.side]. In
/// other words, to specify an outlined button's shape _and_ the
/// appearance of its outline, both the [ButtonStyle.shape] and
/// [ButtonStyle.side] properties must be specified.
///
51
/// {@tool dartpad}
52 53
/// Here is an example of a basic [OutlinedButton].
///
54
/// ** See code in examples/api/lib/material/outlined_button/outlined_button.0.dart **
55 56
/// {@end-tool}
///
57 58 59 60 61
/// The static [styleFrom] method is a convenient way to create a
/// outlined button [ButtonStyle] from simple values.
///
/// See also:
///
62 63 64 65
///  * [ElevatedButton], a filled button whose material elevates when pressed.
///  * [FilledButton], a filled button that doesn't elevate when pressed.
///  * [FilledButton.tonal], a filled button variant that uses a secondary fill color.
///  * [TextButton], a button with no outline or fill color.
66
///  * <https://material.io/design/components/buttons.html>
67
///  * <https://m3.material.io/components/buttons>
68 69 70
class OutlinedButton extends ButtonStyleButton {
  /// Create an OutlinedButton.
  const OutlinedButton({
71 72 73 74 75 76 77 78 79
    super.key,
    required super.onPressed,
    super.onLongPress,
    super.onHover,
    super.onFocusChange,
    super.style,
    super.focusNode,
    super.autofocus = false,
    super.clipBehavior = Clip.none,
80
    super.statesController,
81
    required super.child,
82
  });
83 84 85 86 87 88 89

  /// 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 12 logical pixels
  /// at the start, and 16 at the end, with an 8 pixel gap in between.
  factory OutlinedButton.icon({
90 91 92 93 94 95 96
    Key? key,
    required VoidCallback? onPressed,
    VoidCallback? onLongPress,
    ButtonStyle? style,
    FocusNode? focusNode,
    bool? autofocus,
    Clip? clipBehavior,
97
    MaterialStatesController? statesController,
98 99
    required Widget icon,
    required Widget label,
100 101 102 103 104
  }) = _OutlinedButtonWithIcon;

  /// A static convenience method that constructs an outlined button
  /// [ButtonStyle] given simple values.
  ///
105 106 107 108 109 110 111
  ///
  /// 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].
112 113
  ///
  /// Similarly, the [enabledMouseCursor] and [disabledMouseCursor]
114
  /// parameters are used to construct [ButtonStyle.mouseCursor].
115 116 117 118 119 120 121 122 123 124 125 126 127 128
  ///
  /// 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 shape and outline for an
  /// [OutlinedButton], one could write:
  ///
  /// ```dart
  /// OutlinedButton(
  ///   style: OutlinedButton.styleFrom(
129 130
  ///      shape: const StadiumBorder(),
  ///      side: const BorderSide(width: 2, color: Colors.green),
131
  ///   ),
132 133 134 135 136
  ///   child: const Text('Seasons of Love'),
  ///   onPressed: () {
  ///     // ...
  ///   },
  /// ),
137 138
  /// ```
  static ButtonStyle styleFrom({
139
    Color? foregroundColor,
140
    Color? backgroundColor,
141 142
    Color? disabledForegroundColor,
    Color? disabledBackgroundColor,
143
    Color? shadowColor,
144
    Color? surfaceTintColor,
145 146 147 148
    double? elevation,
    TextStyle? textStyle,
    EdgeInsetsGeometry? padding,
    Size? minimumSize,
149
    Size? fixedSize,
150
    Size? maximumSize,
151 152 153 154 155 156 157 158
    BorderSide? side,
    OutlinedBorder? shape,
    MouseCursor? enabledMouseCursor,
    MouseCursor? disabledMouseCursor,
    VisualDensity? visualDensity,
    MaterialTapTargetSize? tapTargetSize,
    Duration? animationDuration,
    bool? enableFeedback,
159
    AlignmentGeometry? alignment,
160
    InteractiveInkFeatureFactory? splashFactory,
161
  }) {
162 163
    final Color? foreground = foregroundColor;
    final Color? disabledForeground = disabledForegroundColor;
164 165 166 167
    final MaterialStateProperty<Color?>? foregroundColorProp = (foreground == null && disabledForeground == null)
      ? null
      : _OutlinedButtonDefaultColor(foreground, disabledForeground);
    final MaterialStateProperty<Color?>? backgroundColorProp = (backgroundColor == null && disabledBackgroundColor == null)
168
      ? null
169 170 171 172
      : disabledBackgroundColor == null
        ? ButtonStyleButton.allOrNull<Color?>(backgroundColor)
        : _OutlinedButtonDefaultColor(backgroundColor, disabledBackgroundColor);
    final MaterialStateProperty<Color?>? overlayColor = (foreground == null)
173
      ? null
174
      : _OutlinedButtonDefaultOverlay(foreground);
175
    final MaterialStateProperty<MouseCursor?> mouseCursor = _OutlinedButtonDefaultMouseCursor(enabledMouseCursor, disabledMouseCursor);
176 177 178

    return ButtonStyle(
      textStyle: ButtonStyleButton.allOrNull<TextStyle>(textStyle),
179 180
      foregroundColor: foregroundColorProp,
      backgroundColor: backgroundColorProp,
181 182
      overlayColor: overlayColor,
      shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
183
      surfaceTintColor: ButtonStyleButton.allOrNull<Color>(surfaceTintColor),
184 185 186
      elevation: ButtonStyleButton.allOrNull<double>(elevation),
      padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
      minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
187
      fixedSize: ButtonStyleButton.allOrNull<Size>(fixedSize),
188
      maximumSize: ButtonStyleButton.allOrNull<Size>(maximumSize),
189 190 191 192 193 194 195
      side: ButtonStyleButton.allOrNull<BorderSide>(side),
      shape: ButtonStyleButton.allOrNull<OutlinedBorder>(shape),
      mouseCursor: mouseCursor,
      visualDensity: visualDensity,
      tapTargetSize: tapTargetSize,
      animationDuration: animationDuration,
      enableFeedback: enableFeedback,
196
      alignment: alignment,
197
      splashFactory: splashFactory,
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
    );
  }

  /// Defines the button's default appearance.
  ///
  /// With the exception of [ButtonStyle.side], which defines the
  /// outline, and [ButtonStyle.padding], the returned style is the
  /// same as for [TextButton].
  ///
  /// 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.
  ///
  /// All of the ButtonStyle's defaults appear below. 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
217
  /// properties that are not followed by a sublist have the same
218 219 220
  /// value for all states, otherwise the values are as specified for
  /// each state and "others" means all other states.
  ///
221
  /// The color of the [ButtonStyle.textStyle] is not used, the
222
  /// [ButtonStyle.foregroundColor] is used instead.
223
  ///
224 225
  /// ## Material 2 defaults
  ///
226 227 228 229 230 231 232 233
  /// * `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)
234
  /// * `shadowColor` - Theme.shadowColor
235 236
  /// * `elevation` - 0
  /// * `padding`
237 238 239 240
  ///   * `default font size <= 14` - horizontal(16)
  ///   * `14 < default font size <= 28` - lerp(horizontal(16), horizontal(8))
  ///   * `28 < default font size <= 36` - lerp(horizontal(8), horizontal(4))
  ///   * `36 < default font size` - horizontal(4)
241
  /// * `minimumSize` - Size(64, 36)
242
  /// * `fixedSize` - null
243
  /// * `maximumSize` - Size.infinite
244 245 246
  /// * `side` - BorderSide(width: 1, color: Theme.colorScheme.onSurface(0.12))
  /// * `shape` - RoundedRectangleBorder(borderRadius: BorderRadius.circular(4))
  /// * `mouseCursor`
247
  ///   * disabled - SystemMouseCursors.basic
248 249 250 251 252
  ///   * others - SystemMouseCursors.click
  /// * `visualDensity` - theme.visualDensity
  /// * `tapTargetSize` - theme.materialTapTargetSize
  /// * `animationDuration` - kThemeChangeDuration
  /// * `enableFeedback` - true
253
  /// * `alignment` - Alignment.center
254
  /// * `splashFactory` - InkRipple.splashFactory
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
  ///
  /// ## Material 3 defaults
  ///
  /// If [ThemeData.useMaterial3] is set to true the following defaults will
  /// be used:
  ///
  /// * `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
270
  /// * `shadowColor` - Colors.transparent,
271 272 273
  /// * `surfaceTintColor` - null
  /// * `elevation` - 0
  /// * `padding`
274 275 276 277
  ///   * `default font size <= 14` - horizontal(24)
  ///   * `14 < default font size <= 28` - lerp(horizontal(24), horizontal(12))
  ///   * `28 < default font size <= 36` - lerp(horizontal(12), horizontal(6))
  ///   * `36 < default font size` - horizontal(6)
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
  /// * `minimumSize` - Size(64, 40)
  /// * `fixedSize` - null
  /// * `maximumSize` - Size.infinite
  /// * `side`
  ///   * disabled - BorderSide(color: Theme.colorScheme.onSurface(0.12))
  ///   * others - BorderSide(color: Theme.colorScheme.outline)
  /// * `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
294 295 296
  ///
  /// For the [OutlinedButton.icon] factory, the start (generally the left) value of
  /// [padding] is reduced from 24 to 16.
297 298
  @override
  ButtonStyle defaultStyleOf(BuildContext context) {
299
    final ThemeData theme = Theme.of(context);
300 301
    final ColorScheme colorScheme = theme.colorScheme;

302
    return Theme.of(context).useMaterial3
303
      ? _OutlinedButtonDefaultsM3(context)
304
      : styleFrom(
305 306
          foregroundColor: colorScheme.primary,
          disabledForegroundColor: colorScheme.onSurface.withOpacity(0.38),
307
          backgroundColor: Colors.transparent,
308
          disabledBackgroundColor: Colors.transparent,
309 310
          shadowColor: theme.shadowColor,
          elevation: 0,
311
          textStyle: theme.textTheme.labelLarge,
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
          padding: _scaledPadding(context),
          minimumSize: const Size(64, 36),
          maximumSize: Size.infinite,
          side: BorderSide(
            color: Theme.of(context).colorScheme.onSurface.withOpacity(0.12),
          ),
          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,
        );
328 329 330
  }

  @override
331 332
  ButtonStyle? themeStyleOf(BuildContext context) {
    return OutlinedButtonTheme.of(context).style;
333 334 335
  }
}

336
EdgeInsetsGeometry _scaledPadding(BuildContext context) {
337 338 339 340
  final ThemeData theme = Theme.of(context);
  final double padding1x = theme.useMaterial3 ? 24.0 : 16.0;
  final double defaultFontSize = theme.textTheme.labelLarge?.fontSize ?? 14.0;
  final double effectiveTextScale = MediaQuery.textScalerOf(context).scale(defaultFontSize) / 14.0;
341
  return ButtonStyleButton.scaledPadding(
342 343 344
     EdgeInsets.symmetric(horizontal: padding1x),
     EdgeInsets.symmetric(horizontal: padding1x / 2),
     EdgeInsets.symmetric(horizontal: padding1x / 2 / 2),
345
     effectiveTextScale,
346 347 348
  );
}

349
@immutable
350 351
class _OutlinedButtonDefaultColor extends MaterialStateProperty<Color?>  with Diagnosticable {
  _OutlinedButtonDefaultColor(this.color, this.disabled);
352

353 354
  final Color? color;
  final Color? disabled;
355 356

  @override
357
  Color? resolve(Set<MaterialState> states) {
358
    if (states.contains(MaterialState.disabled)) {
359
      return disabled;
360
    }
361
    return color;
362 363 364 365
  }
}

@immutable
366
class _OutlinedButtonDefaultOverlay extends MaterialStateProperty<Color?> with Diagnosticable {
367
  _OutlinedButtonDefaultOverlay(this.foreground);
368

369
  final Color foreground;
370 371

  @override
372
  Color? resolve(Set<MaterialState> states) {
373 374 375
    if (states.contains(MaterialState.pressed)) {
      return foreground.withOpacity(0.12);
    }
376
    if (states.contains(MaterialState.hovered)) {
377
      return foreground.withOpacity(0.04);
378
    }
379
    if (states.contains(MaterialState.focused)) {
380
      return foreground.withOpacity(0.12);
381
    }
382 383 384 385 386
    return null;
  }
}

@immutable
387
class _OutlinedButtonDefaultMouseCursor extends MaterialStateProperty<MouseCursor?> with Diagnosticable {
388 389
  _OutlinedButtonDefaultMouseCursor(this.enabledCursor, this.disabledCursor);

390 391
  final MouseCursor? enabledCursor;
  final MouseCursor? disabledCursor;
392 393

  @override
394
  MouseCursor? resolve(Set<MaterialState> states) {
395
    if (states.contains(MaterialState.disabled)) {
396
      return disabledCursor;
397
    }
398 399 400 401 402 403
    return enabledCursor;
  }
}

class _OutlinedButtonWithIcon extends OutlinedButton {
  _OutlinedButtonWithIcon({
404 405 406 407 408
    super.key,
    required super.onPressed,
    super.onLongPress,
    super.style,
    super.focusNode,
409 410
    bool? autofocus,
    Clip? clipBehavior,
411
    super.statesController,
412 413
    required Widget icon,
    required Widget label,
414
  }) : super(
415 416 417 418
         autofocus: autofocus ?? false,
         clipBehavior: clipBehavior ?? Clip.none,
         child: _OutlinedButtonWithIconChild(icon: icon, label: label),
      );
419 420 421 422 423 424 425

  @override
  ButtonStyle defaultStyleOf(BuildContext context) {
    final bool useMaterial3 = Theme.of(context).useMaterial3;
    if (!useMaterial3) {
      return super.defaultStyleOf(context);
    }
426 427 428
    final ButtonStyle buttonStyle = super.defaultStyleOf(context);
    final double defaultFontSize = buttonStyle.textStyle?.resolve(const <MaterialState>{})?.fontSize ?? 14.0;
    final double effectiveTextScale = MediaQuery.textScalerOf(context).scale(defaultFontSize) / 14.0;
429 430 431 432
    final EdgeInsetsGeometry scaledPadding = ButtonStyleButton.scaledPadding(
      const EdgeInsetsDirectional.fromSTEB(16, 0, 24, 0),
      const EdgeInsetsDirectional.fromSTEB(8, 0, 12, 0),
      const EdgeInsetsDirectional.fromSTEB(4, 0, 6, 0),
433
      effectiveTextScale,
434
    );
435
    return buttonStyle.copyWith(
436 437 438
      padding: MaterialStatePropertyAll<EdgeInsetsGeometry>(scaledPadding),
    );
  }
439 440 441
}

class _OutlinedButtonWithIconChild extends StatelessWidget {
442 443 444
  const _OutlinedButtonWithIconChild({
    required this.label,
    required this.icon,
445
  });
446 447 448 449 450 451

  final Widget label;
  final Widget icon;

  @override
  Widget build(BuildContext context) {
452
    final double scale = MediaQuery.textScalerOf(context).textScaleFactor;
453
    final double gap = scale <= 1 ? 8 : lerpDouble(8, 4, math.min(scale - 1, 1))!;
454 455
    return Row(
      mainAxisSize: MainAxisSize.min,
456
      children: <Widget>[icon, SizedBox(width: gap), Flexible(child: label)],
457 458 459
    );
  }
}
460

461
// BEGIN GENERATED TOKEN PROPERTIES - OutlinedButton
462

463 464 465 466
// 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.
467

468 469
class _OutlinedButtonDefaultsM3 extends ButtonStyle {
  _OutlinedButtonDefaultsM3(this.context)
470 471 472 473 474 475 476 477 478 479 480
   : super(
       animationDuration: kThemeChangeDuration,
       enableFeedback: true,
       alignment: Alignment.center,
     );

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

  @override
  MaterialStateProperty<TextStyle?> get textStyle =>
481
    MaterialStatePropertyAll<TextStyle?>(Theme.of(context).textTheme.labelLarge);
482 483 484

  @override
  MaterialStateProperty<Color?>? get backgroundColor =>
485
    const MaterialStatePropertyAll<Color>(Colors.transparent);
486 487 488 489

  @override
  MaterialStateProperty<Color?>? get foregroundColor =>
    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
490
      if (states.contains(MaterialState.disabled)) {
491
        return _colors.onSurface.withOpacity(0.38);
492
      }
493 494 495 496 497 498
      return _colors.primary;
    });

  @override
  MaterialStateProperty<Color?>? get overlayColor =>
    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
499 500 501
      if (states.contains(MaterialState.pressed)) {
        return _colors.primary.withOpacity(0.12);
      }
502
      if (states.contains(MaterialState.hovered)) {
503
        return _colors.primary.withOpacity(0.08);
504 505
      }
      if (states.contains(MaterialState.focused)) {
506
        return _colors.primary.withOpacity(0.12);
507
      }
508 509 510
      return null;
    });

511 512 513
  @override
  MaterialStateProperty<Color>? get shadowColor =>
    const MaterialStatePropertyAll<Color>(Colors.transparent);
514

515 516 517
  @override
  MaterialStateProperty<Color>? get surfaceTintColor =>
    const MaterialStatePropertyAll<Color>(Colors.transparent);
518 519 520

  @override
  MaterialStateProperty<double>? get elevation =>
521
    const MaterialStatePropertyAll<double>(0.0);
522 523 524

  @override
  MaterialStateProperty<EdgeInsetsGeometry>? get padding =>
525
    MaterialStatePropertyAll<EdgeInsetsGeometry>(_scaledPadding(context));
526 527 528

  @override
  MaterialStateProperty<Size>? get minimumSize =>
529
    const MaterialStatePropertyAll<Size>(Size(64.0, 40.0));
530 531 532 533 534

  // No default fixedSize

  @override
  MaterialStateProperty<Size>? get maximumSize =>
535
    const MaterialStatePropertyAll<Size>(Size.infinite);
536 537 538 539

  @override
  MaterialStateProperty<BorderSide>? get side =>
    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
540
    if (states.contains(MaterialState.disabled)) {
541
      return BorderSide(color: _colors.onSurface.withOpacity(0.12));
542
    }
543 544 545
    if (states.contains(MaterialState.focused)) {
      return BorderSide(color: _colors.primary);
    }
546 547 548 549 550
    return BorderSide(color: _colors.outline);
  });

  @override
  MaterialStateProperty<OutlinedBorder>? get shape =>
551
    const MaterialStatePropertyAll<OutlinedBorder>(StadiumBorder());
552 553 554 555

  @override
  MaterialStateProperty<MouseCursor?>? get mouseCursor =>
    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
556
      if (states.contains(MaterialState.disabled)) {
557
        return SystemMouseCursors.basic;
558
      }
559 560 561 562 563 564 565 566 567 568 569 570 571
      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;
}

572
// END GENERATED TOKEN PROPERTIES - OutlinedButton