1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/// @docImport 'elevated_button.dart';
6/// @docImport 'filled_button.dart';
7/// @docImport 'material.dart';
8/// @docImport 'outlined_button.dart';
9library;
10
11import 'dart:ui' show lerpDouble;
12
13import 'package:flutter/foundation.dart';
14import 'package:flutter/widgets.dart';
15
16import 'button_style.dart';
17import 'button_style_button.dart';
18import 'color_scheme.dart';
19import 'colors.dart';
20import 'constants.dart';
21import 'ink_ripple.dart';
22import 'ink_well.dart';
23import 'material_state.dart';
24import 'text_button_theme.dart';
25import 'theme.dart';
26import 'theme_data.dart';
27
28/// A Material Design "Text Button".
29///
30/// Use text buttons on toolbars, in dialogs, or inline with other
31/// content but offset from that content with padding so that the
32/// button's presence is obvious. Text buttons do not have visible
33/// borders and must therefore rely on their position relative to
34/// other content for context. In dialogs and cards, they should be
35/// grouped together in one of the bottom corners. Avoid using text
36/// buttons where they would blend in with other content, for example
37/// in the middle of lists.
38///
39/// A text button is a label [child] displayed on a (zero elevation)
40/// [Material] widget. The label's [Text] and [Icon] widgets are
41/// displayed in the [style]'s [ButtonStyle.foregroundColor]. The
42/// button reacts to touches by filling with the [style]'s
43/// [ButtonStyle.backgroundColor].
44///
45/// The text button's default style is defined by [defaultStyleOf].
46/// The style of this text button can be overridden with its [style]
47/// parameter. The style of all text buttons in a subtree can be
48/// overridden with the [TextButtonTheme] and the style of all of the
49/// text buttons in an app can be overridden with the [Theme]'s
50/// [ThemeData.textButtonTheme] property.
51///
52/// The static [styleFrom] method is a convenient way to create a
53/// text button [ButtonStyle] from simple values.
54///
55/// If the [onPressed] and [onLongPress] callbacks are null, then this
56/// button will be disabled, it will not react to touch.
57///
58/// {@tool dartpad}
59/// This sample shows various ways to configure TextButtons, from the
60/// simplest default appearance to versions that don't resemble
61/// Material Design at all.
62///
63/// ** See code in examples/api/lib/material/text_button/text_button.0.dart **
64/// {@end-tool}
65///
66/// {@tool dartpad}
67/// This sample demonstrates using the [statesController] parameter to create a button
68/// that adds support for [WidgetState.selected].
69///
70/// ** See code in examples/api/lib/material/text_button/text_button.1.dart **
71/// {@end-tool}
72///
73/// See also:
74///
75/// * [ElevatedButton], a filled button whose material elevates when pressed.
76/// * [FilledButton], a filled button that doesn't elevate when pressed.
77/// * [FilledButton.tonal], a filled button variant that uses a secondary fill color.
78/// * [OutlinedButton], a button with an outlined border and no fill color.
79/// * <https://material.io/design/components/buttons.html>
80/// * <https://m3.material.io/components/buttons>
81class TextButton extends ButtonStyleButton {
82 /// Create a [TextButton].
83 const TextButton({
84 super.key,
85 required super.onPressed,
86 super.onLongPress,
87 super.onHover,
88 super.onFocusChange,
89 super.style,
90 super.focusNode,
91 super.autofocus = false,
92 super.clipBehavior,
93 super.statesController,
94 super.isSemanticButton,
95 required Widget super.child,
96 });
97
98 /// Create a text button from a pair of widgets that serve as the button's
99 /// [icon] and [label].
100 ///
101 /// The icon and label are arranged in a row and padded by 8 logical pixels
102 /// at the ends, with an 8 pixel gap in between.
103 ///
104 /// If [icon] is null, will create a [TextButton] instead.
105 ///
106 /// {@macro flutter.material.ButtonStyleButton.iconAlignment}
107 ///
108 factory TextButton.icon({
109 Key? key,
110 required VoidCallback? onPressed,
111 VoidCallback? onLongPress,
112 ValueChanged<bool>? onHover,
113 ValueChanged<bool>? onFocusChange,
114 ButtonStyle? style,
115 FocusNode? focusNode,
116 bool? autofocus,
117 Clip? clipBehavior,
118 MaterialStatesController? statesController,
119 Widget? icon,
120 required Widget label,
121 IconAlignment? iconAlignment,
122 }) {
123 if (icon == null) {
124 return TextButton(
125 key: key,
126 onPressed: onPressed,
127 onLongPress: onLongPress,
128 onHover: onHover,
129 onFocusChange: onFocusChange,
130 style: style,
131 focusNode: focusNode,
132 autofocus: autofocus ?? false,
133 clipBehavior: clipBehavior ?? Clip.none,
134 statesController: statesController,
135 child: label,
136 );
137 }
138 return _TextButtonWithIcon(
139 key: key,
140 onPressed: onPressed,
141 onLongPress: onLongPress,
142 onHover: onHover,
143 onFocusChange: onFocusChange,
144 style: style,
145 focusNode: focusNode,
146 autofocus: autofocus ?? false,
147 clipBehavior: clipBehavior ?? Clip.none,
148 statesController: statesController,
149 icon: icon,
150 label: label,
151 iconAlignment: iconAlignment,
152 );
153 }
154
155 /// A static convenience method that constructs a text button
156 /// [ButtonStyle] given simple values.
157 ///
158 /// The [foregroundColor] and [disabledForegroundColor] colors are used
159 /// to create a [WidgetStateProperty] [ButtonStyle.foregroundColor], and
160 /// a derived [ButtonStyle.overlayColor] if [overlayColor] isn't specified.
161 ///
162 /// The [backgroundColor] and [disabledBackgroundColor] colors are
163 /// used to create a [WidgetStateProperty] [ButtonStyle.backgroundColor].
164 ///
165 /// Similarly, the [enabledMouseCursor] and [disabledMouseCursor]
166 /// parameters are used to construct [ButtonStyle.mouseCursor].
167 ///
168 /// The [iconColor], [disabledIconColor] are used to construct
169 /// [ButtonStyle.iconColor] and [iconSize] is used to construct
170 /// [ButtonStyle.iconSize].
171 ///
172 /// If [iconColor] is null, the button icon will use [foregroundColor]. If [foregroundColor] is also
173 /// null, the button icon will use the default icon color.
174 ///
175 /// If [overlayColor] is specified and its value is [Colors.transparent]
176 /// then the pressed/focused/hovered highlights are effectively defeated.
177 /// Otherwise a [WidgetStateProperty] with the same opacities as the
178 /// default is created.
179 ///
180 /// All of the other parameters are either used directly or used to
181 /// create a [WidgetStateProperty] with a single value for all
182 /// states.
183 ///
184 /// All parameters default to null. By default this method returns
185 /// a [ButtonStyle] that doesn't override anything.
186 ///
187 /// For example, to override the default text and icon colors for a
188 /// [TextButton], as well as its overlay color, with all of the
189 /// standard opacity adjustments for the pressed, focused, and
190 /// hovered states, one could write:
191 ///
192 /// ```dart
193 /// TextButton(
194 /// style: TextButton.styleFrom(foregroundColor: Colors.green),
195 /// child: const Text('Give Kate a mix tape'),
196 /// onPressed: () {
197 /// // ...
198 /// },
199 /// ),
200 /// ```
201 static ButtonStyle styleFrom({
202 Color? foregroundColor,
203 Color? backgroundColor,
204 Color? disabledForegroundColor,
205 Color? disabledBackgroundColor,
206 Color? shadowColor,
207 Color? surfaceTintColor,
208 Color? iconColor,
209 double? iconSize,
210 IconAlignment? iconAlignment,
211 Color? disabledIconColor,
212 Color? overlayColor,
213 double? elevation,
214 TextStyle? textStyle,
215 EdgeInsetsGeometry? padding,
216 Size? minimumSize,
217 Size? fixedSize,
218 Size? maximumSize,
219 BorderSide? side,
220 OutlinedBorder? shape,
221 MouseCursor? enabledMouseCursor,
222 MouseCursor? disabledMouseCursor,
223 VisualDensity? visualDensity,
224 MaterialTapTargetSize? tapTargetSize,
225 Duration? animationDuration,
226 bool? enableFeedback,
227 AlignmentGeometry? alignment,
228 InteractiveInkFeatureFactory? splashFactory,
229 ButtonLayerBuilder? backgroundBuilder,
230 ButtonLayerBuilder? foregroundBuilder,
231 }) {
232 final MaterialStateProperty<Color?>? backgroundColorProp = switch ((
233 backgroundColor,
234 disabledBackgroundColor,
235 )) {
236 (_?, null) => MaterialStatePropertyAll<Color?>(backgroundColor),
237 (_, _) => ButtonStyleButton.defaultColor(backgroundColor, disabledBackgroundColor),
238 };
239 final MaterialStateProperty<Color?>? iconColorProp = switch ((iconColor, disabledIconColor)) {
240 (_?, null) => MaterialStatePropertyAll<Color?>(iconColor),
241 (_, _) => ButtonStyleButton.defaultColor(iconColor, disabledIconColor),
242 };
243 final MaterialStateProperty<Color?>? overlayColorProp = switch ((
244 foregroundColor,
245 overlayColor,
246 )) {
247 (null, null) => null,
248 (_, Color(a: 0.0)) => WidgetStatePropertyAll<Color?>(overlayColor),
249 (_, final Color color) ||
250 (final Color color, _) => WidgetStateProperty<Color?>.fromMap(<WidgetState, Color?>{
251 WidgetState.pressed: color.withOpacity(0.1),
252 WidgetState.hovered: color.withOpacity(0.08),
253 WidgetState.focused: color.withOpacity(0.1),
254 }),
255 };
256
257 return ButtonStyle(
258 textStyle: ButtonStyleButton.allOrNull<TextStyle>(textStyle),
259 foregroundColor: ButtonStyleButton.defaultColor(foregroundColor, disabledForegroundColor),
260 backgroundColor: backgroundColorProp,
261 overlayColor: overlayColorProp,
262 shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
263 surfaceTintColor: ButtonStyleButton.allOrNull<Color>(surfaceTintColor),
264 iconColor: iconColorProp,
265 iconSize: ButtonStyleButton.allOrNull<double>(iconSize),
266 iconAlignment: iconAlignment,
267 elevation: ButtonStyleButton.allOrNull<double>(elevation),
268 padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
269 minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
270 fixedSize: ButtonStyleButton.allOrNull<Size>(fixedSize),
271 maximumSize: ButtonStyleButton.allOrNull<Size>(maximumSize),
272 side: ButtonStyleButton.allOrNull<BorderSide>(side),
273 shape: ButtonStyleButton.allOrNull<OutlinedBorder>(shape),
274 mouseCursor: WidgetStateProperty<MouseCursor?>.fromMap(<WidgetStatesConstraint, MouseCursor?>{
275 WidgetState.disabled: disabledMouseCursor,
276 WidgetState.any: enabledMouseCursor,
277 }),
278 visualDensity: visualDensity,
279 tapTargetSize: tapTargetSize,
280 animationDuration: animationDuration,
281 enableFeedback: enableFeedback,
282 alignment: alignment,
283 splashFactory: splashFactory,
284 backgroundBuilder: backgroundBuilder,
285 foregroundBuilder: foregroundBuilder,
286 );
287 }
288
289 /// Defines the button's default appearance.
290 ///
291 /// {@template flutter.material.text_button.default_style_of}
292 /// The button [child]'s [Text] and [Icon] widgets are rendered with
293 /// the [ButtonStyle]'s foreground color. The button's [InkWell] adds
294 /// the style's overlay color when the button is focused, hovered
295 /// or pressed. The button's background color becomes its [Material]
296 /// color and is transparent by default.
297 ///
298 /// All of the [ButtonStyle]'s defaults appear below.
299 ///
300 /// In this list "Theme.foo" is shorthand for
301 /// `Theme.of(context).foo`. Color scheme values like
302 /// "onSurface(0.38)" are shorthand for
303 /// `onSurface.withOpacity(0.38)`. [WidgetStateProperty] valued
304 /// properties that are not followed by a sublist have the same
305 /// value for all states, otherwise the values are as specified for
306 /// each state and "others" means all other states.
307 ///
308 /// The "default font size" below refers to the font size specified in the
309 /// [defaultStyleOf] method (or 14.0 if unspecified), scaled by the
310 /// `MediaQuery.textScalerOf(context).scale` method. And the names of the
311 /// EdgeInsets constructors and `EdgeInsetsGeometry.lerp` have been abbreviated
312 /// for readability.
313 ///
314 /// The color of the [ButtonStyle.textStyle] is not used, the
315 /// [ButtonStyle.foregroundColor] color is used instead.
316 /// {@endtemplate}
317 ///
318 /// ## Material 2 defaults
319 ///
320 /// * `textStyle` - Theme.textTheme.button
321 /// * `backgroundColor` - transparent
322 /// * `foregroundColor`
323 /// * disabled - Theme.colorScheme.onSurface(0.38)
324 /// * others - Theme.colorScheme.primary
325 /// * `overlayColor`
326 /// * hovered - Theme.colorScheme.primary(0.08)
327 /// * focused or pressed - Theme.colorScheme.primary(0.12)
328 /// * `shadowColor` - Theme.shadowColor
329 /// * `elevation` - 0
330 /// * `padding`
331 /// * `default font size <= 14` - (horizontal(12), vertical(8))
332 /// * `14 < default font size <= 28` - lerp(all(8), horizontal(8))
333 /// * `28 < default font size <= 36` - lerp(horizontal(8), horizontal(4))
334 /// * `36 < default font size` - horizontal(4)
335 /// * `minimumSize` - Size(64, 36)
336 /// * `fixedSize` - null
337 /// * `maximumSize` - Size.infinite
338 /// * `side` - null
339 /// * `shape` - RoundedRectangleBorder(borderRadius: BorderRadius.circular(4))
340 /// * `mouseCursor`
341 /// * disabled - SystemMouseCursors.basic
342 /// * others - SystemMouseCursors.click
343 /// * `visualDensity` - theme.visualDensity
344 /// * `tapTargetSize` - theme.materialTapTargetSize
345 /// * `animationDuration` - kThemeChangeDuration
346 /// * `enableFeedback` - true
347 /// * `alignment` - Alignment.center
348 /// * `splashFactory` - InkRipple.splashFactory
349 ///
350 /// The default padding values for the [TextButton.icon] factory are slightly different:
351 ///
352 /// * `padding`
353 /// * `default font size <= 14` - all(8)
354 /// * `14 < default font size <= 28 `- lerp(all(8), horizontal(4))
355 /// * `28 < default font size` - horizontal(4)
356 ///
357 /// The default value for `side`, which defines the appearance of the button's
358 /// outline, is null. That means that the outline is defined by the button
359 /// shape's [OutlinedBorder.side]. Typically the default value of an
360 /// [OutlinedBorder]'s side is [BorderSide.none], so an outline is not drawn.
361 ///
362 /// ## Material 3 defaults
363 ///
364 /// If [ThemeData.useMaterial3] is set to true the following defaults will
365 /// be used:
366 ///
367 /// {@template flutter.material.text_button.material3_defaults}
368 /// * `textStyle` - Theme.textTheme.labelLarge
369 /// * `backgroundColor` - transparent
370 /// * `foregroundColor`
371 /// * disabled - Theme.colorScheme.onSurface(0.38)
372 /// * others - Theme.colorScheme.primary
373 /// * `overlayColor`
374 /// * hovered - Theme.colorScheme.primary(0.08)
375 /// * focused or pressed - Theme.colorScheme.primary(0.1)
376 /// * others - null
377 /// * `shadowColor` - Colors.transparent,
378 /// * `surfaceTintColor` - null
379 /// * `elevation` - 0
380 /// * `padding`
381 /// * `default font size <= 14` - lerp(horizontal(12), horizontal(4))
382 /// * `14 < default font size <= 28` - lerp(all(8), horizontal(8))
383 /// * `28 < default font size <= 36` - lerp(horizontal(8), horizontal(4))
384 /// * `36 < default font size` - horizontal(4)
385 /// * `minimumSize` - Size(64, 40)
386 /// * `fixedSize` - null
387 /// * `maximumSize` - Size.infinite
388 /// * `side` - null
389 /// * `shape` - StadiumBorder()
390 /// * `mouseCursor`
391 /// * disabled - SystemMouseCursors.basic
392 /// * others - SystemMouseCursors.click
393 /// * `visualDensity` - theme.visualDensity
394 /// * `tapTargetSize` - theme.materialTapTargetSize
395 /// * `animationDuration` - kThemeChangeDuration
396 /// * `enableFeedback` - true
397 /// * `alignment` - Alignment.center
398 /// * `splashFactory` - Theme.splashFactory
399 ///
400 /// For the [TextButton.icon] factory, the end (generally the right) value of
401 /// `padding` is increased from 12 to 16.
402 /// {@endtemplate}
403 @override
404 ButtonStyle defaultStyleOf(BuildContext context) {
405 final ThemeData theme = Theme.of(context);
406 final ColorScheme colorScheme = theme.colorScheme;
407
408 return Theme.of(context).useMaterial3
409 ? _TextButtonDefaultsM3(context)
410 : styleFrom(
411 foregroundColor: colorScheme.primary,
412 disabledForegroundColor: colorScheme.onSurface.withOpacity(0.38),
413 backgroundColor: Colors.transparent,
414 disabledBackgroundColor: Colors.transparent,
415 shadowColor: theme.shadowColor,
416 elevation: 0,
417 textStyle: theme.textTheme.labelLarge,
418 padding: _scaledPadding(context),
419 minimumSize: const Size(64, 36),
420 maximumSize: Size.infinite,
421 shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))),
422 enabledMouseCursor: SystemMouseCursors.click,
423 disabledMouseCursor: SystemMouseCursors.basic,
424 visualDensity: theme.visualDensity,
425 tapTargetSize: theme.materialTapTargetSize,
426 animationDuration: kThemeChangeDuration,
427 enableFeedback: true,
428 alignment: Alignment.center,
429 splashFactory: InkRipple.splashFactory,
430 );
431 }
432
433 /// Returns the [TextButtonThemeData.style] of the closest
434 /// [TextButtonTheme] ancestor.
435 @override
436 ButtonStyle? themeStyleOf(BuildContext context) {
437 return TextButtonTheme.of(context).style;
438 }
439}
440
441EdgeInsetsGeometry _scaledPadding(BuildContext context) {
442 final ThemeData theme = Theme.of(context);
443 final double defaultFontSize = theme.textTheme.labelLarge?.fontSize ?? 14.0;
444 final double effectiveTextScale = MediaQuery.textScalerOf(context).scale(defaultFontSize) / 14.0;
445 return ButtonStyleButton.scaledPadding(
446 theme.useMaterial3
447 ? const EdgeInsets.symmetric(horizontal: 12, vertical: 8)
448 : const EdgeInsets.all(8),
449 const EdgeInsets.symmetric(horizontal: 8),
450 const EdgeInsets.symmetric(horizontal: 4),
451 effectiveTextScale,
452 );
453}
454
455class _TextButtonWithIcon extends TextButton {
456 _TextButtonWithIcon({
457 super.key,
458 required super.onPressed,
459 super.onLongPress,
460 super.onHover,
461 super.onFocusChange,
462 super.style,
463 super.focusNode,
464 bool? autofocus,
465 super.clipBehavior,
466 super.statesController,
467 required Widget icon,
468 required Widget label,
469 IconAlignment? iconAlignment,
470 }) : super(
471 autofocus: autofocus ?? false,
472 child: _TextButtonWithIconChild(
473 icon: icon,
474 label: label,
475 buttonStyle: style,
476 iconAlignment: iconAlignment,
477 ),
478 );
479
480 @override
481 ButtonStyle defaultStyleOf(BuildContext context) {
482 final bool useMaterial3 = Theme.of(context).useMaterial3;
483 final ButtonStyle buttonStyle = super.defaultStyleOf(context);
484 final double defaultFontSize =
485 buttonStyle.textStyle?.resolve(const <MaterialState>{})?.fontSize ?? 14.0;
486 final double effectiveTextScale =
487 MediaQuery.textScalerOf(context).scale(defaultFontSize) / 14.0;
488 final EdgeInsetsGeometry scaledPadding = ButtonStyleButton.scaledPadding(
489 useMaterial3 ? const EdgeInsetsDirectional.fromSTEB(12, 8, 16, 8) : const EdgeInsets.all(8),
490 const EdgeInsets.symmetric(horizontal: 4),
491 const EdgeInsets.symmetric(horizontal: 4),
492 effectiveTextScale,
493 );
494 return buttonStyle.copyWith(
495 padding: MaterialStatePropertyAll<EdgeInsetsGeometry>(scaledPadding),
496 );
497 }
498}
499
500class _TextButtonWithIconChild extends StatelessWidget {
501 const _TextButtonWithIconChild({
502 required this.label,
503 required this.icon,
504 required this.buttonStyle,
505 required this.iconAlignment,
506 });
507
508 final Widget label;
509 final Widget icon;
510 final ButtonStyle? buttonStyle;
511 final IconAlignment? iconAlignment;
512
513 @override
514 Widget build(BuildContext context) {
515 final double defaultFontSize =
516 buttonStyle?.textStyle?.resolve(const <MaterialState>{})?.fontSize ?? 14.0;
517 final double scale =
518 clampDouble(MediaQuery.textScalerOf(context).scale(defaultFontSize) / 14.0, 1.0, 2.0) - 1.0;
519 final TextButtonThemeData textButtonTheme = TextButtonTheme.of(context);
520 final IconAlignment effectiveIconAlignment =
521 iconAlignment ??
522 textButtonTheme.style?.iconAlignment ??
523 buttonStyle?.iconAlignment ??
524 IconAlignment.start;
525 return Row(
526 mainAxisSize: MainAxisSize.min,
527 spacing: lerpDouble(8, 4, scale)!,
528 children: effectiveIconAlignment == IconAlignment.start
529 ? <Widget>[icon, Flexible(child: label)]
530 : <Widget>[Flexible(child: label), icon],
531 );
532 }
533}
534
535// BEGIN GENERATED TOKEN PROPERTIES - TextButton
536
537// Do not edit by hand. The code between the "BEGIN GENERATED" and
538// "END GENERATED" comments are generated from data in the Material
539// Design token database by the script:
540// dev/tools/gen_defaults/bin/gen_defaults.dart.
541
542// dart format off
543class _TextButtonDefaultsM3 extends ButtonStyle {
544 _TextButtonDefaultsM3(this.context)
545 : super(
546 animationDuration: kThemeChangeDuration,
547 enableFeedback: true,
548 alignment: Alignment.center,
549 );
550
551 final BuildContext context;
552 late final ColorScheme _colors = Theme.of(context).colorScheme;
553
554 @override
555 MaterialStateProperty<TextStyle?> get textStyle =>
556 MaterialStatePropertyAll<TextStyle?>(Theme.of(context).textTheme.labelLarge);
557
558 @override
559 MaterialStateProperty<Color?>? get backgroundColor =>
560 const MaterialStatePropertyAll<Color>(Colors.transparent);
561
562 @override
563 MaterialStateProperty<Color?>? get foregroundColor =>
564 MaterialStateProperty.resolveWith((Set<MaterialState> states) {
565 if (states.contains(MaterialState.disabled)) {
566 return _colors.onSurface.withOpacity(0.38);
567 }
568 return _colors.primary;
569 });
570
571 @override
572 MaterialStateProperty<Color?>? get overlayColor =>
573 MaterialStateProperty.resolveWith((Set<MaterialState> states) {
574 if (states.contains(MaterialState.pressed)) {
575 return _colors.primary.withOpacity(0.1);
576 }
577 if (states.contains(MaterialState.hovered)) {
578 return _colors.primary.withOpacity(0.08);
579 }
580 if (states.contains(MaterialState.focused)) {
581 return _colors.primary.withOpacity(0.1);
582 }
583 return null;
584 });
585
586 @override
587 MaterialStateProperty<Color>? get shadowColor =>
588 const MaterialStatePropertyAll<Color>(Colors.transparent);
589
590 @override
591 MaterialStateProperty<Color>? get surfaceTintColor =>
592 const MaterialStatePropertyAll<Color>(Colors.transparent);
593
594 @override
595 MaterialStateProperty<double>? get elevation =>
596 const MaterialStatePropertyAll<double>(0.0);
597
598 @override
599 MaterialStateProperty<EdgeInsetsGeometry>? get padding =>
600 MaterialStatePropertyAll<EdgeInsetsGeometry>(_scaledPadding(context));
601
602 @override
603 MaterialStateProperty<Size>? get minimumSize =>
604 const MaterialStatePropertyAll<Size>(Size(64.0, 40.0));
605
606 // No default fixedSize
607
608 @override
609 MaterialStateProperty<double>? get iconSize =>
610 const MaterialStatePropertyAll<double>(18.0);
611
612 @override
613 MaterialStateProperty<Color>? get iconColor {
614 return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
615 if (states.contains(MaterialState.disabled)) {
616 return _colors.onSurface.withOpacity(0.38);
617 }
618 if (states.contains(MaterialState.pressed)) {
619 return _colors.primary;
620 }
621 if (states.contains(MaterialState.hovered)) {
622 return _colors.primary;
623 }
624 if (states.contains(MaterialState.focused)) {
625 return _colors.primary;
626 }
627 return _colors.primary;
628 });
629 }
630
631 @override
632 MaterialStateProperty<Size>? get maximumSize =>
633 const MaterialStatePropertyAll<Size>(Size.infinite);
634
635 // No default side
636
637 @override
638 MaterialStateProperty<OutlinedBorder>? get shape =>
639 const MaterialStatePropertyAll<OutlinedBorder>(StadiumBorder());
640
641 @override
642 MaterialStateProperty<MouseCursor?>? get mouseCursor =>
643 MaterialStateProperty.resolveWith((Set<MaterialState> states) {
644 if (states.contains(MaterialState.disabled)) {
645 return SystemMouseCursors.basic;
646 }
647 return SystemMouseCursors.click;
648 });
649
650 @override
651 VisualDensity? get visualDensity => Theme.of(context).visualDensity;
652
653 @override
654 MaterialTapTargetSize? get tapTargetSize => Theme.of(context).materialTapTargetSize;
655
656 @override
657 InteractiveInkFeatureFactory? get splashFactory => Theme.of(context).splashFactory;
658}
659// dart format on
660
661// END GENERATED TOKEN PROPERTIES - TextButton
662