box_shadow.dart 4.33 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 31 32 33
  /// Creates a box shadow.
  ///
  /// By default, the shadow is solid black with zero [offset], [blurRadius],
  /// and [spreadRadius].
  const BoxShadow({
34 35 36
    Color color = const Color(0xFF000000),
    Offset offset = Offset.zero,
    double blurRadius = 0.0,
37
    this.spreadRadius = 0.0,
38
  }) : super(color: color, offset: offset, blurRadius: blurRadius);
39 40 41 42

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

43 44 45 46 47 48
  /// 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].
49
  @override
50
  Paint toPaint() {
51
    final Paint result = Paint()
52
      ..color = color
53
      ..maskFilter = MaskFilter.blur(BlurStyle.normal, blurSigma);
54 55 56 57 58 59 60 61
    assert(() {
      if (debugDisableShadows)
        result.maskFilter = null;
      return true;
    }());
    return result;
  }

62
  /// Returns a new box shadow with its offset, blurRadius, and spreadRadius scaled by the given factor.
63
  @override
64
  BoxShadow scale(double factor) {
65
    return BoxShadow(
66 67 68
      color: color,
      offset: offset * factor,
      blurRadius: blurRadius * factor,
69
      spreadRadius: spreadRadius * factor,
70 71 72 73 74 75 76 77
    );
  }

  /// 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.
78
  ///
79
  /// {@macro dart.ui.shadow.lerp}
80
  static BoxShadow lerp(BoxShadow a, BoxShadow b, double t) {
81
    assert(t != null);
82 83 84 85 86 87
    if (a == null && b == null)
      return null;
    if (a == null)
      return b.scale(t);
    if (b == null)
      return a.scale(1.0 - t);
88
    return BoxShadow(
89 90 91
      color: Color.lerp(a.color, b.color, t),
      offset: Offset.lerp(a.offset, b.offset, t),
      blurRadius: ui.lerpDouble(a.blurRadius, b.blurRadius, t),
92
      spreadRadius: ui.lerpDouble(a.spreadRadius, b.spreadRadius, t),
93 94 95 96 97 98
    );
  }

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

  @override
  bool operator ==(dynamic other) {
    if (identical(this, other))
      return true;
    if (runtimeType != other.runtimeType)
      return false;
    final BoxShadow typedOther = other;
    return color == typedOther.color &&
           offset == typedOther.offset &&
           blurRadius == typedOther.blurRadius &&
           spreadRadius == typedOther.spreadRadius;
  }

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

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