box_shadow.dart 4.62 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5

6
import 'dart:math' as math;
7
import 'dart:ui' as ui show Shadow, lerpDouble;
8 9 10 11

import 'package:flutter/foundation.dart';

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

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

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

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

50 51 52 53 54 55
  /// 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].
56
  @override
57
  Paint toPaint() {
58
    final Paint result = Paint()
59
      ..color = color
60
      ..maskFilter = MaskFilter.blur(blurStyle, blurSigma);
61 62 63 64 65 66 67 68
    assert(() {
      if (debugDisableShadows)
        result.maskFilter = null;
      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 83 84 85
    );
  }

  /// Linearly interpolate between two box shadows.
  ///
  /// If either box shadow is null, this function linearly interpolates from a
  /// 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
    assert(t != null);
90 91 92
    if (a == null && b == null)
      return null;
    if (a == null)
93
      return b!.scale(t);
94 95
    if (b == null)
      return a.scale(1.0 - t);
96
    return BoxShadow(
97 98 99 100
      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)!,
101
      blurStyle: a.blurStyle == BlurStyle.normal ? b.blurStyle : a.blurStyle,
102 103 104 105 106 107
    );
  }

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

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

  @override
139
  int get hashCode => hashValues(color, offset, blurRadius, spreadRadius, blurStyle);
140 141

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