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