// 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. import 'dart:ui' as ui show WindowPadding, lerpDouble; import 'package:flutter/foundation.dart'; import 'basic_types.dart'; /// Base class for [EdgeInsets] that allows for text-direction aware /// resolution. /// /// A property or argument of this type accepts classes created either with [ /// EdgeInsets.fromLTRB] and its variants, or [ /// EdgeInsetsDirectional.fromSTEB] and its variants. /// /// To convert an [EdgeInsetsGeometry] object of indeterminate type into a /// [EdgeInsets] object, call the [resolve] method. /// /// See also: /// /// * [Padding], a widget that describes margins using [EdgeInsetsGeometry]. @immutable abstract class EdgeInsetsGeometry { /// Abstract const constructor. This constructor enables subclasses to provide /// const constructors so that they can be used in const expressions. const EdgeInsetsGeometry(); double get _bottom; double get _end; double get _left; double get _right; double get _start; double get _top; /// An [EdgeInsetsGeometry] with infinite offsets in each direction. /// /// Can be used as an infinite upper bound for [clamp]. static const EdgeInsetsGeometry infinity = _MixedEdgeInsets.fromLRSETB( double.infinity, double.infinity, double.infinity, double.infinity, double.infinity, double.infinity, ); /// Whether every dimension is non-negative. bool get isNonNegative { return _left >= 0.0 && _right >= 0.0 && _start >= 0.0 && _end >= 0.0 && _top >= 0.0 && _bottom >= 0.0; } /// The total offset in the horizontal direction. double get horizontal => _left + _right + _start + _end; /// The total offset in the vertical direction. double get vertical => _top + _bottom; /// The total offset in the given direction. double along(Axis axis) { assert(axis != null); switch (axis) { case Axis.horizontal: return horizontal; case Axis.vertical: return vertical; } } /// The size that this [EdgeInsets] would occupy with an empty interior. Size get collapsedSize => Size(horizontal, vertical); /// An [EdgeInsetsGeometry] with top and bottom, left and right, and start and end flipped. EdgeInsetsGeometry get flipped => _MixedEdgeInsets.fromLRSETB(_right, _left, _end, _start, _bottom, _top); /// Returns a new size that is bigger than the given size by the amount of /// inset in the horizontal and vertical directions. /// /// See also: /// /// * [EdgeInsets.inflateRect], to inflate a [Rect] rather than a [Size] (for /// [EdgeInsetsDirectional], requires first calling [resolve] to establish /// how the start and end map to the left or right). /// * [deflateSize], to deflate a [Size] rather than inflating it. Size inflateSize(Size size) { return Size(size.width + horizontal, size.height + vertical); } /// Returns a new size that is smaller than the given size by the amount of /// inset in the horizontal and vertical directions. /// /// If the argument is smaller than [collapsedSize], then the resulting size /// will have negative dimensions. /// /// See also: /// /// * [EdgeInsets.deflateRect], to deflate a [Rect] rather than a [Size]. (for /// [EdgeInsetsDirectional], requires first calling [resolve] to establish /// how the start and end map to the left or right). /// * [inflateSize], to inflate a [Size] rather than deflating it. Size deflateSize(Size size) { return Size(size.width - horizontal, size.height - vertical); } /// Returns the difference between two [EdgeInsetsGeometry] objects. /// /// If you know you are applying this to two [EdgeInsets] or two /// [EdgeInsetsDirectional] objects, consider using the binary infix `-` /// operator instead, which always returns an object of the same type as the /// operands, and is typed accordingly. /// /// If [subtract] is applied to two objects of the same type ([EdgeInsets] or /// [EdgeInsetsDirectional]), an object of that type will be returned (though /// this is not reflected in the type system). Otherwise, an object /// representing a combination of both is returned. That object can be turned /// into a concrete [EdgeInsets] using [resolve]. /// /// This method returns the same result as [add] applied to the result of /// negating the argument (using the prefix unary `-` operator or multiplying /// the argument by -1.0 using the `*` operator). EdgeInsetsGeometry subtract(EdgeInsetsGeometry other) { return _MixedEdgeInsets.fromLRSETB( _left - other._left, _right - other._right, _start - other._start, _end - other._end, _top - other._top, _bottom - other._bottom, ); } /// Returns the sum of two [EdgeInsetsGeometry] objects. /// /// If you know you are adding two [EdgeInsets] or two [EdgeInsetsDirectional] /// objects, consider using the `+` operator instead, which always returns an /// object of the same type as the operands, and is typed accordingly. /// /// If [add] is applied to two objects of the same type ([EdgeInsets] or /// [EdgeInsetsDirectional]), an object of that type will be returned (though /// this is not reflected in the type system). Otherwise, an object /// representing a combination of both is returned. That object can be turned /// into a concrete [EdgeInsets] using [resolve]. EdgeInsetsGeometry add(EdgeInsetsGeometry other) { return _MixedEdgeInsets.fromLRSETB( _left + other._left, _right + other._right, _start + other._start, _end + other._end, _top + other._top, _bottom + other._bottom, ); } /// Returns a new [EdgeInsetsGeometry] object with all values greater than /// or equal to `min`, and less than or equal to `max`. EdgeInsetsGeometry clamp(EdgeInsetsGeometry min, EdgeInsetsGeometry max) { return _MixedEdgeInsets.fromLRSETB( clampDouble(_left, min._left, max._left), clampDouble(_right, min._right, max._right), clampDouble(_start, min._start, max._start), clampDouble(_end, min._end, max._end), clampDouble(_top, min._top, max._top), clampDouble(_bottom, min._bottom, max._bottom), ); } /// Returns the [EdgeInsetsGeometry] object with each dimension negated. /// /// This is the same as multiplying the object by -1.0. /// /// This operator returns an object of the same type as the operand. EdgeInsetsGeometry operator -(); /// Scales the [EdgeInsetsGeometry] object in each dimension by the given factor. /// /// This operator returns an object of the same type as the operand. EdgeInsetsGeometry operator *(double other); /// Divides the [EdgeInsetsGeometry] object in each dimension by the given factor. /// /// This operator returns an object of the same type as the operand. EdgeInsetsGeometry operator /(double other); /// Integer divides the [EdgeInsetsGeometry] object in each dimension by the given factor. /// /// This operator returns an object of the same type as the operand. /// /// This operator may have unexpected results when applied to a mixture of /// [EdgeInsets] and [EdgeInsetsDirectional] objects. EdgeInsetsGeometry operator ~/(double other); /// Computes the remainder in each dimension by the given factor. /// /// This operator returns an object of the same type as the operand. /// /// This operator may have unexpected results when applied to a mixture of /// [EdgeInsets] and [EdgeInsetsDirectional] objects. EdgeInsetsGeometry operator %(double other); /// Linearly interpolate between two [EdgeInsetsGeometry] objects. /// /// If either is null, this function interpolates from [EdgeInsets.zero], and /// the result is an object of the same type as the non-null argument. /// /// If [lerp] is applied to two objects of the same type ([EdgeInsets] or /// [EdgeInsetsDirectional]), an object of that type will be returned (though /// this is not reflected in the type system). Otherwise, an object /// representing a combination of both is returned. That object can be turned /// into a concrete [EdgeInsets] using [resolve]. /// /// {@macro dart.ui.shadow.lerp} static EdgeInsetsGeometry? lerp(EdgeInsetsGeometry? a, EdgeInsetsGeometry? b, double t) { assert(t != null); if (a == null && b == null) { return null; } if (a == null) { return b! * t; } if (b == null) { return a * (1.0 - t); } if (a is EdgeInsets && b is EdgeInsets) { return EdgeInsets.lerp(a, b, t); } if (a is EdgeInsetsDirectional && b is EdgeInsetsDirectional) { return EdgeInsetsDirectional.lerp(a, b, t); } return _MixedEdgeInsets.fromLRSETB( ui.lerpDouble(a._left, b._left, t)!, ui.lerpDouble(a._right, b._right, t)!, ui.lerpDouble(a._start, b._start, t)!, ui.lerpDouble(a._end, b._end, t)!, ui.lerpDouble(a._top, b._top, t)!, ui.lerpDouble(a._bottom, b._bottom, t)!, ); } /// Convert this instance into an [EdgeInsets], which uses literal coordinates /// (i.e. the `left` coordinate being explicitly a distance from the left, and /// the `right` coordinate being explicitly a distance from the right). /// /// See also: /// /// * [EdgeInsets], for which this is a no-op (returns itself). /// * [EdgeInsetsDirectional], which flips the horizontal direction /// based on the `direction` argument. EdgeInsets resolve(TextDirection? direction); @override String toString() { if (_start == 0.0 && _end == 0.0) { if (_left == 0.0 && _right == 0.0 && _top == 0.0 && _bottom == 0.0) { return 'EdgeInsets.zero'; } if (_left == _right && _right == _top && _top == _bottom) { return 'EdgeInsets.all(${_left.toStringAsFixed(1)})'; } return 'EdgeInsets(${_left.toStringAsFixed(1)}, ' '${_top.toStringAsFixed(1)}, ' '${_right.toStringAsFixed(1)}, ' '${_bottom.toStringAsFixed(1)})'; } if (_left == 0.0 && _right == 0.0) { return 'EdgeInsetsDirectional(${_start.toStringAsFixed(1)}, ' '${_top.toStringAsFixed(1)}, ' '${_end.toStringAsFixed(1)}, ' '${_bottom.toStringAsFixed(1)})'; } return 'EdgeInsets(${_left.toStringAsFixed(1)}, ' '${_top.toStringAsFixed(1)}, ' '${_right.toStringAsFixed(1)}, ' '${_bottom.toStringAsFixed(1)})' ' + ' 'EdgeInsetsDirectional(${_start.toStringAsFixed(1)}, ' '0.0, ' '${_end.toStringAsFixed(1)}, ' '0.0)'; } @override bool operator ==(Object other) { return other is EdgeInsetsGeometry && other._left == _left && other._right == _right && other._start == _start && other._end == _end && other._top == _top && other._bottom == _bottom; } @override int get hashCode => Object.hash(_left, _right, _start, _end, _top, _bottom); } /// An immutable set of offsets in each of the four cardinal directions. /// /// Typically used for an offset from each of the four sides of a box. For /// example, the padding inside a box can be represented using this class. /// /// The [EdgeInsets] class specifies offsets in terms of visual edges, left, /// top, right, and bottom. These values are not affected by the /// [TextDirection]. To support both left-to-right and right-to-left layouts, /// consider using [EdgeInsetsDirectional], which is expressed in terms of /// _start_, top, _end_, and bottom, where start and end are resolved in terms /// of a [TextDirection] (typically obtained from the ambient [Directionality]). /// /// {@tool snippet} /// /// Here are some examples of how to create [EdgeInsets] instances: /// /// Typical eight-pixel margin on all sides: /// /// ```dart /// const EdgeInsets.all(8.0) /// ``` /// {@end-tool} /// {@tool snippet} /// /// Eight pixel margin above and below, no horizontal margins: /// /// ```dart /// const EdgeInsets.symmetric(vertical: 8.0) /// ``` /// {@end-tool} /// {@tool snippet} /// /// Left margin indent of 40 pixels: /// /// ```dart /// const EdgeInsets.only(left: 40.0) /// ``` /// {@end-tool} /// /// See also: /// /// * [Padding], a widget that accepts [EdgeInsets] to describe its margins. /// * [EdgeInsetsDirectional], which (for properties and arguments that accept /// the type [EdgeInsetsGeometry]) allows the horizontal insets to be /// specified in a [TextDirection]-aware manner. class EdgeInsets extends EdgeInsetsGeometry { /// Creates insets from offsets from the left, top, right, and bottom. const EdgeInsets.fromLTRB(this.left, this.top, this.right, this.bottom); /// Creates insets where all the offsets are `value`. /// /// {@tool snippet} /// /// Typical eight-pixel margin on all sides: /// /// ```dart /// const EdgeInsets.all(8.0) /// ``` /// {@end-tool} const EdgeInsets.all(double value) : left = value, top = value, right = value, bottom = value; /// Creates insets with only the given values non-zero. /// /// {@tool snippet} /// /// Left margin indent of 40 pixels: /// /// ```dart /// const EdgeInsets.only(left: 40.0) /// ``` /// {@end-tool} const EdgeInsets.only({ this.left = 0.0, this.top = 0.0, this.right = 0.0, this.bottom = 0.0, }); /// Creates insets with symmetrical vertical and horizontal offsets. /// /// {@tool snippet} /// /// Eight pixel margin above and below, no horizontal margins: /// /// ```dart /// const EdgeInsets.symmetric(vertical: 8.0) /// ``` /// {@end-tool} const EdgeInsets.symmetric({ double vertical = 0.0, double horizontal = 0.0, }) : left = horizontal, top = vertical, right = horizontal, bottom = vertical; /// Creates insets that match the given window padding. /// /// If you need the current system padding or view insets in the context of a /// widget, consider using [MediaQuery.of] to obtain these values rather than /// using the value from [dart:ui.window], so that you get notified of /// changes. EdgeInsets.fromWindowPadding(ui.WindowPadding padding, double devicePixelRatio) : left = padding.left / devicePixelRatio, top = padding.top / devicePixelRatio, right = padding.right / devicePixelRatio, bottom = padding.bottom / devicePixelRatio; /// An [EdgeInsets] with zero offsets in each direction. static const EdgeInsets zero = EdgeInsets.only(); /// The offset from the left. final double left; @override double get _left => left; /// The offset from the top. final double top; @override double get _top => top; /// The offset from the right. final double right; @override double get _right => right; /// The offset from the bottom. final double bottom; @override double get _bottom => bottom; @override double get _start => 0.0; @override double get _end => 0.0; /// An Offset describing the vector from the top left of a rectangle to the /// top left of that rectangle inset by this object. Offset get topLeft => Offset(left, top); /// An Offset describing the vector from the top right of a rectangle to the /// top right of that rectangle inset by this object. Offset get topRight => Offset(-right, top); /// An Offset describing the vector from the bottom left of a rectangle to the /// bottom left of that rectangle inset by this object. Offset get bottomLeft => Offset(left, -bottom); /// An Offset describing the vector from the bottom right of a rectangle to the /// bottom right of that rectangle inset by this object. Offset get bottomRight => Offset(-right, -bottom); /// An [EdgeInsets] with top and bottom as well as left and right flipped. @override EdgeInsets get flipped => EdgeInsets.fromLTRB(right, bottom, left, top); /// Returns a new rect that is bigger than the given rect in each direction by /// the amount of inset in each direction. Specifically, the left edge of the /// rect is moved left by [left], the top edge of the rect is moved up by /// [top], the right edge of the rect is moved right by [right], and the /// bottom edge of the rect is moved down by [bottom]. /// /// See also: /// /// * [inflateSize], to inflate a [Size] rather than a [Rect]. /// * [deflateRect], to deflate a [Rect] rather than inflating it. Rect inflateRect(Rect rect) { return Rect.fromLTRB(rect.left - left, rect.top - top, rect.right + right, rect.bottom + bottom); } /// Returns a new rect that is smaller than the given rect in each direction by /// the amount of inset in each direction. Specifically, the left edge of the /// rect is moved right by [left], the top edge of the rect is moved down by /// [top], the right edge of the rect is moved left by [right], and the /// bottom edge of the rect is moved up by [bottom]. /// /// If the argument's [Rect.size] is smaller than [collapsedSize], then the /// resulting rectangle will have negative dimensions. /// /// See also: /// /// * [deflateSize], to deflate a [Size] rather than a [Rect]. /// * [inflateRect], to inflate a [Rect] rather than deflating it. Rect deflateRect(Rect rect) { return Rect.fromLTRB(rect.left + left, rect.top + top, rect.right - right, rect.bottom - bottom); } @override EdgeInsetsGeometry subtract(EdgeInsetsGeometry other) { if (other is EdgeInsets) { return this - other; } return super.subtract(other); } @override EdgeInsetsGeometry add(EdgeInsetsGeometry other) { if (other is EdgeInsets) { return this + other; } return super.add(other); } @override EdgeInsetsGeometry clamp(EdgeInsetsGeometry min, EdgeInsetsGeometry max) { return EdgeInsets.fromLTRB( clampDouble(_left, min._left, max._left), clampDouble(_top, min._top, max._top), clampDouble(_right, min._right, max._right), clampDouble(_bottom, min._bottom, max._bottom), ); } /// Returns the difference between two [EdgeInsets]. EdgeInsets operator -(EdgeInsets other) { return EdgeInsets.fromLTRB( left - other.left, top - other.top, right - other.right, bottom - other.bottom, ); } /// Returns the sum of two [EdgeInsets]. EdgeInsets operator +(EdgeInsets other) { return EdgeInsets.fromLTRB( left + other.left, top + other.top, right + other.right, bottom + other.bottom, ); } /// Returns the [EdgeInsets] object with each dimension negated. /// /// This is the same as multiplying the object by -1.0. @override EdgeInsets operator -() { return EdgeInsets.fromLTRB( -left, -top, -right, -bottom, ); } /// Scales the [EdgeInsets] in each dimension by the given factor. @override EdgeInsets operator *(double other) { return EdgeInsets.fromLTRB( left * other, top * other, right * other, bottom * other, ); } /// Divides the [EdgeInsets] in each dimension by the given factor. @override EdgeInsets operator /(double other) { return EdgeInsets.fromLTRB( left / other, top / other, right / other, bottom / other, ); } /// Integer divides the [EdgeInsets] in each dimension by the given factor. @override EdgeInsets operator ~/(double other) { return EdgeInsets.fromLTRB( (left ~/ other).toDouble(), (top ~/ other).toDouble(), (right ~/ other).toDouble(), (bottom ~/ other).toDouble(), ); } /// Computes the remainder in each dimension by the given factor. @override EdgeInsets operator %(double other) { return EdgeInsets.fromLTRB( left % other, top % other, right % other, bottom % other, ); } /// Linearly interpolate between two [EdgeInsets]. /// /// If either is null, this function interpolates from [EdgeInsets.zero]. /// /// {@macro dart.ui.shadow.lerp} static EdgeInsets? lerp(EdgeInsets? a, EdgeInsets? b, double t) { assert(t != null); if (a == null && b == null) { return null; } if (a == null) { return b! * t; } if (b == null) { return a * (1.0 - t); } return EdgeInsets.fromLTRB( ui.lerpDouble(a.left, b.left, t)!, ui.lerpDouble(a.top, b.top, t)!, ui.lerpDouble(a.right, b.right, t)!, ui.lerpDouble(a.bottom, b.bottom, t)!, ); } @override EdgeInsets resolve(TextDirection? direction) => this; /// Creates a copy of this EdgeInsets but with the given fields replaced /// with the new values. EdgeInsets copyWith({ double? left, double? top, double? right, double? bottom, }) { return EdgeInsets.only( left: left ?? this.left, top: top ?? this.top, right: right ?? this.right, bottom: bottom ?? this.bottom, ); } } /// An immutable set of offsets in each of the four cardinal directions, but /// whose horizontal components are dependent on the writing direction. /// /// This can be used to indicate padding from the left in [TextDirection.ltr] /// text and padding from the right in [TextDirection.rtl] text without having /// to be aware of the current text direction. /// /// See also: /// /// * [EdgeInsets], a variant that uses physical labels (left and right instead /// of start and end). class EdgeInsetsDirectional extends EdgeInsetsGeometry { /// Creates insets from offsets from the start, top, end, and bottom. const EdgeInsetsDirectional.fromSTEB(this.start, this.top, this.end, this.bottom); /// Creates insets with only the given values non-zero. /// /// {@tool snippet} /// /// A margin indent of 40 pixels on the leading side: /// /// ```dart /// const EdgeInsetsDirectional.only(start: 40.0) /// ``` /// {@end-tool} const EdgeInsetsDirectional.only({ this.start = 0.0, this.top = 0.0, this.end = 0.0, this.bottom = 0.0, }); /// Creates insets with symmetric vertical and horizontal offsets. /// /// This is equivalent to [EdgeInsets.symmetric], since the inset is the same /// with either [TextDirection]. This constructor is just a convenience for /// type compatibility. /// /// {@tool snippet} /// Eight pixel margin above and below, no horizontal margins: /// /// ```dart /// const EdgeInsetsDirectional.symmetric(vertical: 8.0) /// ``` /// {@end-tool} const EdgeInsetsDirectional.symmetric({ double horizontal = 0.0, double vertical = 0.0, }) : start = horizontal, end = horizontal, top = vertical, bottom = vertical; /// Creates insets where all the offsets are `value`. /// /// {@tool snippet} /// /// Typical eight-pixel margin on all sides: /// /// ```dart /// const EdgeInsetsDirectional.all(8.0) /// ``` /// {@end-tool} const EdgeInsetsDirectional.all(double value) : start = value, top = value, end = value, bottom = value; /// An [EdgeInsetsDirectional] with zero offsets in each direction. /// /// Consider using [EdgeInsets.zero] instead, since that object has the same /// effect, but will be cheaper to [resolve]. static const EdgeInsetsDirectional zero = EdgeInsetsDirectional.only(); /// The offset from the start side, the side from which the user will start /// reading text. /// /// This value is normalized into an [EdgeInsets.left] or [EdgeInsets.right] /// value by the [resolve] method. final double start; @override double get _start => start; /// The offset from the top. /// /// This value is passed through to [EdgeInsets.top] unmodified by the /// [resolve] method. final double top; @override double get _top => top; /// The offset from the end side, the side on which the user ends reading /// text. /// /// This value is normalized into an [EdgeInsets.left] or [EdgeInsets.right] /// value by the [resolve] method. final double end; @override double get _end => end; /// The offset from the bottom. /// /// This value is passed through to [EdgeInsets.bottom] unmodified by the /// [resolve] method. final double bottom; @override double get _bottom => bottom; @override double get _left => 0.0; @override double get _right => 0.0; @override bool get isNonNegative => start >= 0.0 && top >= 0.0 && end >= 0.0 && bottom >= 0.0; /// An [EdgeInsetsDirectional] with [top] and [bottom] as well as [start] and [end] flipped. @override EdgeInsetsDirectional get flipped => EdgeInsetsDirectional.fromSTEB(end, bottom, start, top); @override EdgeInsetsGeometry subtract(EdgeInsetsGeometry other) { if (other is EdgeInsetsDirectional) { return this - other; } return super.subtract(other); } @override EdgeInsetsGeometry add(EdgeInsetsGeometry other) { if (other is EdgeInsetsDirectional) { return this + other; } return super.add(other); } /// Returns the difference between two [EdgeInsetsDirectional] objects. EdgeInsetsDirectional operator -(EdgeInsetsDirectional other) { return EdgeInsetsDirectional.fromSTEB( start - other.start, top - other.top, end - other.end, bottom - other.bottom, ); } /// Returns the sum of two [EdgeInsetsDirectional] objects. EdgeInsetsDirectional operator +(EdgeInsetsDirectional other) { return EdgeInsetsDirectional.fromSTEB( start + other.start, top + other.top, end + other.end, bottom + other.bottom, ); } /// Returns the [EdgeInsetsDirectional] object with each dimension negated. /// /// This is the same as multiplying the object by -1.0. @override EdgeInsetsDirectional operator -() { return EdgeInsetsDirectional.fromSTEB( -start, -top, -end, -bottom, ); } /// Scales the [EdgeInsetsDirectional] object in each dimension by the given factor. @override EdgeInsetsDirectional operator *(double other) { return EdgeInsetsDirectional.fromSTEB( start * other, top * other, end * other, bottom * other, ); } /// Divides the [EdgeInsetsDirectional] object in each dimension by the given factor. @override EdgeInsetsDirectional operator /(double other) { return EdgeInsetsDirectional.fromSTEB( start / other, top / other, end / other, bottom / other, ); } /// Integer divides the [EdgeInsetsDirectional] object in each dimension by the given factor. @override EdgeInsetsDirectional operator ~/(double other) { return EdgeInsetsDirectional.fromSTEB( (start ~/ other).toDouble(), (top ~/ other).toDouble(), (end ~/ other).toDouble(), (bottom ~/ other).toDouble(), ); } /// Computes the remainder in each dimension by the given factor. @override EdgeInsetsDirectional operator %(double other) { return EdgeInsetsDirectional.fromSTEB( start % other, top % other, end % other, bottom % other, ); } /// Linearly interpolate between two [EdgeInsetsDirectional]. /// /// If either is null, this function interpolates from [EdgeInsetsDirectional.zero]. /// /// To interpolate between two [EdgeInsetsGeometry] objects of arbitrary type /// (either [EdgeInsets] or [EdgeInsetsDirectional]), consider the /// [EdgeInsetsGeometry.lerp] static method. /// /// {@macro dart.ui.shadow.lerp} static EdgeInsetsDirectional? lerp(EdgeInsetsDirectional? a, EdgeInsetsDirectional? b, double t) { assert(t != null); if (a == null && b == null) { return null; } if (a == null) { return b! * t; } if (b == null) { return a * (1.0 - t); } return EdgeInsetsDirectional.fromSTEB( ui.lerpDouble(a.start, b.start, t)!, ui.lerpDouble(a.top, b.top, t)!, ui.lerpDouble(a.end, b.end, t)!, ui.lerpDouble(a.bottom, b.bottom, t)!, ); } @override EdgeInsets resolve(TextDirection? direction) { assert(direction != null); switch (direction!) { case TextDirection.rtl: return EdgeInsets.fromLTRB(end, top, start, bottom); case TextDirection.ltr: return EdgeInsets.fromLTRB(start, top, end, bottom); } } } class _MixedEdgeInsets extends EdgeInsetsGeometry { const _MixedEdgeInsets.fromLRSETB(this._left, this._right, this._start, this._end, this._top, this._bottom); @override final double _left; @override final double _right; @override final double _start; @override final double _end; @override final double _top; @override final double _bottom; @override bool get isNonNegative { return _left >= 0.0 && _right >= 0.0 && _start >= 0.0 && _end >= 0.0 && _top >= 0.0 && _bottom >= 0.0; } @override _MixedEdgeInsets operator -() { return _MixedEdgeInsets.fromLRSETB( -_left, -_right, -_start, -_end, -_top, -_bottom, ); } @override _MixedEdgeInsets operator *(double other) { return _MixedEdgeInsets.fromLRSETB( _left * other, _right * other, _start * other, _end * other, _top * other, _bottom * other, ); } @override _MixedEdgeInsets operator /(double other) { return _MixedEdgeInsets.fromLRSETB( _left / other, _right / other, _start / other, _end / other, _top / other, _bottom / other, ); } @override _MixedEdgeInsets operator ~/(double other) { return _MixedEdgeInsets.fromLRSETB( (_left ~/ other).toDouble(), (_right ~/ other).toDouble(), (_start ~/ other).toDouble(), (_end ~/ other).toDouble(), (_top ~/ other).toDouble(), (_bottom ~/ other).toDouble(), ); } @override _MixedEdgeInsets operator %(double other) { return _MixedEdgeInsets.fromLRSETB( _left % other, _right % other, _start % other, _end % other, _top % other, _bottom % other, ); } @override EdgeInsets resolve(TextDirection? direction) { assert(direction != null); switch (direction!) { case TextDirection.rtl: return EdgeInsets.fromLTRB(_end + _left, _top, _start + _right, _bottom); case TextDirection.ltr: return EdgeInsets.fromLTRB(_start + _left, _top, _end + _right, _bottom); } } }