1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
---|---|
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | /// @docImport 'package:flutter/material.dart'; |
6 | library; |
7 | |
8 | import 'dart:math' as math; |
9 | |
10 | import 'package:flutter/foundation.dart'; |
11 | |
12 | import 'basic_types.dart'; |
13 | import 'border_radius.dart'; |
14 | import 'borders.dart'; |
15 | import 'box_border.dart'; |
16 | import 'box_shadow.dart'; |
17 | import 'colors.dart'; |
18 | import 'debug.dart'; |
19 | import 'decoration.dart'; |
20 | import 'decoration_image.dart'; |
21 | import 'edge_insets.dart'; |
22 | import 'gradient.dart'; |
23 | import 'image_provider.dart'; |
24 | |
25 | /// An immutable description of how to paint a box. |
26 | /// |
27 | /// The [BoxDecoration] class provides a variety of ways to draw a box. |
28 | /// |
29 | /// The box has a [border], a body, and may cast a [boxShadow]. |
30 | /// |
31 | /// The [shape] of the box can be a circle or a rectangle. If it is a rectangle, |
32 | /// then the [borderRadius] property controls the roundness of the corners. |
33 | /// |
34 | /// The body of the box is painted in layers. The bottom-most layer is the |
35 | /// [color], which fills the box. Above that is the [gradient], which also fills |
36 | /// the box. Finally there is the [image], the precise alignment of which is |
37 | /// controlled by the [DecorationImage] class. |
38 | /// |
39 | /// The [border] paints over the body; the [boxShadow], naturally, paints below it. |
40 | /// |
41 | /// {@tool snippet} |
42 | /// |
43 | /// The following applies a [BoxDecoration] to a [Container] widget to draw an |
44 | /// [image] of an owl with a thick black [border] and rounded corners. |
45 | /// |
46 | ///  |
47 | /// |
48 | /// ```dart |
49 | /// Container( |
50 | /// decoration: BoxDecoration( |
51 | /// color: const Color(0xff7c94b6), |
52 | /// image: const DecorationImage( |
53 | /// image: NetworkImage('https://flutter.github.io/assets-for-api-docs/assets/widgets/owl-2.jpg'), |
54 | /// fit: BoxFit.cover, |
55 | /// ), |
56 | /// border: Border.all( |
57 | /// width: 8, |
58 | /// ), |
59 | /// borderRadius: BorderRadius.circular(12), |
60 | /// ), |
61 | /// ) |
62 | /// ``` |
63 | /// {@end-tool} |
64 | /// |
65 | /// {@template flutter.painting.BoxDecoration.clip} |
66 | /// The [shape] or the [borderRadius] won't clip the children of the |
67 | /// decorated [Container]. If the clip is required, insert a clip widget |
68 | /// (e.g., [ClipRect], [ClipRRect], [ClipPath]) as the child of the [Container]. |
69 | /// Be aware that clipping may be costly in terms of performance. |
70 | /// {@endtemplate} |
71 | /// |
72 | /// See also: |
73 | /// |
74 | /// * [DecoratedBox] and [Container], widgets that can be configured with |
75 | /// [BoxDecoration] objects. |
76 | /// * [DecoratedSliver], a widget that can be configured with a [BoxDecoration] |
77 | /// that is converted to render with slivers. |
78 | /// * [CustomPaint], a widget that lets you draw arbitrary graphics. |
79 | /// * [Decoration], the base class which lets you define other decorations. |
80 | class BoxDecoration extends Decoration { |
81 | /// Creates a box decoration. |
82 | /// |
83 | /// * If [color] is null, this decoration does not paint a background color. |
84 | /// * If [image] is null, this decoration does not paint a background image. |
85 | /// * If [border] is null, this decoration does not paint a border. |
86 | /// * If [borderRadius] is null, this decoration uses more efficient background |
87 | /// painting commands. The [borderRadius] argument must be null if [shape] is |
88 | /// [BoxShape.circle]. |
89 | /// * If [boxShadow] is null, this decoration does not paint a shadow. |
90 | /// * If [gradient] is null, this decoration does not paint gradients. |
91 | /// * If [backgroundBlendMode] is null, this decoration paints with [BlendMode.srcOver] |
92 | const BoxDecoration({ |
93 | this.color, |
94 | this.image, |
95 | this.border, |
96 | this.borderRadius, |
97 | this.boxShadow, |
98 | this.gradient, |
99 | this.backgroundBlendMode, |
100 | this.shape = BoxShape.rectangle, |
101 | }) : assert( |
102 | backgroundBlendMode == null || color != null || gradient != null, |
103 | "backgroundBlendMode applies to BoxDecoration's background color or " |
104 | 'gradient, but no color or gradient was provided.', |
105 | ); |
106 | |
107 | /// Creates a copy of this object but with the given fields replaced with the |
108 | /// new values. |
109 | BoxDecoration copyWith({ |
110 | Color? color, |
111 | DecorationImage? image, |
112 | BoxBorder? border, |
113 | BorderRadiusGeometry? borderRadius, |
114 | List<BoxShadow>? boxShadow, |
115 | Gradient? gradient, |
116 | BlendMode? backgroundBlendMode, |
117 | BoxShape? shape, |
118 | }) { |
119 | return BoxDecoration( |
120 | color: color ?? this.color, |
121 | image: image ?? this.image, |
122 | border: border ?? this.border, |
123 | borderRadius: borderRadius ?? this.borderRadius, |
124 | boxShadow: boxShadow ?? this.boxShadow, |
125 | gradient: gradient ?? this.gradient, |
126 | backgroundBlendMode: backgroundBlendMode ?? this.backgroundBlendMode, |
127 | shape: shape ?? this.shape, |
128 | ); |
129 | } |
130 | |
131 | @override |
132 | bool debugAssertIsValid() { |
133 | assert(shape != BoxShape.circle || borderRadius == null); // Can't have a border radius if you're a circle. |
134 | return super.debugAssertIsValid(); |
135 | } |
136 | |
137 | /// The color to fill in the background of the box. |
138 | /// |
139 | /// The color is filled into the [shape] of the box (e.g., either a rectangle, |
140 | /// potentially with a [borderRadius], or a circle). |
141 | /// |
142 | /// This is ignored if [gradient] is non-null. |
143 | /// |
144 | /// The [color] is drawn under the [image]. |
145 | final Color? color; |
146 | |
147 | /// An image to paint above the background [color] or [gradient]. |
148 | /// |
149 | /// If [shape] is [BoxShape.circle] then the image is clipped to the circle's |
150 | /// boundary; if [borderRadius] is non-null then the image is clipped to the |
151 | /// given radii. |
152 | final DecorationImage? image; |
153 | |
154 | /// A border to draw above the background [color], [gradient], or [image]. |
155 | /// |
156 | /// Follows the [shape] and [borderRadius]. |
157 | /// |
158 | /// Use [Border] objects to describe borders that do not depend on the reading |
159 | /// direction. |
160 | /// |
161 | /// Use [BoxBorder] objects to describe borders that should flip their left |
162 | /// and right edges based on whether the text is being read left-to-right or |
163 | /// right-to-left. |
164 | final BoxBorder? border; |
165 | |
166 | /// If non-null, the corners of this box are rounded by this [BorderRadius]. |
167 | /// |
168 | /// Applies only to boxes with rectangular shapes; ignored if [shape] is not |
169 | /// [BoxShape.rectangle]. |
170 | /// |
171 | /// {@macro flutter.painting.BoxDecoration.clip} |
172 | final BorderRadiusGeometry? borderRadius; |
173 | |
174 | /// A list of shadows cast by this box behind the box. |
175 | /// |
176 | /// The shadow follows the [shape] of the box. |
177 | /// |
178 | /// See also: |
179 | /// |
180 | /// * [kElevationToShadow], for some predefined shadows used in Material |
181 | /// Design. |
182 | /// * [PhysicalModel], a widget for showing shadows. |
183 | final List<BoxShadow>? boxShadow; |
184 | |
185 | /// A gradient to use when filling the box. |
186 | /// |
187 | /// If this is specified, [color] has no effect. |
188 | /// |
189 | /// The [gradient] is drawn under the [image]. |
190 | final Gradient? gradient; |
191 | |
192 | /// The blend mode applied to the [color] or [gradient] background of the box. |
193 | /// |
194 | /// If no [backgroundBlendMode] is provided then the default painting blend |
195 | /// mode is used. |
196 | /// |
197 | /// If no [color] or [gradient] is provided then the blend mode has no impact. |
198 | final BlendMode? backgroundBlendMode; |
199 | |
200 | /// The shape to fill the background [color], [gradient], and [image] into and |
201 | /// to cast as the [boxShadow]. |
202 | /// |
203 | /// If this is [BoxShape.circle] then [borderRadius] is ignored. |
204 | /// |
205 | /// The [shape] cannot be interpolated; animating between two [BoxDecoration]s |
206 | /// with different [shape]s will result in a discontinuity in the rendering. |
207 | /// To interpolate between two shapes, consider using [ShapeDecoration] and |
208 | /// different [ShapeBorder]s; in particular, [CircleBorder] instead of |
209 | /// [BoxShape.circle] and [RoundedRectangleBorder] instead of |
210 | /// [BoxShape.rectangle]. |
211 | /// |
212 | /// {@macro flutter.painting.BoxDecoration.clip} |
213 | final BoxShape shape; |
214 | |
215 | @override |
216 | EdgeInsetsGeometry get padding => border?.dimensions ?? EdgeInsets.zero; |
217 | |
218 | @override |
219 | Path getClipPath(Rect rect, TextDirection textDirection) { |
220 | switch (shape) { |
221 | case BoxShape.circle: |
222 | final Offset center = rect.center; |
223 | final double radius = rect.shortestSide / 2.0; |
224 | final Rect square = Rect.fromCircle(center: center, radius: radius); |
225 | return Path()..addOval(square); |
226 | case BoxShape.rectangle: |
227 | if (borderRadius != null) { |
228 | return Path()..addRRect(borderRadius!.resolve(textDirection).toRRect(rect)); |
229 | } |
230 | return Path()..addRect(rect); |
231 | } |
232 | } |
233 | |
234 | /// Returns a new box decoration that is scaled by the given factor. |
235 | BoxDecoration scale(double factor) { |
236 | return BoxDecoration( |
237 | color: Color.lerp(null, color, factor), |
238 | image: DecorationImage.lerp(null, image, factor), |
239 | border: BoxBorder.lerp(null, border, factor), |
240 | borderRadius: BorderRadiusGeometry.lerp(null, borderRadius, factor), |
241 | boxShadow: BoxShadow.lerpList(null, boxShadow, factor), |
242 | gradient: gradient?.scale(factor), |
243 | shape: shape, |
244 | ); |
245 | } |
246 | |
247 | @override |
248 | bool get isComplex => boxShadow != null; |
249 | |
250 | @override |
251 | BoxDecoration? lerpFrom(Decoration? a, double t) => switch (a) { |
252 | null => scale(t), |
253 | BoxDecoration() => BoxDecoration.lerp(a, this, t), |
254 | _ => super.lerpFrom(a, t) as BoxDecoration? |
255 | }; |
256 | |
257 | @override |
258 | BoxDecoration? lerpTo(Decoration? b, double t) => switch (b) { |
259 | null => scale(1.0 - t), |
260 | BoxDecoration() => BoxDecoration.lerp(this, b, t), |
261 | _ => super.lerpTo(b, t) as BoxDecoration? |
262 | }; |
263 | |
264 | /// Linearly interpolate between two box decorations. |
265 | /// |
266 | /// Interpolates each parameter of the box decoration separately. |
267 | /// |
268 | /// The [shape] is not interpolated. To interpolate the shape, consider using |
269 | /// a [ShapeDecoration] with different border shapes. |
270 | /// |
271 | /// If both values are null, this returns null. Otherwise, it returns a |
272 | /// non-null value. If one of the values is null, then the result is obtained |
273 | /// by applying [scale] to the other value. If neither value is null and `t == |
274 | /// 0.0`, then `a` is returned unmodified; if `t == 1.0` then `b` is returned |
275 | /// unmodified. Otherwise, the values are computed by interpolating the |
276 | /// properties appropriately. |
277 | /// |
278 | /// {@macro dart.ui.shadow.lerp} |
279 | /// |
280 | /// See also: |
281 | /// |
282 | /// * [Decoration.lerp], which can interpolate between any two types of |
283 | /// [Decoration]s, not just [BoxDecoration]s. |
284 | /// * [lerpFrom] and [lerpTo], which are used to implement [Decoration.lerp] |
285 | /// and which use [BoxDecoration.lerp] when interpolating two |
286 | /// [BoxDecoration]s or a [BoxDecoration] to or from null. |
287 | static BoxDecoration? lerp(BoxDecoration? a, BoxDecoration? b, double t) { |
288 | if (identical(a, b)) { |
289 | return a; |
290 | } |
291 | if (a == null) { |
292 | return b!.scale(t); |
293 | } |
294 | if (b == null) { |
295 | return a.scale(1.0 - t); |
296 | } |
297 | if (t == 0.0) { |
298 | return a; |
299 | } |
300 | if (t == 1.0) { |
301 | return b; |
302 | } |
303 | return BoxDecoration( |
304 | color: Color.lerp(a.color, b.color, t), |
305 | image: DecorationImage.lerp(a.image, b.image, t), |
306 | border: BoxBorder.lerp(a.border, b.border, t), |
307 | borderRadius: BorderRadiusGeometry.lerp(a.borderRadius, b.borderRadius, t), |
308 | boxShadow: BoxShadow.lerpList(a.boxShadow, b.boxShadow, t), |
309 | gradient: Gradient.lerp(a.gradient, b.gradient, t), |
310 | shape: t < 0.5 ? a.shape : b.shape, |
311 | ); |
312 | } |
313 | |
314 | @override |
315 | bool operator ==(Object other) { |
316 | if (identical(this, other)) { |
317 | return true; |
318 | } |
319 | if (other.runtimeType != runtimeType) { |
320 | return false; |
321 | } |
322 | return other is BoxDecoration |
323 | && other.color == color |
324 | && other.image == image |
325 | && other.border == border |
326 | && other.borderRadius == borderRadius |
327 | && listEquals<BoxShadow>(other.boxShadow, boxShadow) |
328 | && other.gradient == gradient |
329 | && other.backgroundBlendMode == backgroundBlendMode |
330 | && other.shape == shape; |
331 | } |
332 | |
333 | @override |
334 | int get hashCode => Object.hash( |
335 | color, |
336 | image, |
337 | border, |
338 | borderRadius, |
339 | boxShadow == null ? null : Object.hashAll(boxShadow!), |
340 | gradient, |
341 | backgroundBlendMode, |
342 | shape, |
343 | ); |
344 | |
345 | @override |
346 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
347 | super.debugFillProperties(properties); |
348 | properties |
349 | ..defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.whitespace |
350 | ..emptyBodyDescription = '<no decorations specified>'; |
351 | |
352 | properties.add(ColorProperty('color', color, defaultValue: null)); |
353 | properties.add(DiagnosticsProperty<DecorationImage>('image', image, defaultValue: null)); |
354 | properties.add(DiagnosticsProperty<BoxBorder>('border', border, defaultValue: null)); |
355 | properties.add(DiagnosticsProperty<BorderRadiusGeometry>('borderRadius', borderRadius, defaultValue: null)); |
356 | properties.add(IterableProperty<BoxShadow>('boxShadow', boxShadow, defaultValue: null, style: DiagnosticsTreeStyle.whitespace)); |
357 | properties.add(DiagnosticsProperty<Gradient>('gradient', gradient, defaultValue: null)); |
358 | properties.add(EnumProperty<BoxShape>('shape', shape, defaultValue: BoxShape.rectangle)); |
359 | } |
360 | |
361 | @override |
362 | bool hitTest(Size size, Offset position, { TextDirection? textDirection }) { |
363 | assert((Offset.zero & size).contains(position)); |
364 | switch (shape) { |
365 | case BoxShape.rectangle: |
366 | if (borderRadius != null) { |
367 | final RRect bounds = borderRadius!.resolve(textDirection).toRRect(Offset.zero & size); |
368 | return bounds.contains(position); |
369 | } |
370 | return true; |
371 | case BoxShape.circle: |
372 | // Circles are inscribed into our smallest dimension. |
373 | final Offset center = size.center(Offset.zero); |
374 | final double distance = (position - center).distance; |
375 | return distance <= math.min(size.width, size.height) / 2.0; |
376 | } |
377 | } |
378 | |
379 | @override |
380 | BoxPainter createBoxPainter([ VoidCallback? onChanged ]) { |
381 | assert(onChanged != null || image == null); |
382 | return _BoxDecorationPainter(this, onChanged); |
383 | } |
384 | } |
385 | |
386 | /// An object that paints a [BoxDecoration] into a canvas. |
387 | class _BoxDecorationPainter extends BoxPainter { |
388 | _BoxDecorationPainter(this._decoration, super.onChanged); |
389 | |
390 | final BoxDecoration _decoration; |
391 | |
392 | Paint? _cachedBackgroundPaint; |
393 | Rect? _rectForCachedBackgroundPaint; |
394 | Paint _getBackgroundPaint(Rect rect, TextDirection? textDirection) { |
395 | assert(_decoration.gradient != null || _rectForCachedBackgroundPaint == null); |
396 | |
397 | if (_cachedBackgroundPaint == null || |
398 | (_decoration.gradient != null && _rectForCachedBackgroundPaint != rect)) { |
399 | final Paint paint = Paint(); |
400 | if (_decoration.backgroundBlendMode != null) { |
401 | paint.blendMode = _decoration.backgroundBlendMode!; |
402 | } |
403 | if (_decoration.color != null) { |
404 | paint.color = _decoration.color!; |
405 | } |
406 | if (_decoration.gradient != null) { |
407 | paint.shader = _decoration.gradient!.createShader(rect, textDirection: textDirection); |
408 | _rectForCachedBackgroundPaint = rect; |
409 | } |
410 | _cachedBackgroundPaint = paint; |
411 | } |
412 | |
413 | return _cachedBackgroundPaint!; |
414 | } |
415 | |
416 | void _paintBox(Canvas canvas, Rect rect, Paint paint, TextDirection? textDirection) { |
417 | switch (_decoration.shape) { |
418 | case BoxShape.circle: |
419 | assert(_decoration.borderRadius == null); |
420 | final Offset center = rect.center; |
421 | final double radius = rect.shortestSide / 2.0; |
422 | canvas.drawCircle(center, radius, paint); |
423 | case BoxShape.rectangle: |
424 | if (_decoration.borderRadius == null || _decoration.borderRadius == BorderRadius.zero) { |
425 | canvas.drawRect(rect, paint); |
426 | } else { |
427 | canvas.drawRRect(_decoration.borderRadius!.resolve(textDirection).toRRect(rect), paint); |
428 | } |
429 | } |
430 | } |
431 | |
432 | void _paintShadows(Canvas canvas, Rect rect, TextDirection? textDirection) { |
433 | if (_decoration.boxShadow == null) { |
434 | return; |
435 | } |
436 | for (final BoxShadow boxShadow in _decoration.boxShadow!) { |
437 | final Paint paint = boxShadow.toPaint(); |
438 | final Rect bounds = rect.shift(boxShadow.offset).inflate(boxShadow.spreadRadius); |
439 | assert(() { |
440 | if (debugDisableShadows && boxShadow.blurStyle == BlurStyle.outer) { |
441 | canvas.save(); |
442 | canvas.clipRect(bounds); |
443 | } |
444 | return true; |
445 | }()); |
446 | _paintBox(canvas, bounds, paint, textDirection); |
447 | assert(() { |
448 | if (debugDisableShadows && boxShadow.blurStyle == BlurStyle.outer) { |
449 | canvas.restore(); |
450 | } |
451 | return true; |
452 | }()); |
453 | } |
454 | } |
455 | |
456 | void _paintBackgroundColor(Canvas canvas, Rect rect, TextDirection? textDirection) { |
457 | if (_decoration.color != null || _decoration.gradient != null) { |
458 | // When border is filled, the rect is reduced to avoid anti-aliasing |
459 | // rounding error leaking the background color around the clipped shape. |
460 | final Rect adjustedRect = _adjustedRectOnOutlinedBorder(rect, textDirection); |
461 | _paintBox(canvas, adjustedRect, _getBackgroundPaint(rect, textDirection), textDirection); |
462 | } |
463 | } |
464 | |
465 | double _calculateAdjustedSide(BorderSide side) { |
466 | if (side.color.alpha == 255 && side.style == BorderStyle.solid) { |
467 | return side.strokeInset; |
468 | } |
469 | return 0; |
470 | } |
471 | |
472 | Rect _adjustedRectOnOutlinedBorder(Rect rect, TextDirection? textDirection) { |
473 | if (_decoration.border == null) { |
474 | return rect; |
475 | } |
476 | |
477 | if (_decoration.border is Border) { |
478 | final Border border = _decoration.border! as Border; |
479 | |
480 | final EdgeInsets insets = EdgeInsets.fromLTRB( |
481 | _calculateAdjustedSide(border.left), |
482 | _calculateAdjustedSide(border.top), |
483 | _calculateAdjustedSide(border.right), |
484 | _calculateAdjustedSide(border.bottom), |
485 | ) / 2; |
486 | |
487 | return Rect.fromLTRB( |
488 | rect.left + insets.left, |
489 | rect.top + insets.top, |
490 | rect.right - insets.right, |
491 | rect.bottom - insets.bottom, |
492 | ); |
493 | } else if (_decoration.border is BorderDirectional && textDirection != null) { |
494 | final BorderDirectional border = _decoration.border! as BorderDirectional; |
495 | final BorderSide leftSide = textDirection == TextDirection.rtl ? border.end : border.start; |
496 | final BorderSide rightSide = textDirection == TextDirection.rtl ? border.start : border.end; |
497 | |
498 | final EdgeInsets insets = EdgeInsets.fromLTRB( |
499 | _calculateAdjustedSide(leftSide), |
500 | _calculateAdjustedSide(border.top), |
501 | _calculateAdjustedSide(rightSide), |
502 | _calculateAdjustedSide(border.bottom), |
503 | ) / 2; |
504 | |
505 | return Rect.fromLTRB( |
506 | rect.left + insets.left, |
507 | rect.top + insets.top, |
508 | rect.right - insets.right, |
509 | rect.bottom - insets.bottom, |
510 | ); |
511 | } |
512 | return rect; |
513 | } |
514 | |
515 | DecorationImagePainter? _imagePainter; |
516 | void _paintBackgroundImage(Canvas canvas, Rect rect, ImageConfiguration configuration) { |
517 | if (_decoration.image == null) { |
518 | return; |
519 | } |
520 | _imagePainter ??= _decoration.image!.createPainter(onChanged!); |
521 | Path? clipPath; |
522 | switch (_decoration.shape) { |
523 | case BoxShape.circle: |
524 | assert(_decoration.borderRadius == null); |
525 | final Offset center = rect.center; |
526 | final double radius = rect.shortestSide / 2.0; |
527 | final Rect square = Rect.fromCircle(center: center, radius: radius); |
528 | clipPath = Path()..addOval(square); |
529 | case BoxShape.rectangle: |
530 | if (_decoration.borderRadius != null) { |
531 | clipPath = Path()..addRRect(_decoration.borderRadius!.resolve(configuration.textDirection).toRRect(rect)); |
532 | } |
533 | } |
534 | _imagePainter!.paint(canvas, rect, clipPath, configuration); |
535 | } |
536 | |
537 | @override |
538 | void dispose() { |
539 | _imagePainter?.dispose(); |
540 | super.dispose(); |
541 | } |
542 | |
543 | /// Paint the box decoration into the given location on the given canvas. |
544 | @override |
545 | void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) { |
546 | assert(configuration.size != null); |
547 | final Rect rect = offset & configuration.size!; |
548 | final TextDirection? textDirection = configuration.textDirection; |
549 | _paintShadows(canvas, rect, textDirection); |
550 | _paintBackgroundColor(canvas, rect, textDirection); |
551 | _paintBackgroundImage(canvas, rect, configuration); |
552 | _decoration.border?.paint( |
553 | canvas, |
554 | rect, |
555 | shape: _decoration.shape, |
556 | borderRadius: _decoration.borderRadius?.resolve(textDirection), |
557 | textDirection: configuration.textDirection, |
558 | ); |
559 | } |
560 | |
561 | @override |
562 | String toString() { |
563 | return 'BoxPainter for$_decoration '; |
564 | } |
565 | } |
566 |
Definitions
- BoxDecoration
- BoxDecoration
- copyWith
- debugAssertIsValid
- padding
- getClipPath
- scale
- isComplex
- lerpFrom
- lerpTo
- lerp
- ==
- hashCode
- debugFillProperties
- hitTest
- createBoxPainter
- _BoxDecorationPainter
- _BoxDecorationPainter
- _getBackgroundPaint
- _paintBox
- _paintShadows
- _paintBackgroundColor
- _calculateAdjustedSide
- _adjustedRectOnOutlinedBorder
- _paintBackgroundImage
- dispose
- paint
Learn more about Flutter for embedded and desktop on industrialflutter.com