1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
// 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.
// @dart = 2.8
import 'package:flutter/foundation.dart';
import 'basic_types.dart';
import 'edge_insets.dart';
import 'image_provider.dart';
// This group of classes is intended for painting in cartesian coordinates.
/// A description of a box decoration (a decoration applied to a [Rect]).
///
/// This class presents the abstract interface for all decorations.
/// See [BoxDecoration] for a concrete example.
///
/// To actually paint a [Decoration], use the [createBoxPainter]
/// method to obtain a [BoxPainter]. [Decoration] objects can be
/// shared between boxes; [BoxPainter] objects can cache resources to
/// make painting on a particular surface faster.
@immutable
abstract class Decoration with Diagnosticable {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const Decoration();
@override
String toStringShort() => objectRuntimeType(this, 'Decoration');
/// In checked mode, throws an exception if the object is not in a
/// valid configuration. Otherwise, returns true.
///
/// This is intended to be used as follows:
/// ```dart
/// assert(myDecoration.debugAssertIsValid());
/// ```
bool debugAssertIsValid() => true;
/// Returns the insets to apply when using this decoration on a box
/// that has contents, so that the contents do not overlap the edges
/// of the decoration. For example, if the decoration draws a frame
/// around its edge, the padding would return the distance by which
/// to inset the children so as to not overlap the frame.
///
/// This only works for decorations that have absolute sizes. If the padding
/// needed would change based on the size at which the decoration is drawn,
/// then this will return incorrect padding values.
///
/// For example, when a [BoxDecoration] has [BoxShape.circle], the padding
/// does not take into account that the circle is drawn in the center of the
/// box regardless of the ratio of the box; it does not provide the extra
/// padding that is implied by changing the ratio.
///
/// The value returned by this getter must be resolved (using
/// [EdgeInsetsGeometry.resolve] to obtain an absolute [EdgeInsets]. (For
/// example, [BorderDirectional] will return an [EdgeInsetsDirectional] for
/// its [padding].)
EdgeInsetsGeometry get padding => EdgeInsets.zero;
/// Whether this decoration is complex enough to benefit from caching its painting.
bool get isComplex => false;
/// Linearly interpolates from another [Decoration] (which may be of a
/// different class) to `this`.
///
/// When implementing this method in subclasses, return null if this class
/// cannot interpolate from `a`. In that case, [lerp] will try `a`'s [lerpTo]
/// method instead.
///
/// Supporting interpolating from null is recommended as the [Decoration.lerp]
/// method uses this as a fallback when two classes can't interpolate between
/// each other.
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `this` (or something equivalent to `this`), and values in
/// between meaning that the interpolation is at the relevant point on the
/// timeline between `a` and `this`. The interpolation can be extrapolated
/// beyond 0.0 and 1.0, so negative values and values greater than 1.0 are
/// valid (and can easily be generated by curves such as
/// [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
///
/// Instead of calling this directly, use [Decoration.lerp].
@protected
Decoration lerpFrom(Decoration a, double t) => null;
/// Linearly interpolates from `this` to another [Decoration] (which may be of
/// a different class).
///
/// This is called if `b`'s [lerpTo] did not know how to handle this class.
///
/// When implementing this method in subclasses, return null if this class
/// cannot interpolate from `b`. In that case, [lerp] will apply a default
/// behavior instead.
///
/// Supporting interpolating to null is recommended as the [Decoration.lerp]
/// method uses this as a fallback when two classes can't interpolate between
/// each other.
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `this` (or something
/// equivalent to `this`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `this` and `b`. The interpolation can be extrapolated beyond 0.0
/// and 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
///
/// Instead of calling this directly, use [Decoration.lerp].
@protected
Decoration lerpTo(Decoration b, double t) => null;
/// Linearly interpolates between two [Decoration]s.
///
/// This attempts to use [lerpFrom] and [lerpTo] on `b` and `a`
/// respectively to find a solution. If the two values can't directly be
/// interpolated, then the interpolation is done via null (at `t == 0.5`).
///
/// {@macro dart.ui.shadow.lerp}
static Decoration lerp(Decoration a, Decoration b, double t) {
assert(t != null);
if (a == null && b == null)
return null;
if (a == null)
return b.lerpFrom(null, t) ?? b;
if (b == null)
return a.lerpTo(null, t) ?? a;
if (t == 0.0)
return a;
if (t == 1.0)
return b;
return b.lerpFrom(a, t)
?? a.lerpTo(b, t)
?? (t < 0.5 ? (a.lerpTo(null, t * 2.0) ?? a) : (b.lerpFrom(null, (t - 0.5) * 2.0) ?? b));
}
/// Tests whether the given point, on a rectangle of a given size,
/// would be considered to hit the decoration or not. For example,
/// if the decoration only draws a circle, this function might
/// return true if the point was inside the circle and false
/// otherwise.
///
/// The decoration may be sensitive to the [TextDirection]. The
/// `textDirection` argument should therefore be provided. If it is known that
/// the decoration is not affected by the text direction, then the argument
/// may be omitted or set to null.
///
/// When a [Decoration] is painted in a [Container] or [DecoratedBox] (which
/// is what [Container] uses), the `textDirection` parameter will be populated
/// based on the ambient [Directionality] (by way of the [RenderDecoratedBox]
/// renderer).
bool hitTest(Size size, Offset position, { TextDirection textDirection }) => true;
/// Returns a [BoxPainter] that will paint this decoration.
///
/// The `onChanged` argument configures [BoxPainter.onChanged]. It can be
/// omitted if there is no chance that the painter will change (for example,
/// if it is a [BoxDecoration] with definitely no [DecorationImage]).
@factory
BoxPainter createBoxPainter([ VoidCallback onChanged ]);
/// Returns a closed [Path] that describes the outer edge of this decoration.
Path getClipPath(Rect rect, TextDirection textDirection) => null;
}
/// A stateful class that can paint a particular [Decoration].
///
/// [BoxPainter] objects can cache resources so that they can be used
/// multiple times.
///
/// Some resources used by [BoxPainter] may load asynchronously. When this
/// happens, the [onChanged] callback will be invoked. To stop this callback
/// from being called after the painter has been discarded, call [dispose].
abstract class BoxPainter {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const BoxPainter([this.onChanged]);
/// Paints the [Decoration] for which this object was created on the
/// given canvas using the given configuration.
///
/// The [ImageConfiguration] object passed as the third argument must, at a
/// minimum, have a non-null [Size].
///
/// If this object caches resources for painting (e.g. [Paint] objects), the
/// cache may be flushed when [paint] is called with a new configuration. For
/// this reason, it may be more efficient to call
/// [Decoration.createBoxPainter] for each different rectangle that is being
/// painted in a particular frame.
///
/// For example, if a decoration's owner wants to paint a particular
/// decoration once for its whole size, and once just in the bottom
/// right, it might get two [BoxPainter] instances, one for each.
/// However, when its size changes, it could continue using those
/// same instances, since the previous resources would no longer be
/// relevant and thus losing them would not be an issue.
///
/// Implementations should paint their decorations on the canvas in a
/// rectangle whose top left corner is at the given `offset` and whose size is
/// given by `configuration.size`.
///
/// When a [Decoration] is painted in a [Container] or [DecoratedBox] (which
/// is what [Container] uses), the [ImageConfiguration.textDirection] property
/// will be populated based on the ambient [Directionality].
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration);
/// Callback that is invoked if an asynchronously-loading resource used by the
/// decoration finishes loading. For example, an image. When this is invoked,
/// the [paint] method should be called again.
///
/// Resources might not start to load until after [paint] has been called,
/// because they might depend on the configuration.
final VoidCallback onChanged;
/// Discard any resources being held by the object.
///
/// The [onChanged] callback will not be invoked after this method has been
/// called.
@mustCallSuper
void dispose() { }
}