button_style.dart 22 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// 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:ui' show lerpDouble;

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

11
import 'ink_well.dart';
12 13 14 15 16 17
import 'material_state.dart';
import 'theme_data.dart';

/// The visual properties that most buttons have in common.
///
/// Buttons and their themes have a ButtonStyle property which defines the visual
18 19
/// properties whose default values are to be overridden. The default values are
/// defined by the individual button widgets and are typically based on overall
20 21 22 23 24 25 26 27 28 29 30
/// theme's [ThemeData.colorScheme] and [ThemeData.textTheme].
///
/// All of the ButtonStyle properties are null by default.
///
/// Many of the ButtonStyle properties are [MaterialStateProperty] objects which
/// resolve to different values depending on the button's state. For example
/// the [Color] properties are defined with `MaterialStateProperty<Color>` and
/// can resolve to different colors depending on if the button is pressed,
/// hovered, focused, disabled, etc.
///
/// These properties can override the default value for just one state or all of
31
/// them. For example to create a [ElevatedButton] whose background color is the
32 33 34 35
/// color scheme’s primary color with 50% opacity, but only when the button is
/// pressed, one could write:
///
/// ```dart
36
/// ElevatedButton(
37
///   style: ButtonStyle(
38
///     backgroundColor: MaterialStateProperty.resolveWith<Color?>(
39 40 41 42 43 44 45 46
///       (Set<MaterialState> states) {
///         if (states.contains(MaterialState.pressed))
///           return Theme.of(context).colorScheme.primary.withOpacity(0.5);
///         return null; // Use the component's default.
///       },
///     ),
///   ),
/// )
47
/// ```
48 49
///
/// In this case the background color for all other button states would fallback
50
/// to the ElevatedButton’s default values. To unconditionally set the button's
51 52 53
/// [backgroundColor] for all states one could write:
///
/// ```dart
54
/// ElevatedButton(
55 56 57 58
///   style: ButtonStyle(
///     backgroundColor: MaterialStateProperty.all<Color>(Colors.green),
///   ),
/// )
59
/// ```
60 61 62 63 64 65 66 67
///
/// Configuring a ButtonStyle directly makes it possible to very
/// precisely control the button’s visual attributes for all states.
/// This level of control is typically required when a custom
/// “branded” look and feel is desirable.  However, in many cases it’s
/// useful to make relatively sweeping changes based on a few initial
/// parameters with simple values. The button styleFrom() methods
/// enable such sweeping changes. See for example:
68
/// [TextButton.styleFrom], [ElevatedButton.styleFrom],
69 70 71 72 73 74 75 76 77 78 79
/// [OutlinedButton.styleFrom].
///
/// 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(
///   style: TextButton.styleFrom(primary: Colors.green),
/// )
80
/// ```
81 82 83 84 85 86 87 88 89 90 91 92
///
/// To configure all of the application's text buttons in the same
/// way, specify the overall theme's `textButtonTheme`:
/// ```dart
/// MaterialApp(
///   theme: ThemeData(
///     textButtonTheme: TextButtonThemeData(
///       style: TextButton.styleFrom(primary: Colors.green),
///     ),
///   ),
///   home: MyAppHome(),
/// )
93
/// ```
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
///
/// ## Material 3 button types
///
/// Material Design 3 specifies five types of common buttons. Flutter provides
/// support for these using the following button classes:
/// <style>table,td,th { border-collapse: collapse; padding: 0.45em; } td { border: 1px solid }</style>
///
/// | Type         | Flutter implementation  |
/// | :----------- | :---------------------- |
/// | Elevated     | [ElevatedButton]        |
/// | Filled       | Styled [ElevatedButton] |
/// | Filled Tonal | Styled [ElevatedButton] |
/// | Outlined     | [OutlinedButton]        |
/// | Text         | [TextButton]            |
///
/// {@tool dartpad}
/// This sample shows how to create each of the Material 3 button types with Flutter.
///
/// ** See code in examples/api/lib/material/button_style/button_style.0.dart **
/// {@end-tool}
///
115 116 117
/// See also:
///
///  * [TextButtonTheme], the theme for [TextButton]s.
118
///  * [ElevatedButtonTheme], the theme for [ElevatedButton]s.
119 120 121 122 123 124 125 126 127 128
///  * [OutlinedButtonTheme], the theme for [OutlinedButton]s.
@immutable
class ButtonStyle with Diagnosticable {
  /// Create a [ButtonStyle].
  const ButtonStyle({
    this.textStyle,
    this.backgroundColor,
    this.foregroundColor,
    this.overlayColor,
    this.shadowColor,
129
    this.surfaceTintColor,
130 131 132
    this.elevation,
    this.padding,
    this.minimumSize,
133
    this.fixedSize,
134
    this.maximumSize,
135 136 137 138 139 140 141
    this.side,
    this.shape,
    this.mouseCursor,
    this.visualDensity,
    this.tapTargetSize,
    this.animationDuration,
    this.enableFeedback,
142
    this.alignment,
143
    this.splashFactory,
144 145 146 147 148
  });

  /// The style for a button's [Text] widget descendants.
  ///
  /// The color of the [textStyle] is typically not used directly, the
149
  /// [foregroundColor] is used instead.
150
  final MaterialStateProperty<TextStyle?>? textStyle;
151 152

  /// The button's background fill color.
153
  final MaterialStateProperty<Color?>? backgroundColor;
154 155 156 157 158 159 160

  /// The color for the button's [Text] and [Icon] widget descendants.
  ///
  /// This color is typically used instead of the color of the [textStyle]. All
  /// of the components that compute defaults from [ButtonStyle] values
  /// compute a default [foregroundColor] and use that instead of the
  /// [textStyle]'s color.
161
  final MaterialStateProperty<Color?>? foregroundColor;
162 163 164

  /// The highlight color that's typically used to indicate that
  /// the button is focused, hovered, or pressed.
165
  final MaterialStateProperty<Color?>? overlayColor;
166 167 168 169 170 171 172

  /// The shadow color of the button's [Material].
  ///
  /// The material's elevation shadow can be difficult to see for
  /// dark themes, so by default the button classes add a
  /// semi-transparent overlay to indicate elevation. See
  /// [ThemeData.applyElevationOverlayColor].
173
  final MaterialStateProperty<Color?>? shadowColor;
174

175 176 177 178 179
  /// The surface tint color of the button's [Material].
  ///
  /// See [Material.surfaceTintColor] for more details.
  final MaterialStateProperty<Color?>? surfaceTintColor;

180
  /// The elevation of the button's [Material].
181
  final MaterialStateProperty<double?>? elevation;
182 183

  /// The padding between the button's boundary and its child.
184
  final MaterialStateProperty<EdgeInsetsGeometry?>? padding;
185 186 187 188 189

  /// The minimum size of the button itself.
  ///
  /// The size of the rectangle the button lies within may be larger
  /// per [tapTargetSize].
190 191
  ///
  /// This value must be less than or equal to [maximumSize].
192
  final MaterialStateProperty<Size?>? minimumSize;
193

194 195
  /// The button's size.
  ///
196 197 198
  /// This size is still constrained by the style's [minimumSize]
  /// and [maximumSize]. Fixed size dimensions whose value is
  /// [double.infinity] are ignored.
199 200 201 202 203 204
  ///
  /// To specify buttons with a fixed width and the default height use
  /// `fixedSize: Size.fromWidth(320)`. Similarly, to specify a fixed
  /// height and the default width use `fixedSize: Size.fromHeight(100)`.
  final MaterialStateProperty<Size?>? fixedSize;

205 206 207 208 209 210 211 212
  /// The maximum size of the button itself.
  ///
  /// A [Size.infinite] or null value for this property means that
  /// the button's maximum size is not constrained.
  ///
  /// This value must be greater than or equal to [minimumSize].
  final MaterialStateProperty<Size?>? maximumSize;

213 214 215 216
  /// The color and weight of the button's outline.
  ///
  /// This value is combined with [shape] to create a shape decorated
  /// with an outline.
217
  final MaterialStateProperty<BorderSide?>? side;
218 219 220 221 222

  /// The shape of the button's underlying [Material].
  ///
  /// This shape is combined with [side] to create a shape decorated
  /// with an outline.
223
  final MaterialStateProperty<OutlinedBorder?>? shape;
224 225 226

  /// The cursor for a mouse pointer when it enters or is hovering over
  /// this button's [InkWell].
227
  final MaterialStateProperty<MouseCursor?>? mouseCursor;
228 229 230 231 232 233 234 235 236

  /// Defines how compact the button's layout will be.
  ///
  /// {@macro flutter.material.themedata.visualDensity}
  ///
  /// See also:
  ///
  ///  * [ThemeData.visualDensity], which specifies the [visualDensity] for all widgets
  ///    within a [Theme].
237
  final VisualDensity? visualDensity;
238 239 240 241 242 243 244

  /// Configures the minimum size of the area within which the button may be pressed.
  ///
  /// If the [tapTargetSize] is larger than [minimumSize], the button will include
  /// a transparent margin that responds to taps.
  ///
  /// Always defaults to [ThemeData.materialTapTargetSize].
245
  final MaterialTapTargetSize? tapTargetSize;
246 247 248 249

  /// Defines the duration of animated changes for [shape] and [elevation].
  ///
  /// Typically the component default value is [kThemeChangeDuration].
250
  final Duration? animationDuration;
251 252 253 254 255 256 257 258 259 260 261

  /// Whether detected gestures should provide acoustic and/or haptic feedback.
  ///
  /// For example, on Android a tap will produce a clicking sound and a
  /// long-press will produce a short vibration, when feedback is enabled.
  ///
  /// Typically the component default value is true.
  ///
  /// See also:
  ///
  ///  * [Feedback] for providing platform-specific feedback to certain actions.
262
  final bool? enableFeedback;
263

264 265 266 267 268 269 270 271 272 273
  /// The alignment of the button's child.
  ///
  /// Typically buttons are sized to be just big enough to contain the child and its
  /// padding. If the button's size is constrained to a fixed size, for example by
  /// enclosing it with a [SizedBox], this property defines how the child is aligned
  /// within the available space.
  ///
  /// Always defaults to [Alignment.center].
  final AlignmentGeometry? alignment;

274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
  /// Creates the [InkWell] splash factory, which defines the appearance of
  /// "ink" splashes that occur in response to taps.
  ///
  /// Use [NoSplash.splashFactory] to defeat ink splash rendering. For example:
  /// ```dart
  /// ElevatedButton(
  ///   style: ElevatedButton.styleFrom(
  ///     splashFactory: NoSplash.splashFactory,
  ///   ),
  ///   onPressed: () { },
  ///   child: Text('No Splash'),
  /// )
  /// ```
  final InteractiveInkFeatureFactory? splashFactory;

289 290 291
  /// Returns a copy of this ButtonStyle with the given fields replaced with
  /// the new values.
  ButtonStyle copyWith({
292 293 294 295 296
    MaterialStateProperty<TextStyle?>? textStyle,
    MaterialStateProperty<Color?>? backgroundColor,
    MaterialStateProperty<Color?>? foregroundColor,
    MaterialStateProperty<Color?>? overlayColor,
    MaterialStateProperty<Color?>? shadowColor,
297
    MaterialStateProperty<Color?>? surfaceTintColor,
298 299 300
    MaterialStateProperty<double?>? elevation,
    MaterialStateProperty<EdgeInsetsGeometry?>? padding,
    MaterialStateProperty<Size?>? minimumSize,
301
    MaterialStateProperty<Size?>? fixedSize,
302
    MaterialStateProperty<Size?>? maximumSize,
303 304 305
    MaterialStateProperty<BorderSide?>? side,
    MaterialStateProperty<OutlinedBorder?>? shape,
    MaterialStateProperty<MouseCursor?>? mouseCursor,
306 307 308 309
    VisualDensity? visualDensity,
    MaterialTapTargetSize? tapTargetSize,
    Duration? animationDuration,
    bool? enableFeedback,
310
    AlignmentGeometry? alignment,
311
    InteractiveInkFeatureFactory? splashFactory,
312 313 314 315 316 317 318
  }) {
    return ButtonStyle(
      textStyle: textStyle ?? this.textStyle,
      backgroundColor: backgroundColor ?? this.backgroundColor,
      foregroundColor: foregroundColor ?? this.foregroundColor,
      overlayColor: overlayColor ?? this.overlayColor,
      shadowColor: shadowColor ?? this.shadowColor,
319
      surfaceTintColor: surfaceTintColor ?? this.surfaceTintColor,
320 321 322
      elevation: elevation ?? this.elevation,
      padding: padding ?? this.padding,
      minimumSize: minimumSize ?? this.minimumSize,
323
      fixedSize: fixedSize ?? this.fixedSize,
324
      maximumSize: maximumSize ?? this.maximumSize,
325 326 327 328 329 330 331
      side: side ?? this.side,
      shape: shape ?? this.shape,
      mouseCursor: mouseCursor ?? this.mouseCursor,
      visualDensity: visualDensity ?? this.visualDensity,
      tapTargetSize: tapTargetSize ?? this.tapTargetSize,
      animationDuration: animationDuration ?? this.animationDuration,
      enableFeedback: enableFeedback ?? this.enableFeedback,
332
      alignment: alignment ?? this.alignment,
333
      splashFactory: splashFactory ?? this.splashFactory,
334 335 336 337 338 339 340 341
    );
  }

  /// Returns a copy of this ButtonStyle where the non-null fields in [style]
  /// have replaced the corresponding null fields in this ButtonStyle.
  ///
  /// In other words, [style] is used to fill in unspecified (null) fields
  /// this ButtonStyle.
342
  ButtonStyle merge(ButtonStyle? style) {
343 344 345 346 347 348 349 350
    if (style == null)
      return this;
    return copyWith(
      textStyle: textStyle ?? style.textStyle,
      backgroundColor: backgroundColor ?? style.backgroundColor,
      foregroundColor: foregroundColor ?? style.foregroundColor,
      overlayColor: overlayColor ?? style.overlayColor,
      shadowColor: shadowColor ?? style.shadowColor,
351
      surfaceTintColor: surfaceTintColor ?? style.surfaceTintColor,
352 353 354
      elevation: elevation ?? style.elevation,
      padding: padding ?? style.padding,
      minimumSize: minimumSize ?? style.minimumSize,
355
      fixedSize: fixedSize ?? style.fixedSize,
356
      maximumSize: maximumSize ?? style.maximumSize,
357 358 359 360 361 362 363
      side: side ?? style.side,
      shape: shape ?? style.shape,
      mouseCursor: mouseCursor ?? style.mouseCursor,
      visualDensity: visualDensity ?? style.visualDensity,
      tapTargetSize: tapTargetSize ?? style.tapTargetSize,
      animationDuration: animationDuration ?? style.animationDuration,
      enableFeedback: enableFeedback ?? style.enableFeedback,
364
      alignment: alignment ?? style.alignment,
365
      splashFactory: splashFactory ?? style.splashFactory,
366 367 368 369
    );
  }

  @override
370 371 372 373 374 375
  int get hashCode => Object.hash(
    textStyle,
    backgroundColor,
    foregroundColor,
    overlayColor,
    shadowColor,
376
    surfaceTintColor,
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
    elevation,
    padding,
    minimumSize,
    fixedSize,
    maximumSize,
    side,
    shape,
    mouseCursor,
    visualDensity,
    tapTargetSize,
    animationDuration,
    enableFeedback,
    alignment,
    splashFactory,
  );
392 393 394 395 396 397 398 399 400 401 402 403 404

  @override
  bool operator ==(Object other) {
    if (identical(this, other))
      return true;
    if (other.runtimeType != runtimeType)
      return false;
    return other is ButtonStyle
        && other.textStyle == textStyle
        && other.backgroundColor == backgroundColor
        && other.foregroundColor == foregroundColor
        && other.overlayColor == overlayColor
        && other.shadowColor == shadowColor
405
        && other.surfaceTintColor == surfaceTintColor
406 407 408
        && other.elevation == elevation
        && other.padding == padding
        && other.minimumSize == minimumSize
409
        && other.fixedSize == fixedSize
410
        && other.maximumSize == maximumSize
411 412 413 414 415 416
        && other.side == side
        && other.shape == shape
        && other.mouseCursor == mouseCursor
        && other.visualDensity == visualDensity
        && other.tapTargetSize == tapTargetSize
        && other.animationDuration == animationDuration
417
        && other.enableFeedback == enableFeedback
418 419
        && other.alignment == alignment
        && other.splashFactory == splashFactory;
420 421 422 423 424
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
425 426 427 428 429
    properties.add(DiagnosticsProperty<MaterialStateProperty<TextStyle?>>('textStyle', textStyle, defaultValue: null));
    properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('backgroundColor', backgroundColor, defaultValue: null));
    properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('foregroundColor', foregroundColor, defaultValue: null));
    properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('overlayColor', overlayColor, defaultValue: null));
    properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('shadowColor', shadowColor, defaultValue: null));
430
    properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('surfaceTintColor', surfaceTintColor, defaultValue: null));
431 432 433
    properties.add(DiagnosticsProperty<MaterialStateProperty<double?>>('elevation', elevation, defaultValue: null));
    properties.add(DiagnosticsProperty<MaterialStateProperty<EdgeInsetsGeometry?>>('padding', padding, defaultValue: null));
    properties.add(DiagnosticsProperty<MaterialStateProperty<Size?>>('minimumSize', minimumSize, defaultValue: null));
434
    properties.add(DiagnosticsProperty<MaterialStateProperty<Size?>>('fixedSize', fixedSize, defaultValue: null));
435
    properties.add(DiagnosticsProperty<MaterialStateProperty<Size?>>('maximumSize', maximumSize, defaultValue: null));
436 437 438
    properties.add(DiagnosticsProperty<MaterialStateProperty<BorderSide?>>('side', side, defaultValue: null));
    properties.add(DiagnosticsProperty<MaterialStateProperty<OutlinedBorder?>>('shape', shape, defaultValue: null));
    properties.add(DiagnosticsProperty<MaterialStateProperty<MouseCursor?>>('mouseCursor', mouseCursor, defaultValue: null));
439 440 441 442
    properties.add(DiagnosticsProperty<VisualDensity>('visualDensity', visualDensity, defaultValue: null));
    properties.add(EnumProperty<MaterialTapTargetSize>('tapTargetSize', tapTargetSize, defaultValue: null));
    properties.add(DiagnosticsProperty<Duration>('animationDuration', animationDuration, defaultValue: null));
    properties.add(DiagnosticsProperty<bool>('enableFeedback', enableFeedback, defaultValue: null));
443
    properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment', alignment, defaultValue: null));
444 445 446
  }

  /// Linearly interpolate between two [ButtonStyle]s.
447
  static ButtonStyle? lerp(ButtonStyle? a, ButtonStyle? b, double t) {
448 449 450 451
    assert (t != null);
    if (a == null && b == null)
      return null;
    return ButtonStyle(
452 453 454 455 456
      textStyle: _lerpProperties<TextStyle?>(a?.textStyle, b?.textStyle, t, TextStyle.lerp),
      backgroundColor: _lerpProperties<Color?>(a?.backgroundColor, b?.backgroundColor, t, Color.lerp),
      foregroundColor:  _lerpProperties<Color?>(a?.foregroundColor, b?.foregroundColor, t, Color.lerp),
      overlayColor: _lerpProperties<Color?>(a?.overlayColor, b?.overlayColor, t, Color.lerp),
      shadowColor: _lerpProperties<Color?>(a?.shadowColor, b?.shadowColor, t, Color.lerp),
457
      surfaceTintColor: _lerpProperties<Color?>(a?.surfaceTintColor, b?.surfaceTintColor, t, Color.lerp),
458 459 460
      elevation: _lerpProperties<double?>(a?.elevation, b?.elevation, t, lerpDouble),
      padding:  _lerpProperties<EdgeInsetsGeometry?>(a?.padding, b?.padding, t, EdgeInsetsGeometry.lerp),
      minimumSize: _lerpProperties<Size?>(a?.minimumSize, b?.minimumSize, t, Size.lerp),
461
      fixedSize: _lerpProperties<Size?>(a?.fixedSize, b?.fixedSize, t, Size.lerp),
462
      maximumSize: _lerpProperties<Size?>(a?.maximumSize, b?.maximumSize, t, Size.lerp),
463 464
      side: _lerpSides(a?.side, b?.side, t),
      shape: _lerpShapes(a?.shape, b?.shape, t),
465 466 467 468 469
      mouseCursor: t < 0.5 ? a?.mouseCursor : b?.mouseCursor,
      visualDensity: t < 0.5 ? a?.visualDensity : b?.visualDensity,
      tapTargetSize: t < 0.5 ? a?.tapTargetSize : b?.tapTargetSize,
      animationDuration: t < 0.5 ? a?.animationDuration : b?.animationDuration,
      enableFeedback: t < 0.5 ? a?.enableFeedback : b?.enableFeedback,
470
      alignment: AlignmentGeometry.lerp(a?.alignment, b?.alignment, t),
471
      splashFactory: t < 0.5 ? a?.splashFactory : b?.splashFactory,
472 473 474
    );
  }

475
  static MaterialStateProperty<T?>? _lerpProperties<T>(MaterialStateProperty<T>? a, MaterialStateProperty<T>? b, double t, T? Function(T?, T?, double) lerpFunction ) {
476 477 478 479 480 481 482
    // Avoid creating a _LerpProperties object for a common case.
    if (a == null && b == null)
      return null;
    return _LerpProperties<T>(a, b, t, lerpFunction);
  }

  // Special case because BorderSide.lerp() doesn't support null arguments
483
  static MaterialStateProperty<BorderSide?>? _lerpSides(MaterialStateProperty<BorderSide?>? a, MaterialStateProperty<BorderSide?>? b, double t) {
484 485 486 487 488 489
    if (a == null && b == null)
      return null;
    return _LerpSides(a, b, t);
  }

  // TODO(hansmuller): OutlinedBorder needs a lerp method - https://github.com/flutter/flutter/issues/60555.
490
  static MaterialStateProperty<OutlinedBorder?>? _lerpShapes(MaterialStateProperty<OutlinedBorder?>? a, MaterialStateProperty<OutlinedBorder?>? b, double t) {
491 492 493 494 495 496
    if (a == null && b == null)
      return null;
    return _LerpShapes(a, b, t);
  }
}

497
class _LerpProperties<T> implements MaterialStateProperty<T?> {
498 499
  const _LerpProperties(this.a, this.b, this.t, this.lerpFunction);

500 501
  final MaterialStateProperty<T>? a;
  final MaterialStateProperty<T>? b;
502
  final double t;
503
  final T? Function(T?, T?, double) lerpFunction;
504 505

  @override
506 507 508
  T? resolve(Set<MaterialState> states) {
    final T? resolvedA = a?.resolve(states);
    final T? resolvedB = b?.resolve(states);
509 510 511 512
    return lerpFunction(resolvedA, resolvedB, t);
  }
}

513
class _LerpSides implements MaterialStateProperty<BorderSide?> {
514 515
  const _LerpSides(this.a, this.b, this.t);

516 517
  final MaterialStateProperty<BorderSide?>? a;
  final MaterialStateProperty<BorderSide?>? b;
518 519 520
  final double t;

  @override
521 522 523
  BorderSide? resolve(Set<MaterialState> states) {
    final BorderSide? resolvedA = a?.resolve(states);
    final BorderSide? resolvedB = b?.resolve(states);
524 525 526
    if (resolvedA == null && resolvedB == null)
      return null;
    if (resolvedA == null)
527
      return BorderSide.lerp(BorderSide(width: 0, color: resolvedB!.color.withAlpha(0)), resolvedB, t);
528
    if (resolvedB == null)
529
      return BorderSide.lerp(resolvedA, BorderSide(width: 0, color: resolvedA.color.withAlpha(0)), t);
530 531 532 533
    return BorderSide.lerp(resolvedA, resolvedB, t);
  }
}

534
class _LerpShapes implements MaterialStateProperty<OutlinedBorder?> {
535 536
  const _LerpShapes(this.a, this.b, this.t);

537 538
  final MaterialStateProperty<OutlinedBorder?>? a;
  final MaterialStateProperty<OutlinedBorder?>? b;
539 540 541
  final double t;

  @override
542 543 544 545
  OutlinedBorder? resolve(Set<MaterialState> states) {
    final OutlinedBorder? resolvedA = a?.resolve(states);
    final OutlinedBorder? resolvedB = b?.resolve(states);
    return ShapeBorder.lerp(resolvedA, resolvedB, t) as OutlinedBorder?;
546 547
  }
}