box_shadow.dart 4.53 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5
// 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;
6
import 'dart:ui' as ui show Shadow, lerpDouble;
7 8 9 10

import 'package:flutter/foundation.dart';

import 'basic_types.dart';
11
import 'debug.dart';
12 13 14

/// A shadow cast by a box.
///
15
/// [BoxShadow] can cast non-rectangular shadows if the box is non-rectangular
16 17 18
/// (e.g., has a border radius or a circular shape).
///
/// This class is similar to CSS box-shadow.
19 20 21 22
///
/// See also:
///
///  * [Canvas.drawShadow], which is a more efficient way to draw shadows.
Ian Hickson's avatar
Ian Hickson committed
23 24 25
///  * [PhysicalModel], a widget for showing shadows.
///  * [kElevationToShadow], for some predefined shadows used in Material
///    Design.
26
///  * [Shadow], which is the parent class that lacks [spreadRadius].
27
@immutable
28
class BoxShadow extends ui.Shadow {
29 30
  /// Creates a box shadow.
  ///
31 32
  /// By default, the shadow is solid black with zero [offset], zero [blurRadius],
  /// zero [spreadRadius], and [BlurStyle.normal].
33
  const BoxShadow({
34 35 36
    super.color,
    super.offset,
    super.blurRadius,
37
    this.spreadRadius = 0.0,
38
    this.blurStyle = BlurStyle.normal,
39
  });
40 41 42 43

  /// The amount the box should be inflated prior to applying the blur.
  final double spreadRadius;

44 45 46 47 48
  /// The [BlurStyle] to use for this shadow.
  ///
  /// Defaults to [BlurStyle.normal].
  final BlurStyle blurStyle;

49 50 51 52 53 54
  /// Create the [Paint] object that corresponds to this shadow description.
  ///
  /// The [offset] and [spreadRadius] are not represented in the [Paint] object.
  /// To honor those as well, the shape should be inflated by [spreadRadius] pixels
  /// in every direction and then translated by [offset] before being filled using
  /// this [Paint].
55
  @override
56
  Paint toPaint() {
57
    final Paint result = Paint()
58
      ..color = color
59
      ..maskFilter = MaskFilter.blur(blurStyle, blurSigma);
60
    assert(() {
61
      if (debugDisableShadows) {
62
        result.maskFilter = null;
63
      }
64 65 66 67 68
      return true;
    }());
    return result;
  }

69
  /// Returns a new box shadow with its offset, blurRadius, and spreadRadius scaled by the given factor.
70
  @override
71
  BoxShadow scale(double factor) {
72
    return BoxShadow(
73 74 75
      color: color,
      offset: offset * factor,
      blurRadius: blurRadius * factor,
76
      spreadRadius: spreadRadius * factor,
77
      blurStyle: blurStyle,
78 79 80 81 82
    );
  }

  /// Linearly interpolate between two box shadows.
  ///
83
  /// If either box shadow is null, this function linearly interpolates from
84 85
  /// a box shadow that matches the other box shadow in color but has a zero
  /// offset and a zero blurRadius.
86
  ///
87
  /// {@macro dart.ui.shadow.lerp}
88
  static BoxShadow? lerp(BoxShadow? a, BoxShadow? b, double t) {
89 90
    if (identical(a, b)) {
      return a;
91 92
    }
    if (a == null) {
93
      return b!.scale(t);
94 95
    }
    if (b == null) {
96
      return a.scale(1.0 - t);
97
    }
98
    return BoxShadow(
99 100 101 102
      color: Color.lerp(a.color, b.color, t)!,
      offset: Offset.lerp(a.offset, b.offset, t)!,
      blurRadius: ui.lerpDouble(a.blurRadius, b.blurRadius, t)!,
      spreadRadius: ui.lerpDouble(a.spreadRadius, b.spreadRadius, t)!,
103
      blurStyle: a.blurStyle == BlurStyle.normal ? b.blurStyle : a.blurStyle,
104 105 106 107 108 109
    );
  }

  /// Linearly interpolate between two lists of box shadows.
  ///
  /// If the lists differ in length, excess items are lerped with null.
110
  ///
111
  /// {@macro dart.ui.shadow.lerp}
112
  static List<BoxShadow>? lerpList(List<BoxShadow>? a, List<BoxShadow>? b, double t) {
113 114
    if (identical(a, b)) {
      return a;
115
    }
116 117 118
    a ??= <BoxShadow>[];
    b ??= <BoxShadow>[];
    final int commonLength = math.min(a.length, b.length);
119
    return <BoxShadow>[
120
      for (int i = 0; i < commonLength; i += 1) BoxShadow.lerp(a[i], b[i], t)!,
121 122 123
      for (int i = commonLength; i < a.length; i += 1) a[i].scale(1.0 - t),
      for (int i = commonLength; i < b.length; i += 1) b[i].scale(t),
    ];
124 125 126
  }

  @override
127
  bool operator ==(Object other) {
128
    if (identical(this, other)) {
129
      return true;
130 131
    }
    if (other.runtimeType != runtimeType) {
132
      return false;
133
    }
134 135 136 137
    return other is BoxShadow
        && other.color == color
        && other.offset == offset
        && other.blurRadius == blurRadius
138 139
        && other.spreadRadius == spreadRadius
        && other.blurStyle == blurStyle;
140 141 142
  }

  @override
143
  int get hashCode => Object.hash(color, offset, blurRadius, spreadRadius, blurStyle);
144 145

  @override
146
  String toString() => 'BoxShadow($color, $offset, ${debugFormatDouble(blurRadius)}, ${debugFormatDouble(spreadRadius)}, $blurStyle)';
147
}