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
5import 'dart:ui' as ui show ViewPadding, lerpDouble;
6
7import 'package:flutter/foundation.dart';
8
9import 'basic_types.dart';
10
11/// Base class for [EdgeInsets] that allows for text-direction aware
12/// resolution.
13///
14/// A property or argument of this type accepts classes created either with [
15/// EdgeInsets.fromLTRB] and its variants, or [
16/// EdgeInsetsDirectional.fromSTEB] and its variants.
17///
18/// To convert an [EdgeInsetsGeometry] object of indeterminate type into a
19/// [EdgeInsets] object, call the [resolve] method.
20///
21/// See also:
22///
23/// * [Padding], a widget that describes margins using [EdgeInsetsGeometry].
24@immutable
25abstract class EdgeInsetsGeometry {
26 /// Abstract const constructor. This constructor enables subclasses to provide
27 /// const constructors so that they can be used in const expressions.
28 const EdgeInsetsGeometry();
29
30 double get _bottom;
31 double get _end;
32 double get _left;
33 double get _right;
34 double get _start;
35 double get _top;
36
37 /// An [EdgeInsetsGeometry] with infinite offsets in each direction.
38 ///
39 /// Can be used as an infinite upper bound for [clamp].
40 static const EdgeInsetsGeometry infinity = _MixedEdgeInsets.fromLRSETB(
41 double.infinity,
42 double.infinity,
43 double.infinity,
44 double.infinity,
45 double.infinity,
46 double.infinity,
47 );
48
49 /// Whether every dimension is non-negative.
50 bool get isNonNegative {
51 return _left >= 0.0
52 && _right >= 0.0
53 && _start >= 0.0
54 && _end >= 0.0
55 && _top >= 0.0
56 && _bottom >= 0.0;
57 }
58
59 /// The total offset in the horizontal direction.
60 double get horizontal => _left + _right + _start + _end;
61
62 /// The total offset in the vertical direction.
63 double get vertical => _top + _bottom;
64
65 /// The total offset in the given direction.
66 double along(Axis axis) {
67 switch (axis) {
68 case Axis.horizontal:
69 return horizontal;
70 case Axis.vertical:
71 return vertical;
72 }
73 }
74
75 /// The size that this [EdgeInsets] would occupy with an empty interior.
76 Size get collapsedSize => Size(horizontal, vertical);
77
78 /// An [EdgeInsetsGeometry] with top and bottom, left and right, and start and end flipped.
79 EdgeInsetsGeometry get flipped => _MixedEdgeInsets.fromLRSETB(_right, _left, _end, _start, _bottom, _top);
80
81 /// Returns a new size that is bigger than the given size by the amount of
82 /// inset in the horizontal and vertical directions.
83 ///
84 /// See also:
85 ///
86 /// * [EdgeInsets.inflateRect], to inflate a [Rect] rather than a [Size] (for
87 /// [EdgeInsetsDirectional], requires first calling [resolve] to establish
88 /// how the start and end map to the left or right).
89 /// * [deflateSize], to deflate a [Size] rather than inflating it.
90 Size inflateSize(Size size) {
91 return Size(size.width + horizontal, size.height + vertical);
92 }
93
94 /// Returns a new size that is smaller than the given size by the amount of
95 /// inset in the horizontal and vertical directions.
96 ///
97 /// If the argument is smaller than [collapsedSize], then the resulting size
98 /// will have negative dimensions.
99 ///
100 /// See also:
101 ///
102 /// * [EdgeInsets.deflateRect], to deflate a [Rect] rather than a [Size]. (for
103 /// [EdgeInsetsDirectional], requires first calling [resolve] to establish
104 /// how the start and end map to the left or right).
105 /// * [inflateSize], to inflate a [Size] rather than deflating it.
106 Size deflateSize(Size size) {
107 return Size(size.width - horizontal, size.height - vertical);
108 }
109
110 /// Returns the difference between two [EdgeInsetsGeometry] objects.
111 ///
112 /// If you know you are applying this to two [EdgeInsets] or two
113 /// [EdgeInsetsDirectional] objects, consider using the binary infix `-`
114 /// operator instead, which always returns an object of the same type as the
115 /// operands, and is typed accordingly.
116 ///
117 /// If [subtract] is applied to two objects of the same type ([EdgeInsets] or
118 /// [EdgeInsetsDirectional]), an object of that type will be returned (though
119 /// this is not reflected in the type system). Otherwise, an object
120 /// representing a combination of both is returned. That object can be turned
121 /// into a concrete [EdgeInsets] using [resolve].
122 ///
123 /// This method returns the same result as [add] applied to the result of
124 /// negating the argument (using the prefix unary `-` operator or multiplying
125 /// the argument by -1.0 using the `*` operator).
126 EdgeInsetsGeometry subtract(EdgeInsetsGeometry other) {
127 return _MixedEdgeInsets.fromLRSETB(
128 _left - other._left,
129 _right - other._right,
130 _start - other._start,
131 _end - other._end,
132 _top - other._top,
133 _bottom - other._bottom,
134 );
135 }
136
137 /// Returns the sum of two [EdgeInsetsGeometry] objects.
138 ///
139 /// If you know you are adding two [EdgeInsets] or two [EdgeInsetsDirectional]
140 /// objects, consider using the `+` operator instead, which always returns an
141 /// object of the same type as the operands, and is typed accordingly.
142 ///
143 /// If [add] is applied to two objects of the same type ([EdgeInsets] or
144 /// [EdgeInsetsDirectional]), an object of that type will be returned (though
145 /// this is not reflected in the type system). Otherwise, an object
146 /// representing a combination of both is returned. That object can be turned
147 /// into a concrete [EdgeInsets] using [resolve].
148 EdgeInsetsGeometry add(EdgeInsetsGeometry other) {
149 return _MixedEdgeInsets.fromLRSETB(
150 _left + other._left,
151 _right + other._right,
152 _start + other._start,
153 _end + other._end,
154 _top + other._top,
155 _bottom + other._bottom,
156 );
157 }
158
159 /// Returns a new [EdgeInsetsGeometry] object with all values greater than
160 /// or equal to `min`, and less than or equal to `max`.
161 EdgeInsetsGeometry clamp(EdgeInsetsGeometry min, EdgeInsetsGeometry max) {
162 return _MixedEdgeInsets.fromLRSETB(
163 clampDouble(_left, min._left, max._left),
164 clampDouble(_right, min._right, max._right),
165 clampDouble(_start, min._start, max._start),
166 clampDouble(_end, min._end, max._end),
167 clampDouble(_top, min._top, max._top),
168 clampDouble(_bottom, min._bottom, max._bottom),
169 );
170 }
171
172 /// Returns the [EdgeInsetsGeometry] object with each dimension negated.
173 ///
174 /// This is the same as multiplying the object by -1.0.
175 ///
176 /// This operator returns an object of the same type as the operand.
177 EdgeInsetsGeometry operator -();
178
179 /// Scales the [EdgeInsetsGeometry] object in each dimension by the given factor.
180 ///
181 /// This operator returns an object of the same type as the operand.
182 EdgeInsetsGeometry operator *(double other);
183
184 /// Divides the [EdgeInsetsGeometry] object in each dimension by the given factor.
185 ///
186 /// This operator returns an object of the same type as the operand.
187 EdgeInsetsGeometry operator /(double other);
188
189 /// Integer divides the [EdgeInsetsGeometry] object in each dimension by the given factor.
190 ///
191 /// This operator returns an object of the same type as the operand.
192 ///
193 /// This operator may have unexpected results when applied to a mixture of
194 /// [EdgeInsets] and [EdgeInsetsDirectional] objects.
195 EdgeInsetsGeometry operator ~/(double other);
196
197 /// Computes the remainder in each dimension by the given factor.
198 ///
199 /// This operator returns an object of the same type as the operand.
200 ///
201 /// This operator may have unexpected results when applied to a mixture of
202 /// [EdgeInsets] and [EdgeInsetsDirectional] objects.
203 EdgeInsetsGeometry operator %(double other);
204
205 /// Linearly interpolate between two [EdgeInsetsGeometry] objects.
206 ///
207 /// If either is null, this function interpolates from [EdgeInsets.zero], and
208 /// the result is an object of the same type as the non-null argument.
209 ///
210 /// If [lerp] is applied to two objects of the same type ([EdgeInsets] or
211 /// [EdgeInsetsDirectional]), an object of that type will be returned (though
212 /// this is not reflected in the type system). Otherwise, an object
213 /// representing a combination of both is returned. That object can be turned
214 /// into a concrete [EdgeInsets] using [resolve].
215 ///
216 /// {@macro dart.ui.shadow.lerp}
217 static EdgeInsetsGeometry? lerp(EdgeInsetsGeometry? a, EdgeInsetsGeometry? b, double t) {
218 if (identical(a, b)) {
219 return a;
220 }
221 if (a == null) {
222 return b! * t;
223 }
224 if (b == null) {
225 return a * (1.0 - t);
226 }
227 if (a is EdgeInsets && b is EdgeInsets) {
228 return EdgeInsets.lerp(a, b, t);
229 }
230 if (a is EdgeInsetsDirectional && b is EdgeInsetsDirectional) {
231 return EdgeInsetsDirectional.lerp(a, b, t);
232 }
233 return _MixedEdgeInsets.fromLRSETB(
234 ui.lerpDouble(a._left, b._left, t)!,
235 ui.lerpDouble(a._right, b._right, t)!,
236 ui.lerpDouble(a._start, b._start, t)!,
237 ui.lerpDouble(a._end, b._end, t)!,
238 ui.lerpDouble(a._top, b._top, t)!,
239 ui.lerpDouble(a._bottom, b._bottom, t)!,
240 );
241 }
242
243 /// Convert this instance into an [EdgeInsets], which uses literal coordinates
244 /// (i.e. the `left` coordinate being explicitly a distance from the left, and
245 /// the `right` coordinate being explicitly a distance from the right).
246 ///
247 /// See also:
248 ///
249 /// * [EdgeInsets], for which this is a no-op (returns itself).
250 /// * [EdgeInsetsDirectional], which flips the horizontal direction
251 /// based on the `direction` argument.
252 EdgeInsets resolve(TextDirection? direction);
253
254 @override
255 String toString() {
256 if (_start == 0.0 && _end == 0.0) {
257 if (_left == 0.0 && _right == 0.0 && _top == 0.0 && _bottom == 0.0) {
258 return 'EdgeInsets.zero';
259 }
260 if (_left == _right && _right == _top && _top == _bottom) {
261 return 'EdgeInsets.all(${_left.toStringAsFixed(1)})';
262 }
263 return 'EdgeInsets(${_left.toStringAsFixed(1)}, '
264 '${_top.toStringAsFixed(1)}, '
265 '${_right.toStringAsFixed(1)}, '
266 '${_bottom.toStringAsFixed(1)})';
267 }
268 if (_left == 0.0 && _right == 0.0) {
269 return 'EdgeInsetsDirectional(${_start.toStringAsFixed(1)}, '
270 '${_top.toStringAsFixed(1)}, '
271 '${_end.toStringAsFixed(1)}, '
272 '${_bottom.toStringAsFixed(1)})';
273 }
274 return 'EdgeInsets(${_left.toStringAsFixed(1)}, '
275 '${_top.toStringAsFixed(1)}, '
276 '${_right.toStringAsFixed(1)}, '
277 '${_bottom.toStringAsFixed(1)})'
278 ' + '
279 'EdgeInsetsDirectional(${_start.toStringAsFixed(1)}, '
280 '0.0, '
281 '${_end.toStringAsFixed(1)}, '
282 '0.0)';
283 }
284
285 @override
286 bool operator ==(Object other) {
287 return other is EdgeInsetsGeometry
288 && other._left == _left
289 && other._right == _right
290 && other._start == _start
291 && other._end == _end
292 && other._top == _top
293 && other._bottom == _bottom;
294 }
295
296 @override
297 int get hashCode => Object.hash(_left, _right, _start, _end, _top, _bottom);
298}
299
300/// An immutable set of offsets in each of the four cardinal directions.
301///
302/// Typically used for an offset from each of the four sides of a box. For
303/// example, the padding inside a box can be represented using this class.
304///
305/// The [EdgeInsets] class specifies offsets in terms of visual edges, left,
306/// top, right, and bottom. These values are not affected by the
307/// [TextDirection]. To support both left-to-right and right-to-left layouts,
308/// consider using [EdgeInsetsDirectional], which is expressed in terms of
309/// _start_, top, _end_, and bottom, where start and end are resolved in terms
310/// of a [TextDirection] (typically obtained from the ambient [Directionality]).
311///
312/// {@tool snippet}
313///
314/// Here are some examples of how to create [EdgeInsets] instances:
315///
316/// Typical eight-pixel margin on all sides:
317///
318/// ```dart
319/// const EdgeInsets.all(8.0)
320/// ```
321/// {@end-tool}
322/// {@tool snippet}
323///
324/// Eight pixel margin above and below, no horizontal margins:
325///
326/// ```dart
327/// const EdgeInsets.symmetric(vertical: 8.0)
328/// ```
329/// {@end-tool}
330/// {@tool snippet}
331///
332/// Left margin indent of 40 pixels:
333///
334/// ```dart
335/// const EdgeInsets.only(left: 40.0)
336/// ```
337/// {@end-tool}
338///
339/// See also:
340///
341/// * [Padding], a widget that accepts [EdgeInsets] to describe its margins.
342/// * [EdgeInsetsDirectional], which (for properties and arguments that accept
343/// the type [EdgeInsetsGeometry]) allows the horizontal insets to be
344/// specified in a [TextDirection]-aware manner.
345class EdgeInsets extends EdgeInsetsGeometry {
346 /// Creates insets from offsets from the left, top, right, and bottom.
347 const EdgeInsets.fromLTRB(this.left, this.top, this.right, this.bottom);
348
349 /// Creates insets where all the offsets are `value`.
350 ///
351 /// {@tool snippet}
352 ///
353 /// Typical eight-pixel margin on all sides:
354 ///
355 /// ```dart
356 /// const EdgeInsets.all(8.0)
357 /// ```
358 /// {@end-tool}
359 const EdgeInsets.all(double value)
360 : left = value,
361 top = value,
362 right = value,
363 bottom = value;
364
365 /// Creates insets with only the given values non-zero.
366 ///
367 /// {@tool snippet}
368 ///
369 /// Left margin indent of 40 pixels:
370 ///
371 /// ```dart
372 /// const EdgeInsets.only(left: 40.0)
373 /// ```
374 /// {@end-tool}
375 const EdgeInsets.only({
376 this.left = 0.0,
377 this.top = 0.0,
378 this.right = 0.0,
379 this.bottom = 0.0,
380 });
381
382 /// Creates insets with symmetrical vertical and horizontal offsets.
383 ///
384 /// {@tool snippet}
385 ///
386 /// Eight pixel margin above and below, no horizontal margins:
387 ///
388 /// ```dart
389 /// const EdgeInsets.symmetric(vertical: 8.0)
390 /// ```
391 /// {@end-tool}
392 const EdgeInsets.symmetric({
393 double vertical = 0.0,
394 double horizontal = 0.0,
395 }) : left = horizontal,
396 top = vertical,
397 right = horizontal,
398 bottom = vertical;
399
400 /// Creates insets that match the given view padding.
401 ///
402 /// If you need the current system padding or view insets in the context of a
403 /// widget, consider using [MediaQuery.paddingOf] to obtain these values rather than
404 /// using the value from a [FlutterView] directly, so that you get notified of
405 /// changes.
406 EdgeInsets.fromViewPadding(ui.ViewPadding padding, double devicePixelRatio)
407 : left = padding.left / devicePixelRatio,
408 top = padding.top / devicePixelRatio,
409 right = padding.right / devicePixelRatio,
410 bottom = padding.bottom / devicePixelRatio;
411
412 /// Deprecated. Will be removed in a future version of Flutter.
413 ///
414 /// Use [EdgeInsets.fromViewPadding] instead.
415 @Deprecated(
416 'Use EdgeInsets.fromViewPadding instead. '
417 'This feature was deprecated after v3.8.0-14.0.pre.',
418 )
419 factory EdgeInsets.fromWindowPadding(ui.ViewPadding padding, double devicePixelRatio) = EdgeInsets.fromViewPadding;
420
421 /// An [EdgeInsets] with zero offsets in each direction.
422 static const EdgeInsets zero = EdgeInsets.only();
423
424 /// The offset from the left.
425 final double left;
426
427 @override
428 double get _left => left;
429
430 /// The offset from the top.
431 final double top;
432
433 @override
434 double get _top => top;
435
436 /// The offset from the right.
437 final double right;
438
439 @override
440 double get _right => right;
441
442 /// The offset from the bottom.
443 final double bottom;
444
445 @override
446 double get _bottom => bottom;
447
448 @override
449 double get _start => 0.0;
450
451 @override
452 double get _end => 0.0;
453
454 /// An Offset describing the vector from the top left of a rectangle to the
455 /// top left of that rectangle inset by this object.
456 Offset get topLeft => Offset(left, top);
457
458 /// An Offset describing the vector from the top right of a rectangle to the
459 /// top right of that rectangle inset by this object.
460 Offset get topRight => Offset(-right, top);
461
462 /// An Offset describing the vector from the bottom left of a rectangle to the
463 /// bottom left of that rectangle inset by this object.
464 Offset get bottomLeft => Offset(left, -bottom);
465
466 /// An Offset describing the vector from the bottom right of a rectangle to the
467 /// bottom right of that rectangle inset by this object.
468 Offset get bottomRight => Offset(-right, -bottom);
469
470 /// An [EdgeInsets] with top and bottom as well as left and right flipped.
471 @override
472 EdgeInsets get flipped => EdgeInsets.fromLTRB(right, bottom, left, top);
473
474 /// Returns a new rect that is bigger than the given rect in each direction by
475 /// the amount of inset in each direction. Specifically, the left edge of the
476 /// rect is moved left by [left], the top edge of the rect is moved up by
477 /// [top], the right edge of the rect is moved right by [right], and the
478 /// bottom edge of the rect is moved down by [bottom].
479 ///
480 /// See also:
481 ///
482 /// * [inflateSize], to inflate a [Size] rather than a [Rect].
483 /// * [deflateRect], to deflate a [Rect] rather than inflating it.
484 Rect inflateRect(Rect rect) {
485 return Rect.fromLTRB(rect.left - left, rect.top - top, rect.right + right, rect.bottom + bottom);
486 }
487
488 /// Returns a new rect that is smaller than the given rect in each direction by
489 /// the amount of inset in each direction. Specifically, the left edge of the
490 /// rect is moved right by [left], the top edge of the rect is moved down by
491 /// [top], the right edge of the rect is moved left by [right], and the
492 /// bottom edge of the rect is moved up by [bottom].
493 ///
494 /// If the argument's [Rect.size] is smaller than [collapsedSize], then the
495 /// resulting rectangle will have negative dimensions.
496 ///
497 /// See also:
498 ///
499 /// * [deflateSize], to deflate a [Size] rather than a [Rect].
500 /// * [inflateRect], to inflate a [Rect] rather than deflating it.
501 Rect deflateRect(Rect rect) {
502 return Rect.fromLTRB(rect.left + left, rect.top + top, rect.right - right, rect.bottom - bottom);
503 }
504
505 @override
506 EdgeInsetsGeometry subtract(EdgeInsetsGeometry other) {
507 if (other is EdgeInsets) {
508 return this - other;
509 }
510 return super.subtract(other);
511 }
512
513 @override
514 EdgeInsetsGeometry add(EdgeInsetsGeometry other) {
515 if (other is EdgeInsets) {
516 return this + other;
517 }
518 return super.add(other);
519 }
520
521 @override
522 EdgeInsetsGeometry clamp(EdgeInsetsGeometry min, EdgeInsetsGeometry max) {
523 return EdgeInsets.fromLTRB(
524 clampDouble(_left, min._left, max._left),
525 clampDouble(_top, min._top, max._top),
526 clampDouble(_right, min._right, max._right),
527 clampDouble(_bottom, min._bottom, max._bottom),
528 );
529 }
530
531 /// Returns the difference between two [EdgeInsets].
532 EdgeInsets operator -(EdgeInsets other) {
533 return EdgeInsets.fromLTRB(
534 left - other.left,
535 top - other.top,
536 right - other.right,
537 bottom - other.bottom,
538 );
539 }
540
541 /// Returns the sum of two [EdgeInsets].
542 EdgeInsets operator +(EdgeInsets other) {
543 return EdgeInsets.fromLTRB(
544 left + other.left,
545 top + other.top,
546 right + other.right,
547 bottom + other.bottom,
548 );
549 }
550
551 /// Returns the [EdgeInsets] object with each dimension negated.
552 ///
553 /// This is the same as multiplying the object by -1.0.
554 @override
555 EdgeInsets operator -() {
556 return EdgeInsets.fromLTRB(
557 -left,
558 -top,
559 -right,
560 -bottom,
561 );
562 }
563
564 /// Scales the [EdgeInsets] in each dimension by the given factor.
565 @override
566 EdgeInsets operator *(double other) {
567 return EdgeInsets.fromLTRB(
568 left * other,
569 top * other,
570 right * other,
571 bottom * other,
572 );
573 }
574
575 /// Divides the [EdgeInsets] in each dimension by the given factor.
576 @override
577 EdgeInsets operator /(double other) {
578 return EdgeInsets.fromLTRB(
579 left / other,
580 top / other,
581 right / other,
582 bottom / other,
583 );
584 }
585
586 /// Integer divides the [EdgeInsets] in each dimension by the given factor.
587 @override
588 EdgeInsets operator ~/(double other) {
589 return EdgeInsets.fromLTRB(
590 (left ~/ other).toDouble(),
591 (top ~/ other).toDouble(),
592 (right ~/ other).toDouble(),
593 (bottom ~/ other).toDouble(),
594 );
595 }
596
597 /// Computes the remainder in each dimension by the given factor.
598 @override
599 EdgeInsets operator %(double other) {
600 return EdgeInsets.fromLTRB(
601 left % other,
602 top % other,
603 right % other,
604 bottom % other,
605 );
606 }
607
608 /// Linearly interpolate between two [EdgeInsets].
609 ///
610 /// If either is null, this function interpolates from [EdgeInsets.zero].
611 ///
612 /// {@macro dart.ui.shadow.lerp}
613 static EdgeInsets? lerp(EdgeInsets? a, EdgeInsets? b, double t) {
614 if (identical(a, b)) {
615 return a;
616 }
617 if (a == null) {
618 return b! * t;
619 }
620 if (b == null) {
621 return a * (1.0 - t);
622 }
623 return EdgeInsets.fromLTRB(
624 ui.lerpDouble(a.left, b.left, t)!,
625 ui.lerpDouble(a.top, b.top, t)!,
626 ui.lerpDouble(a.right, b.right, t)!,
627 ui.lerpDouble(a.bottom, b.bottom, t)!,
628 );
629 }
630
631 @override
632 EdgeInsets resolve(TextDirection? direction) => this;
633
634 /// Creates a copy of this EdgeInsets but with the given fields replaced
635 /// with the new values.
636 EdgeInsets copyWith({
637 double? left,
638 double? top,
639 double? right,
640 double? bottom,
641 }) {
642 return EdgeInsets.only(
643 left: left ?? this.left,
644 top: top ?? this.top,
645 right: right ?? this.right,
646 bottom: bottom ?? this.bottom,
647 );
648 }
649}
650
651/// An immutable set of offsets in each of the four cardinal directions, but
652/// whose horizontal components are dependent on the writing direction.
653///
654/// This can be used to indicate padding from the left in [TextDirection.ltr]
655/// text and padding from the right in [TextDirection.rtl] text without having
656/// to be aware of the current text direction.
657///
658/// See also:
659///
660/// * [EdgeInsets], a variant that uses physical labels (left and right instead
661/// of start and end).
662class EdgeInsetsDirectional extends EdgeInsetsGeometry {
663 /// Creates insets from offsets from the start, top, end, and bottom.
664 const EdgeInsetsDirectional.fromSTEB(this.start, this.top, this.end, this.bottom);
665
666 /// Creates insets with only the given values non-zero.
667 ///
668 /// {@tool snippet}
669 ///
670 /// A margin indent of 40 pixels on the leading side:
671 ///
672 /// ```dart
673 /// const EdgeInsetsDirectional.only(start: 40.0)
674 /// ```
675 /// {@end-tool}
676 const EdgeInsetsDirectional.only({
677 this.start = 0.0,
678 this.top = 0.0,
679 this.end = 0.0,
680 this.bottom = 0.0,
681 });
682
683 /// Creates insets with symmetric vertical and horizontal offsets.
684 ///
685 /// This is equivalent to [EdgeInsets.symmetric], since the inset is the same
686 /// with either [TextDirection]. This constructor is just a convenience for
687 /// type compatibility.
688 ///
689 /// {@tool snippet}
690 /// Eight pixel margin above and below, no horizontal margins:
691 ///
692 /// ```dart
693 /// const EdgeInsetsDirectional.symmetric(vertical: 8.0)
694 /// ```
695 /// {@end-tool}
696 const EdgeInsetsDirectional.symmetric({
697 double horizontal = 0.0,
698 double vertical = 0.0,
699 }) : start = horizontal,
700 end = horizontal,
701 top = vertical,
702 bottom = vertical;
703
704 /// Creates insets where all the offsets are `value`.
705 ///
706 /// {@tool snippet}
707 ///
708 /// Typical eight-pixel margin on all sides:
709 ///
710 /// ```dart
711 /// const EdgeInsetsDirectional.all(8.0)
712 /// ```
713 /// {@end-tool}
714 const EdgeInsetsDirectional.all(double value)
715 : start = value,
716 top = value,
717 end = value,
718 bottom = value;
719
720 /// An [EdgeInsetsDirectional] with zero offsets in each direction.
721 ///
722 /// Consider using [EdgeInsets.zero] instead, since that object has the same
723 /// effect, but will be cheaper to [resolve].
724 static const EdgeInsetsDirectional zero = EdgeInsetsDirectional.only();
725
726 /// The offset from the start side, the side from which the user will start
727 /// reading text.
728 ///
729 /// This value is normalized into an [EdgeInsets.left] or [EdgeInsets.right]
730 /// value by the [resolve] method.
731 final double start;
732
733 @override
734 double get _start => start;
735
736 /// The offset from the top.
737 ///
738 /// This value is passed through to [EdgeInsets.top] unmodified by the
739 /// [resolve] method.
740 final double top;
741
742 @override
743 double get _top => top;
744
745 /// The offset from the end side, the side on which the user ends reading
746 /// text.
747 ///
748 /// This value is normalized into an [EdgeInsets.left] or [EdgeInsets.right]
749 /// value by the [resolve] method.
750 final double end;
751
752 @override
753 double get _end => end;
754
755 /// The offset from the bottom.
756 ///
757 /// This value is passed through to [EdgeInsets.bottom] unmodified by the
758 /// [resolve] method.
759 final double bottom;
760
761 @override
762 double get _bottom => bottom;
763
764 @override
765 double get _left => 0.0;
766
767 @override
768 double get _right => 0.0;
769
770 @override
771 bool get isNonNegative => start >= 0.0 && top >= 0.0 && end >= 0.0 && bottom >= 0.0;
772
773 /// An [EdgeInsetsDirectional] with [top] and [bottom] as well as [start] and [end] flipped.
774 @override
775 EdgeInsetsDirectional get flipped => EdgeInsetsDirectional.fromSTEB(end, bottom, start, top);
776
777 @override
778 EdgeInsetsGeometry subtract(EdgeInsetsGeometry other) {
779 if (other is EdgeInsetsDirectional) {
780 return this - other;
781 }
782 return super.subtract(other);
783 }
784
785 @override
786 EdgeInsetsGeometry add(EdgeInsetsGeometry other) {
787 if (other is EdgeInsetsDirectional) {
788 return this + other;
789 }
790 return super.add(other);
791 }
792
793 /// Returns the difference between two [EdgeInsetsDirectional] objects.
794 EdgeInsetsDirectional operator -(EdgeInsetsDirectional other) {
795 return EdgeInsetsDirectional.fromSTEB(
796 start - other.start,
797 top - other.top,
798 end - other.end,
799 bottom - other.bottom,
800 );
801 }
802
803 /// Returns the sum of two [EdgeInsetsDirectional] objects.
804 EdgeInsetsDirectional operator +(EdgeInsetsDirectional other) {
805 return EdgeInsetsDirectional.fromSTEB(
806 start + other.start,
807 top + other.top,
808 end + other.end,
809 bottom + other.bottom,
810 );
811 }
812
813 /// Returns the [EdgeInsetsDirectional] object with each dimension negated.
814 ///
815 /// This is the same as multiplying the object by -1.0.
816 @override
817 EdgeInsetsDirectional operator -() {
818 return EdgeInsetsDirectional.fromSTEB(
819 -start,
820 -top,
821 -end,
822 -bottom,
823 );
824 }
825
826 /// Scales the [EdgeInsetsDirectional] object in each dimension by the given factor.
827 @override
828 EdgeInsetsDirectional operator *(double other) {
829 return EdgeInsetsDirectional.fromSTEB(
830 start * other,
831 top * other,
832 end * other,
833 bottom * other,
834 );
835 }
836
837 /// Divides the [EdgeInsetsDirectional] object in each dimension by the given factor.
838 @override
839 EdgeInsetsDirectional operator /(double other) {
840 return EdgeInsetsDirectional.fromSTEB(
841 start / other,
842 top / other,
843 end / other,
844 bottom / other,
845 );
846 }
847
848 /// Integer divides the [EdgeInsetsDirectional] object in each dimension by the given factor.
849 @override
850 EdgeInsetsDirectional operator ~/(double other) {
851 return EdgeInsetsDirectional.fromSTEB(
852 (start ~/ other).toDouble(),
853 (top ~/ other).toDouble(),
854 (end ~/ other).toDouble(),
855 (bottom ~/ other).toDouble(),
856 );
857 }
858
859 /// Computes the remainder in each dimension by the given factor.
860 @override
861 EdgeInsetsDirectional operator %(double other) {
862 return EdgeInsetsDirectional.fromSTEB(
863 start % other,
864 top % other,
865 end % other,
866 bottom % other,
867 );
868 }
869
870 /// Linearly interpolate between two [EdgeInsetsDirectional].
871 ///
872 /// If either is null, this function interpolates from [EdgeInsetsDirectional.zero].
873 ///
874 /// To interpolate between two [EdgeInsetsGeometry] objects of arbitrary type
875 /// (either [EdgeInsets] or [EdgeInsetsDirectional]), consider the
876 /// [EdgeInsetsGeometry.lerp] static method.
877 ///
878 /// {@macro dart.ui.shadow.lerp}
879 static EdgeInsetsDirectional? lerp(EdgeInsetsDirectional? a, EdgeInsetsDirectional? b, double t) {
880 if (identical(a, b)) {
881 return a;
882 }
883 if (a == null) {
884 return b! * t;
885 }
886 if (b == null) {
887 return a * (1.0 - t);
888 }
889 return EdgeInsetsDirectional.fromSTEB(
890 ui.lerpDouble(a.start, b.start, t)!,
891 ui.lerpDouble(a.top, b.top, t)!,
892 ui.lerpDouble(a.end, b.end, t)!,
893 ui.lerpDouble(a.bottom, b.bottom, t)!,
894 );
895 }
896
897 @override
898 EdgeInsets resolve(TextDirection? direction) {
899 assert(direction != null);
900 switch (direction!) {
901 case TextDirection.rtl:
902 return EdgeInsets.fromLTRB(end, top, start, bottom);
903 case TextDirection.ltr:
904 return EdgeInsets.fromLTRB(start, top, end, bottom);
905 }
906 }
907
908 /// Creates a copy of this EdgeInsetsDirectional but with the given
909 /// fields replaced with the new values.
910 EdgeInsetsDirectional copyWith({
911 double? start,
912 double? top,
913 double? end,
914 double? bottom,
915 }) {
916 return EdgeInsetsDirectional.only(
917 start: start ?? this.start,
918 top: top ?? this.top,
919 end: end ?? this.end,
920 bottom: bottom ?? this.bottom,
921 );
922 }
923}
924
925class _MixedEdgeInsets extends EdgeInsetsGeometry {
926 const _MixedEdgeInsets.fromLRSETB(this._left, this._right, this._start, this._end, this._top, this._bottom);
927
928 @override
929 final double _left;
930
931 @override
932 final double _right;
933
934 @override
935 final double _start;
936
937 @override
938 final double _end;
939
940 @override
941 final double _top;
942
943 @override
944 final double _bottom;
945
946 @override
947 bool get isNonNegative {
948 return _left >= 0.0
949 && _right >= 0.0
950 && _start >= 0.0
951 && _end >= 0.0
952 && _top >= 0.0
953 && _bottom >= 0.0;
954 }
955
956 @override
957 _MixedEdgeInsets operator -() {
958 return _MixedEdgeInsets.fromLRSETB(
959 -_left,
960 -_right,
961 -_start,
962 -_end,
963 -_top,
964 -_bottom,
965 );
966 }
967
968 @override
969 _MixedEdgeInsets operator *(double other) {
970 return _MixedEdgeInsets.fromLRSETB(
971 _left * other,
972 _right * other,
973 _start * other,
974 _end * other,
975 _top * other,
976 _bottom * other,
977 );
978 }
979
980 @override
981 _MixedEdgeInsets operator /(double other) {
982 return _MixedEdgeInsets.fromLRSETB(
983 _left / other,
984 _right / other,
985 _start / other,
986 _end / other,
987 _top / other,
988 _bottom / other,
989 );
990 }
991
992 @override
993 _MixedEdgeInsets operator ~/(double other) {
994 return _MixedEdgeInsets.fromLRSETB(
995 (_left ~/ other).toDouble(),
996 (_right ~/ other).toDouble(),
997 (_start ~/ other).toDouble(),
998 (_end ~/ other).toDouble(),
999 (_top ~/ other).toDouble(),
1000 (_bottom ~/ other).toDouble(),
1001 );
1002 }
1003
1004 @override
1005 _MixedEdgeInsets operator %(double other) {
1006 return _MixedEdgeInsets.fromLRSETB(
1007 _left % other,
1008 _right % other,
1009 _start % other,
1010 _end % other,
1011 _top % other,
1012 _bottom % other,
1013 );
1014 }
1015
1016 @override
1017 EdgeInsets resolve(TextDirection? direction) {
1018 assert(direction != null);
1019 switch (direction!) {
1020 case TextDirection.rtl:
1021 return EdgeInsets.fromLTRB(_end + _left, _top, _start + _right, _bottom);
1022 case TextDirection.ltr:
1023 return EdgeInsets.fromLTRB(_start + _left, _top, _end + _right, _bottom);
1024 }
1025 }
1026}
1027