| // 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 'package:flutter/foundation.dart'; |
| |
| import 'basic_types.dart'; |
| |
| /// Base class for [BorderRadius] that allows for text-direction aware resolution. |
| /// |
| /// A property or argument of this type accepts classes created either with [ |
| /// BorderRadius.only] and its variants, or [BorderRadiusDirectional.only] |
| /// and its variants. |
| /// |
| /// To convert a [BorderRadiusGeometry] object of indeterminate type into a |
| /// [BorderRadius] object, call the [resolve] method. |
| @immutable |
| abstract class BorderRadiusGeometry { |
| /// Abstract const constructor. This constructor enables subclasses to provide |
| /// const constructors so that they can be used in const expressions. |
| const BorderRadiusGeometry(); |
| |
| Radius get _topLeft; |
| Radius get _topRight; |
| Radius get _bottomLeft; |
| Radius get _bottomRight; |
| Radius get _topStart; |
| Radius get _topEnd; |
| Radius get _bottomStart; |
| Radius get _bottomEnd; |
| |
| /// Returns the difference between two [BorderRadiusGeometry] objects. |
| /// |
| /// If you know you are applying this to two [BorderRadius] or two |
| /// [BorderRadiusDirectional] 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 ([BorderRadius] or |
| /// [BorderRadiusDirectional]), 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 [BorderRadius] 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). |
| BorderRadiusGeometry subtract(BorderRadiusGeometry other) { |
| return _MixedBorderRadius( |
| _topLeft - other._topLeft, |
| _topRight - other._topRight, |
| _bottomLeft - other._bottomLeft, |
| _bottomRight - other._bottomRight, |
| _topStart - other._topStart, |
| _topEnd - other._topEnd, |
| _bottomStart - other._bottomStart, |
| _bottomEnd - other._bottomEnd, |
| ); |
| } |
| |
| /// Returns the sum of two [BorderRadiusGeometry] objects. |
| /// |
| /// If you know you are adding two [BorderRadius] or two [BorderRadiusDirectional] |
| /// 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 ([BorderRadius] or |
| /// [BorderRadiusDirectional]), 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 [BorderRadius] using [resolve]. |
| BorderRadiusGeometry add(BorderRadiusGeometry other) { |
| return _MixedBorderRadius( |
| _topLeft + other._topLeft, |
| _topRight + other._topRight, |
| _bottomLeft + other._bottomLeft, |
| _bottomRight + other._bottomRight, |
| _topStart + other._topStart, |
| _topEnd + other._topEnd, |
| _bottomStart + other._bottomStart, |
| _bottomEnd + other._bottomEnd, |
| ); |
| } |
| |
| /// Returns the [BorderRadiusGeometry] object with each corner radius negated. |
| /// |
| /// This is the same as multiplying the object by -1.0. |
| /// |
| /// This operator returns an object of the same type as the operand. |
| BorderRadiusGeometry operator -(); |
| |
| /// Scales the [BorderRadiusGeometry] object's corners by the given factor. |
| /// |
| /// This operator returns an object of the same type as the operand. |
| BorderRadiusGeometry operator *(double other); |
| |
| /// Divides the [BorderRadiusGeometry] object's corners by the given factor. |
| /// |
| /// This operator returns an object of the same type as the operand. |
| BorderRadiusGeometry operator /(double other); |
| |
| /// Integer divides the [BorderRadiusGeometry] object's corners 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 |
| /// [BorderRadius] and [BorderRadiusDirectional] objects. |
| BorderRadiusGeometry operator ~/(double other); |
| |
| /// Computes the remainder of each corner 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 |
| /// [BorderRadius] and [BorderRadiusDirectional] objects. |
| BorderRadiusGeometry operator %(double other); |
| |
| /// Linearly interpolate between two [BorderRadiusGeometry] objects. |
| /// |
| /// If either is null, this function interpolates from [BorderRadius.zero], |
| /// and the result is an object of the same type as the non-null argument. (If |
| /// both are null, this returns null.) |
| /// |
| /// If [lerp] is applied to two objects of the same type ([BorderRadius] or |
| /// [BorderRadiusDirectional]), 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 [BorderRadius] using [resolve]. |
| /// |
| /// {@macro dart.ui.shadow.lerp} |
| static BorderRadiusGeometry? lerp(BorderRadiusGeometry? a, BorderRadiusGeometry? b, double t) { |
| assert(t != null); |
| if (a == null && b == null) { |
| return null; |
| } |
| a ??= BorderRadius.zero; |
| b ??= BorderRadius.zero; |
| return a.add((b.subtract(a)) * t); |
| } |
| |
| /// Convert this instance into a [BorderRadius], so that the radii are |
| /// expressed for specific physical corners (top-left, top-right, etc) rather |
| /// than in a direction-dependent manner. |
| /// |
| /// See also: |
| /// |
| /// * [BorderRadius], for which this is a no-op (returns itself). |
| /// * [BorderRadiusDirectional], which flips the horizontal direction |
| /// based on the `direction` argument. |
| BorderRadius resolve(TextDirection? direction); |
| |
| @override |
| String toString() { |
| String? visual, logical; |
| if (_topLeft == _topRight && |
| _topRight == _bottomLeft && |
| _bottomLeft == _bottomRight) { |
| if (_topLeft != Radius.zero) { |
| if (_topLeft.x == _topLeft.y) { |
| visual = 'BorderRadius.circular(${_topLeft.x.toStringAsFixed(1)})'; |
| } else { |
| visual = 'BorderRadius.all($_topLeft)'; |
| } |
| } |
| } else { |
| // visuals aren't the same and at least one isn't zero |
| final StringBuffer result = StringBuffer(); |
| result.write('BorderRadius.only('); |
| bool comma = false; |
| if (_topLeft != Radius.zero) { |
| result.write('topLeft: $_topLeft'); |
| comma = true; |
| } |
| if (_topRight != Radius.zero) { |
| if (comma) { |
| result.write(', '); |
| } |
| result.write('topRight: $_topRight'); |
| comma = true; |
| } |
| if (_bottomLeft != Radius.zero) { |
| if (comma) { |
| result.write(', '); |
| } |
| result.write('bottomLeft: $_bottomLeft'); |
| comma = true; |
| } |
| if (_bottomRight != Radius.zero) { |
| if (comma) { |
| result.write(', '); |
| } |
| result.write('bottomRight: $_bottomRight'); |
| } |
| result.write(')'); |
| visual = result.toString(); |
| } |
| if (_topStart == _topEnd && |
| _topEnd == _bottomEnd && |
| _bottomEnd == _bottomStart) { |
| if (_topStart != Radius.zero) { |
| if (_topStart.x == _topStart.y) { |
| logical = 'BorderRadiusDirectional.circular(${_topStart.x.toStringAsFixed(1)})'; |
| } else { |
| logical = 'BorderRadiusDirectional.all($_topStart)'; |
| } |
| } |
| } else { |
| // logicals aren't the same and at least one isn't zero |
| final StringBuffer result = StringBuffer(); |
| result.write('BorderRadiusDirectional.only('); |
| bool comma = false; |
| if (_topStart != Radius.zero) { |
| result.write('topStart: $_topStart'); |
| comma = true; |
| } |
| if (_topEnd != Radius.zero) { |
| if (comma) { |
| result.write(', '); |
| } |
| result.write('topEnd: $_topEnd'); |
| comma = true; |
| } |
| if (_bottomStart != Radius.zero) { |
| if (comma) { |
| result.write(', '); |
| } |
| result.write('bottomStart: $_bottomStart'); |
| comma = true; |
| } |
| if (_bottomEnd != Radius.zero) { |
| if (comma) { |
| result.write(', '); |
| } |
| result.write('bottomEnd: $_bottomEnd'); |
| } |
| result.write(')'); |
| logical = result.toString(); |
| } |
| if (visual != null && logical != null) { |
| return '$visual + $logical'; |
| } |
| if (visual != null) { |
| return visual; |
| } |
| if (logical != null) { |
| return logical; |
| } |
| return 'BorderRadius.zero'; |
| } |
| |
| @override |
| bool operator ==(Object other) { |
| if (identical(this, other)) { |
| return true; |
| } |
| if (other.runtimeType != runtimeType) { |
| return false; |
| } |
| return other is BorderRadiusGeometry |
| && other._topLeft == _topLeft |
| && other._topRight == _topRight |
| && other._bottomLeft == _bottomLeft |
| && other._bottomRight == _bottomRight |
| && other._topStart == _topStart |
| && other._topEnd == _topEnd |
| && other._bottomStart == _bottomStart |
| && other._bottomEnd == _bottomEnd; |
| } |
| |
| @override |
| int get hashCode => Object.hash( |
| _topLeft, |
| _topRight, |
| _bottomLeft, |
| _bottomRight, |
| _topStart, |
| _topEnd, |
| _bottomStart, |
| _bottomEnd, |
| ); |
| } |
| |
| /// An immutable set of radii for each corner of a rectangle. |
| /// |
| /// Used by [BoxDecoration] when the shape is a [BoxShape.rectangle]. |
| /// |
| /// The [BorderRadius] class specifies offsets in terms of visual corners, e.g. |
| /// [topLeft]. These values are not affected by the [TextDirection]. To support |
| /// both left-to-right and right-to-left layouts, consider using |
| /// [BorderRadiusDirectional], which is expressed in terms that are relative to |
| /// a [TextDirection] (typically obtained from the ambient [Directionality]). |
| class BorderRadius extends BorderRadiusGeometry { |
| /// Creates a border radius where all radii are [radius]. |
| const BorderRadius.all(Radius radius) : this.only( |
| topLeft: radius, |
| topRight: radius, |
| bottomLeft: radius, |
| bottomRight: radius, |
| ); |
| |
| /// Creates a border radius where all radii are [Radius.circular(radius)]. |
| BorderRadius.circular(double radius) : this.all( |
| Radius.circular(radius), |
| ); |
| |
| /// Creates a vertically symmetric border radius where the top and bottom |
| /// sides of the rectangle have the same radii. |
| const BorderRadius.vertical({ |
| Radius top = Radius.zero, |
| Radius bottom = Radius.zero, |
| }) : this.only( |
| topLeft: top, |
| topRight: top, |
| bottomLeft: bottom, |
| bottomRight: bottom, |
| ); |
| |
| /// Creates a horizontally symmetrical border radius where the left and right |
| /// sides of the rectangle have the same radii. |
| const BorderRadius.horizontal({ |
| Radius left = Radius.zero, |
| Radius right = Radius.zero, |
| }) : this.only( |
| topLeft: left, |
| topRight: right, |
| bottomLeft: left, |
| bottomRight: right, |
| ); |
| |
| /// Creates a border radius with only the given non-zero values. The other |
| /// corners will be right angles. |
| const BorderRadius.only({ |
| this.topLeft = Radius.zero, |
| this.topRight = Radius.zero, |
| this.bottomLeft = Radius.zero, |
| this.bottomRight = Radius.zero, |
| }); |
| |
| /// Returns a copy of this BorderRadius with the given fields replaced with |
| /// the new values. |
| BorderRadius copyWith({ |
| Radius? topLeft, |
| Radius? topRight, |
| Radius? bottomLeft, |
| Radius? bottomRight, |
| }) { |
| return BorderRadius.only( |
| topLeft: topLeft ?? this.topLeft, |
| topRight: topRight ?? this.topRight, |
| bottomLeft: bottomLeft ?? this.bottomLeft, |
| bottomRight: bottomRight ?? this.bottomRight, |
| ); |
| } |
| |
| /// A border radius with all zero radii. |
| static const BorderRadius zero = BorderRadius.all(Radius.zero); |
| |
| /// The top-left [Radius]. |
| final Radius topLeft; |
| |
| @override |
| Radius get _topLeft => topLeft; |
| |
| /// The top-right [Radius]. |
| final Radius topRight; |
| |
| @override |
| Radius get _topRight => topRight; |
| |
| /// The bottom-left [Radius]. |
| final Radius bottomLeft; |
| |
| @override |
| Radius get _bottomLeft => bottomLeft; |
| |
| /// The bottom-right [Radius]. |
| final Radius bottomRight; |
| |
| @override |
| Radius get _bottomRight => bottomRight; |
| |
| @override |
| Radius get _topStart => Radius.zero; |
| |
| @override |
| Radius get _topEnd => Radius.zero; |
| |
| @override |
| Radius get _bottomStart => Radius.zero; |
| |
| @override |
| Radius get _bottomEnd => Radius.zero; |
| |
| /// Creates an [RRect] from the current border radius and a [Rect]. |
| /// |
| /// If any of the radii have negative values in x or y, those values will be |
| /// clamped to zero in order to produce a valid [RRect]. |
| RRect toRRect(Rect rect) { |
| // Because the current radii could be negative, we must clamp them before |
| // converting them to an RRect to be rendered, since negative radii on |
| // RRects don't make sense. |
| return RRect.fromRectAndCorners( |
| rect, |
| topLeft: topLeft.clamp(minimum: Radius.zero), // ignore_clamp_double_lint |
| topRight: topRight.clamp(minimum: Radius.zero), // ignore_clamp_double_lint |
| bottomLeft: bottomLeft.clamp(minimum: Radius.zero), // ignore_clamp_double_lint |
| bottomRight: bottomRight.clamp(minimum: Radius.zero), // ignore_clamp_double_lint |
| ); |
| } |
| |
| @override |
| BorderRadiusGeometry subtract(BorderRadiusGeometry other) { |
| if (other is BorderRadius) { |
| return this - other; |
| } |
| return super.subtract(other); |
| } |
| |
| @override |
| BorderRadiusGeometry add(BorderRadiusGeometry other) { |
| if (other is BorderRadius) { |
| return this + other; |
| } |
| return super.add(other); |
| } |
| |
| /// Returns the difference between two [BorderRadius] objects. |
| BorderRadius operator -(BorderRadius other) { |
| return BorderRadius.only( |
| topLeft: topLeft - other.topLeft, |
| topRight: topRight - other.topRight, |
| bottomLeft: bottomLeft - other.bottomLeft, |
| bottomRight: bottomRight - other.bottomRight, |
| ); |
| } |
| |
| /// Returns the sum of two [BorderRadius] objects. |
| BorderRadius operator +(BorderRadius other) { |
| return BorderRadius.only( |
| topLeft: topLeft + other.topLeft, |
| topRight: topRight + other.topRight, |
| bottomLeft: bottomLeft + other.bottomLeft, |
| bottomRight: bottomRight + other.bottomRight, |
| ); |
| } |
| |
| /// Returns the [BorderRadius] object with each corner negated. |
| /// |
| /// This is the same as multiplying the object by -1.0. |
| @override |
| BorderRadius operator -() { |
| return BorderRadius.only( |
| topLeft: -topLeft, |
| topRight: -topRight, |
| bottomLeft: -bottomLeft, |
| bottomRight: -bottomRight, |
| ); |
| } |
| |
| /// Scales each corner of the [BorderRadius] by the given factor. |
| @override |
| BorderRadius operator *(double other) { |
| return BorderRadius.only( |
| topLeft: topLeft * other, |
| topRight: topRight * other, |
| bottomLeft: bottomLeft * other, |
| bottomRight: bottomRight * other, |
| ); |
| } |
| |
| /// Divides each corner of the [BorderRadius] by the given factor. |
| @override |
| BorderRadius operator /(double other) { |
| return BorderRadius.only( |
| topLeft: topLeft / other, |
| topRight: topRight / other, |
| bottomLeft: bottomLeft / other, |
| bottomRight: bottomRight / other, |
| ); |
| } |
| |
| /// Integer divides each corner of the [BorderRadius] by the given factor. |
| @override |
| BorderRadius operator ~/(double other) { |
| return BorderRadius.only( |
| topLeft: topLeft ~/ other, |
| topRight: topRight ~/ other, |
| bottomLeft: bottomLeft ~/ other, |
| bottomRight: bottomRight ~/ other, |
| ); |
| } |
| |
| /// Computes the remainder of each corner by the given factor. |
| @override |
| BorderRadius operator %(double other) { |
| return BorderRadius.only( |
| topLeft: topLeft % other, |
| topRight: topRight % other, |
| bottomLeft: bottomLeft % other, |
| bottomRight: bottomRight % other, |
| ); |
| } |
| |
| /// Linearly interpolate between two [BorderRadius] objects. |
| /// |
| /// If either is null, this function interpolates from [BorderRadius.zero]. |
| /// |
| /// {@macro dart.ui.shadow.lerp} |
| static BorderRadius? lerp(BorderRadius? a, BorderRadius? 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 BorderRadius.only( |
| topLeft: Radius.lerp(a.topLeft, b.topLeft, t)!, |
| topRight: Radius.lerp(a.topRight, b.topRight, t)!, |
| bottomLeft: Radius.lerp(a.bottomLeft, b.bottomLeft, t)!, |
| bottomRight: Radius.lerp(a.bottomRight, b.bottomRight, t)!, |
| ); |
| } |
| |
| @override |
| BorderRadius resolve(TextDirection? direction) => this; |
| } |
| |
| /// An immutable set of radii for each corner of a rectangle, but with the |
| /// corners specified in a manner dependent on the writing direction. |
| /// |
| /// This can be used to specify a corner radius on the leading or trailing edge |
| /// of a box, so that it flips to the other side when the text alignment flips |
| /// (e.g. being on the top right in English text but the top left in Arabic |
| /// text). |
| /// |
| /// See also: |
| /// |
| /// * [BorderRadius], a variant that uses physical labels (`topLeft` and |
| /// `topRight` instead of `topStart` and `topEnd`). |
| class BorderRadiusDirectional extends BorderRadiusGeometry { |
| /// Creates a border radius where all radii are [radius]. |
| const BorderRadiusDirectional.all(Radius radius) : this.only( |
| topStart: radius, |
| topEnd: radius, |
| bottomStart: radius, |
| bottomEnd: radius, |
| ); |
| |
| /// Creates a border radius where all radii are [Radius.circular(radius)]. |
| BorderRadiusDirectional.circular(double radius) : this.all( |
| Radius.circular(radius), |
| ); |
| |
| /// Creates a vertically symmetric border radius where the top and bottom |
| /// sides of the rectangle have the same radii. |
| const BorderRadiusDirectional.vertical({ |
| Radius top = Radius.zero, |
| Radius bottom = Radius.zero, |
| }) : this.only( |
| topStart: top, |
| topEnd: top, |
| bottomStart: bottom, |
| bottomEnd: bottom, |
| ); |
| |
| /// Creates a horizontally symmetrical border radius where the start and end |
| /// sides of the rectangle have the same radii. |
| const BorderRadiusDirectional.horizontal({ |
| Radius start = Radius.zero, |
| Radius end = Radius.zero, |
| }) : this.only( |
| topStart: start, |
| topEnd: end, |
| bottomStart: start, |
| bottomEnd: end, |
| ); |
| |
| /// Creates a border radius with only the given non-zero values. The other |
| /// corners will be right angles. |
| const BorderRadiusDirectional.only({ |
| this.topStart = Radius.zero, |
| this.topEnd = Radius.zero, |
| this.bottomStart = Radius.zero, |
| this.bottomEnd = Radius.zero, |
| }); |
| |
| /// A border radius with all zero radii. |
| /// |
| /// Consider using [EdgeInsets.zero] instead, since that object has the same |
| /// effect, but will be cheaper to [resolve]. |
| static const BorderRadiusDirectional zero = BorderRadiusDirectional.all(Radius.zero); |
| |
| /// The top-start [Radius]. |
| final Radius topStart; |
| |
| @override |
| Radius get _topStart => topStart; |
| |
| /// The top-end [Radius]. |
| final Radius topEnd; |
| |
| @override |
| Radius get _topEnd => topEnd; |
| |
| /// The bottom-start [Radius]. |
| final Radius bottomStart; |
| |
| @override |
| Radius get _bottomStart => bottomStart; |
| |
| /// The bottom-end [Radius]. |
| final Radius bottomEnd; |
| |
| @override |
| Radius get _bottomEnd => bottomEnd; |
| |
| @override |
| Radius get _topLeft => Radius.zero; |
| |
| @override |
| Radius get _topRight => Radius.zero; |
| |
| @override |
| Radius get _bottomLeft => Radius.zero; |
| |
| @override |
| Radius get _bottomRight => Radius.zero; |
| |
| @override |
| BorderRadiusGeometry subtract(BorderRadiusGeometry other) { |
| if (other is BorderRadiusDirectional) { |
| return this - other; |
| } |
| return super.subtract(other); |
| } |
| |
| @override |
| BorderRadiusGeometry add(BorderRadiusGeometry other) { |
| if (other is BorderRadiusDirectional) { |
| return this + other; |
| } |
| return super.add(other); |
| } |
| |
| /// Returns the difference between two [BorderRadiusDirectional] objects. |
| BorderRadiusDirectional operator -(BorderRadiusDirectional other) { |
| return BorderRadiusDirectional.only( |
| topStart: topStart - other.topStart, |
| topEnd: topEnd - other.topEnd, |
| bottomStart: bottomStart - other.bottomStart, |
| bottomEnd: bottomEnd - other.bottomEnd, |
| ); |
| } |
| |
| /// Returns the sum of two [BorderRadiusDirectional] objects. |
| BorderRadiusDirectional operator +(BorderRadiusDirectional other) { |
| return BorderRadiusDirectional.only( |
| topStart: topStart + other.topStart, |
| topEnd: topEnd + other.topEnd, |
| bottomStart: bottomStart + other.bottomStart, |
| bottomEnd: bottomEnd + other.bottomEnd, |
| ); |
| } |
| |
| /// Returns the [BorderRadiusDirectional] object with each corner negated. |
| /// |
| /// This is the same as multiplying the object by -1.0. |
| @override |
| BorderRadiusDirectional operator -() { |
| return BorderRadiusDirectional.only( |
| topStart: -topStart, |
| topEnd: -topEnd, |
| bottomStart: -bottomStart, |
| bottomEnd: -bottomEnd, |
| ); |
| } |
| |
| /// Scales each corner of the [BorderRadiusDirectional] by the given factor. |
| @override |
| BorderRadiusDirectional operator *(double other) { |
| return BorderRadiusDirectional.only( |
| topStart: topStart * other, |
| topEnd: topEnd * other, |
| bottomStart: bottomStart * other, |
| bottomEnd: bottomEnd * other, |
| ); |
| } |
| |
| /// Divides each corner of the [BorderRadiusDirectional] by the given factor. |
| @override |
| BorderRadiusDirectional operator /(double other) { |
| return BorderRadiusDirectional.only( |
| topStart: topStart / other, |
| topEnd: topEnd / other, |
| bottomStart: bottomStart / other, |
| bottomEnd: bottomEnd / other, |
| ); |
| } |
| |
| /// Integer divides each corner of the [BorderRadiusDirectional] by the given factor. |
| @override |
| BorderRadiusDirectional operator ~/(double other) { |
| return BorderRadiusDirectional.only( |
| topStart: topStart ~/ other, |
| topEnd: topEnd ~/ other, |
| bottomStart: bottomStart ~/ other, |
| bottomEnd: bottomEnd ~/ other, |
| ); |
| } |
| |
| /// Computes the remainder of each corner by the given factor. |
| @override |
| BorderRadiusDirectional operator %(double other) { |
| return BorderRadiusDirectional.only( |
| topStart: topStart % other, |
| topEnd: topEnd % other, |
| bottomStart: bottomStart % other, |
| bottomEnd: bottomEnd % other, |
| ); |
| } |
| |
| /// Linearly interpolate between two [BorderRadiusDirectional] objects. |
| /// |
| /// If either is null, this function interpolates from [BorderRadiusDirectional.zero]. |
| /// |
| /// {@macro dart.ui.shadow.lerp} |
| static BorderRadiusDirectional? lerp(BorderRadiusDirectional? a, BorderRadiusDirectional? 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 BorderRadiusDirectional.only( |
| topStart: Radius.lerp(a.topStart, b.topStart, t)!, |
| topEnd: Radius.lerp(a.topEnd, b.topEnd, t)!, |
| bottomStart: Radius.lerp(a.bottomStart, b.bottomStart, t)!, |
| bottomEnd: Radius.lerp(a.bottomEnd, b.bottomEnd, t)!, |
| ); |
| } |
| |
| @override |
| BorderRadius resolve(TextDirection? direction) { |
| assert(direction != null); |
| switch (direction!) { |
| case TextDirection.rtl: |
| return BorderRadius.only( |
| topLeft: topEnd, |
| topRight: topStart, |
| bottomLeft: bottomEnd, |
| bottomRight: bottomStart, |
| ); |
| case TextDirection.ltr: |
| return BorderRadius.only( |
| topLeft: topStart, |
| topRight: topEnd, |
| bottomLeft: bottomStart, |
| bottomRight: bottomEnd, |
| ); |
| } |
| } |
| } |
| |
| class _MixedBorderRadius extends BorderRadiusGeometry { |
| const _MixedBorderRadius( |
| this._topLeft, |
| this._topRight, |
| this._bottomLeft, |
| this._bottomRight, |
| this._topStart, |
| this._topEnd, |
| this._bottomStart, |
| this._bottomEnd, |
| ); |
| |
| @override |
| final Radius _topLeft; |
| |
| @override |
| final Radius _topRight; |
| |
| @override |
| final Radius _bottomLeft; |
| |
| @override |
| final Radius _bottomRight; |
| |
| @override |
| final Radius _topStart; |
| |
| @override |
| final Radius _topEnd; |
| |
| @override |
| final Radius _bottomStart; |
| |
| @override |
| final Radius _bottomEnd; |
| |
| @override |
| _MixedBorderRadius operator -() { |
| return _MixedBorderRadius( |
| -_topLeft, |
| -_topRight, |
| -_bottomLeft, |
| -_bottomRight, |
| -_topStart, |
| -_topEnd, |
| -_bottomStart, |
| -_bottomEnd, |
| ); |
| } |
| |
| /// Scales each corner of the [_MixedBorderRadius] by the given factor. |
| @override |
| _MixedBorderRadius operator *(double other) { |
| return _MixedBorderRadius( |
| _topLeft * other, |
| _topRight * other, |
| _bottomLeft * other, |
| _bottomRight * other, |
| _topStart * other, |
| _topEnd * other, |
| _bottomStart * other, |
| _bottomEnd * other, |
| ); |
| } |
| |
| @override |
| _MixedBorderRadius operator /(double other) { |
| return _MixedBorderRadius( |
| _topLeft / other, |
| _topRight / other, |
| _bottomLeft / other, |
| _bottomRight / other, |
| _topStart / other, |
| _topEnd / other, |
| _bottomStart / other, |
| _bottomEnd / other, |
| ); |
| } |
| |
| @override |
| _MixedBorderRadius operator ~/(double other) { |
| return _MixedBorderRadius( |
| _topLeft ~/ other, |
| _topRight ~/ other, |
| _bottomLeft ~/ other, |
| _bottomRight ~/ other, |
| _topStart ~/ other, |
| _topEnd ~/ other, |
| _bottomStart ~/ other, |
| _bottomEnd ~/ other, |
| ); |
| } |
| |
| @override |
| _MixedBorderRadius operator %(double other) { |
| return _MixedBorderRadius( |
| _topLeft % other, |
| _topRight % other, |
| _bottomLeft % other, |
| _bottomRight % other, |
| _topStart % other, |
| _topEnd % other, |
| _bottomStart % other, |
| _bottomEnd % other, |
| ); |
| } |
| |
| @override |
| BorderRadius resolve(TextDirection? direction) { |
| assert(direction != null); |
| switch (direction!) { |
| case TextDirection.rtl: |
| return BorderRadius.only( |
| topLeft: _topLeft + _topEnd, |
| topRight: _topRight + _topStart, |
| bottomLeft: _bottomLeft + _bottomEnd, |
| bottomRight: _bottomRight + _bottomStart, |
| ); |
| case TextDirection.ltr: |
| return BorderRadius.only( |
| topLeft: _topLeft + _topStart, |
| topRight: _topRight + _topEnd, |
| bottomLeft: _bottomLeft + _bottomStart, |
| bottomRight: _bottomRight + _bottomEnd, |
| ); |
| } |
| } |
| } |