"Fossies" - the Fresh Open Source Software Archive

Member "flutter-3.7.1/packages/flutter/lib/src/painting/alignment.dart" (1 Feb 2023, 23336 Bytes) of package /linux/misc/flutter-3.7.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Dart source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    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 import 'dart:ui' as ui show lerpDouble;
    6 
    7 import 'package:flutter/foundation.dart';
    8 
    9 import 'basic_types.dart';
   10 
   11 /// Base class for [Alignment] that allows for text-direction aware
   12 /// resolution.
   13 ///
   14 /// A property or argument of this type accepts classes created either with [
   15 /// Alignment] and its variants, or [AlignmentDirectional.new].
   16 ///
   17 /// To convert an [AlignmentGeometry] object of indeterminate type into an
   18 /// [Alignment] object, call the [resolve] method.
   19 @immutable
   20 abstract class AlignmentGeometry {
   21   /// Abstract const constructor. This constructor enables subclasses to provide
   22   /// const constructors so that they can be used in const expressions.
   23   const AlignmentGeometry();
   24 
   25   double get _x;
   26 
   27   double get _start;
   28 
   29   double get _y;
   30 
   31   /// Returns the sum of two [AlignmentGeometry] objects.
   32   ///
   33   /// If you know you are adding two [Alignment] or two [AlignmentDirectional]
   34   /// objects, consider using the `+` operator instead, which always returns an
   35   /// object of the same type as the operands, and is typed accordingly.
   36   ///
   37   /// If [add] is applied to two objects of the same type ([Alignment] or
   38   /// [AlignmentDirectional]), an object of that type will be returned (though
   39   /// this is not reflected in the type system). Otherwise, an object
   40   /// representing a combination of both is returned. That object can be turned
   41   /// into a concrete [Alignment] using [resolve].
   42   AlignmentGeometry add(AlignmentGeometry other) {
   43     return _MixedAlignment(
   44       _x + other._x,
   45       _start + other._start,
   46       _y + other._y,
   47     );
   48   }
   49 
   50   /// Returns the negation of the given [AlignmentGeometry] object.
   51   ///
   52   /// This is the same as multiplying the object by -1.0.
   53   ///
   54   /// This operator returns an object of the same type as the operand.
   55   AlignmentGeometry operator -();
   56 
   57   /// Scales the [AlignmentGeometry] object in each dimension by the given factor.
   58   ///
   59   /// This operator returns an object of the same type as the operand.
   60   AlignmentGeometry operator *(double other);
   61 
   62   /// Divides the [AlignmentGeometry] object in each dimension by the given factor.
   63   ///
   64   /// This operator returns an object of the same type as the operand.
   65   AlignmentGeometry operator /(double other);
   66 
   67   /// Integer divides the [AlignmentGeometry] object in each dimension by the given factor.
   68   ///
   69   /// This operator returns an object of the same type as the operand.
   70   AlignmentGeometry operator ~/(double other);
   71 
   72   /// Computes the remainder in each dimension by the given factor.
   73   ///
   74   /// This operator returns an object of the same type as the operand.
   75   AlignmentGeometry operator %(double other);
   76 
   77   /// Linearly interpolate between two [AlignmentGeometry] objects.
   78   ///
   79   /// If either is null, this function interpolates from [Alignment.center], and
   80   /// the result is an object of the same type as the non-null argument.
   81   ///
   82   /// If [lerp] is applied to two objects of the same type ([Alignment] or
   83   /// [AlignmentDirectional]), an object of that type will be returned (though
   84   /// this is not reflected in the type system). Otherwise, an object
   85   /// representing a combination of both is returned. That object can be turned
   86   /// into a concrete [Alignment] using [resolve].
   87   ///
   88   /// {@macro dart.ui.shadow.lerp}
   89   static AlignmentGeometry? lerp(AlignmentGeometry? a, AlignmentGeometry? b, double t) {
   90     assert(t != null);
   91     if (a == null && b == null) {
   92       return null;
   93     }
   94     if (a == null) {
   95       return b! * t;
   96     }
   97     if (b == null) {
   98       return a * (1.0 - t);
   99     }
  100     if (a is Alignment && b is Alignment) {
  101       return Alignment.lerp(a, b, t);
  102     }
  103     if (a is AlignmentDirectional && b is AlignmentDirectional) {
  104       return AlignmentDirectional.lerp(a, b, t);
  105     }
  106     return _MixedAlignment(
  107       ui.lerpDouble(a._x, b._x, t)!,
  108       ui.lerpDouble(a._start, b._start, t)!,
  109       ui.lerpDouble(a._y, b._y, t)!,
  110     );
  111   }
  112 
  113   /// Convert this instance into an [Alignment], which uses literal
  114   /// coordinates (the `x` coordinate being explicitly a distance from the
  115   /// left).
  116   ///
  117   /// See also:
  118   ///
  119   ///  * [Alignment], for which this is a no-op (returns itself).
  120   ///  * [AlignmentDirectional], which flips the horizontal direction
  121   ///    based on the `direction` argument.
  122   Alignment resolve(TextDirection? direction);
  123 
  124   @override
  125   String toString() {
  126     if (_start == 0.0) {
  127       return Alignment._stringify(_x, _y);
  128     }
  129     if (_x == 0.0) {
  130       return AlignmentDirectional._stringify(_start, _y);
  131     }
  132     return '${Alignment._stringify(_x, _y)} + ${AlignmentDirectional._stringify(_start, 0.0)}';
  133   }
  134 
  135   @override
  136   bool operator ==(Object other) {
  137     return other is AlignmentGeometry
  138         && other._x == _x
  139         && other._start == _start
  140         && other._y == _y;
  141   }
  142 
  143   @override
  144   int get hashCode => Object.hash(_x, _start, _y);
  145 }
  146 
  147 /// A point within a rectangle.
  148 ///
  149 /// `Alignment(0.0, 0.0)` represents the center of the rectangle. The distance
  150 /// from -1.0 to +1.0 is the distance from one side of the rectangle to the
  151 /// other side of the rectangle. Therefore, 2.0 units horizontally (or
  152 /// vertically) is equivalent to the width (or height) of the rectangle.
  153 ///
  154 /// `Alignment(-1.0, -1.0)` represents the top left of the rectangle.
  155 ///
  156 /// `Alignment(1.0, 1.0)` represents the bottom right of the rectangle.
  157 ///
  158 /// `Alignment(0.0, 3.0)` represents a point that is horizontally centered with
  159 /// respect to the rectangle and vertically below the bottom of the rectangle by
  160 /// the height of the rectangle.
  161 ///
  162 /// `Alignment(0.0, -0.5)` represents a point that is horizontally centered with
  163 /// respect to the rectangle and vertically half way between the top edge and
  164 /// the center.
  165 ///
  166 /// `Alignment(x, y)` in a rectangle with height h and width w describes
  167 /// the point (x * w/2 + w/2, y * h/2 + h/2) in the coordinate system of the
  168 /// rectangle.
  169 ///
  170 /// [Alignment] uses visual coordinates, which means increasing [x] moves the
  171 /// point from left to right. To support layouts with a right-to-left
  172 /// [TextDirection], consider using [AlignmentDirectional], in which the
  173 /// direction the point moves when increasing the horizontal value depends on
  174 /// the [TextDirection].
  175 ///
  176 /// A variety of widgets use [Alignment] in their configuration, most
  177 /// notably:
  178 ///
  179 ///  * [Align] positions a child according to an [Alignment].
  180 ///
  181 /// See also:
  182 ///
  183 ///  * [AlignmentDirectional], which has a horizontal coordinate orientation
  184 ///    that depends on the [TextDirection].
  185 ///  * [AlignmentGeometry], which is an abstract type that is agnostic as to
  186 ///    whether the horizontal direction depends on the [TextDirection].
  187 class Alignment extends AlignmentGeometry {
  188   /// Creates an alignment.
  189   ///
  190   /// The [x] and [y] arguments must not be null.
  191   const Alignment(this.x, this.y)
  192     : assert(x != null),
  193       assert(y != null);
  194 
  195   /// The distance fraction in the horizontal direction.
  196   ///
  197   /// A value of -1.0 corresponds to the leftmost edge. A value of 1.0
  198   /// corresponds to the rightmost edge. Values are not limited to that range;
  199   /// values less than -1.0 represent positions to the left of the left edge,
  200   /// and values greater than 1.0 represent positions to the right of the right
  201   /// edge.
  202   final double x;
  203 
  204   /// The distance fraction in the vertical direction.
  205   ///
  206   /// A value of -1.0 corresponds to the topmost edge. A value of 1.0
  207   /// corresponds to the bottommost edge. Values are not limited to that range;
  208   /// values less than -1.0 represent positions above the top, and values
  209   /// greater than 1.0 represent positions below the bottom.
  210   final double y;
  211 
  212   @override
  213   double get _x => x;
  214 
  215   @override
  216   double get _start => 0.0;
  217 
  218   @override
  219   double get _y => y;
  220 
  221   /// The top left corner.
  222   static const Alignment topLeft = Alignment(-1.0, -1.0);
  223 
  224   /// The center point along the top edge.
  225   static const Alignment topCenter = Alignment(0.0, -1.0);
  226 
  227   /// The top right corner.
  228   static const Alignment topRight = Alignment(1.0, -1.0);
  229 
  230   /// The center point along the left edge.
  231   static const Alignment centerLeft = Alignment(-1.0, 0.0);
  232 
  233   /// The center point, both horizontally and vertically.
  234   static const Alignment center = Alignment(0.0, 0.0);
  235 
  236   /// The center point along the right edge.
  237   static const Alignment centerRight = Alignment(1.0, 0.0);
  238 
  239   /// The bottom left corner.
  240   static const Alignment bottomLeft = Alignment(-1.0, 1.0);
  241 
  242   /// The center point along the bottom edge.
  243   static const Alignment bottomCenter = Alignment(0.0, 1.0);
  244 
  245   /// The bottom right corner.
  246   static const Alignment bottomRight = Alignment(1.0, 1.0);
  247 
  248   @override
  249   AlignmentGeometry add(AlignmentGeometry other) {
  250     if (other is Alignment) {
  251       return this + other;
  252     }
  253     return super.add(other);
  254   }
  255 
  256   /// Returns the difference between two [Alignment]s.
  257   Alignment operator -(Alignment other) {
  258     return Alignment(x - other.x, y - other.y);
  259   }
  260 
  261   /// Returns the sum of two [Alignment]s.
  262   Alignment operator +(Alignment other) {
  263     return Alignment(x + other.x, y + other.y);
  264   }
  265 
  266   /// Returns the negation of the given [Alignment].
  267   @override
  268   Alignment operator -() {
  269     return Alignment(-x, -y);
  270   }
  271 
  272   /// Scales the [Alignment] in each dimension by the given factor.
  273   @override
  274   Alignment operator *(double other) {
  275     return Alignment(x * other, y * other);
  276   }
  277 
  278   /// Divides the [Alignment] in each dimension by the given factor.
  279   @override
  280   Alignment operator /(double other) {
  281     return Alignment(x / other, y / other);
  282   }
  283 
  284   /// Integer divides the [Alignment] in each dimension by the given factor.
  285   @override
  286   Alignment operator ~/(double other) {
  287     return Alignment((x ~/ other).toDouble(), (y ~/ other).toDouble());
  288   }
  289 
  290   /// Computes the remainder in each dimension by the given factor.
  291   @override
  292   Alignment operator %(double other) {
  293     return Alignment(x % other, y % other);
  294   }
  295 
  296   /// Returns the offset that is this fraction in the direction of the given offset.
  297   Offset alongOffset(Offset other) {
  298     final double centerX = other.dx / 2.0;
  299     final double centerY = other.dy / 2.0;
  300     return Offset(centerX + x * centerX, centerY + y * centerY);
  301   }
  302 
  303   /// Returns the offset that is this fraction within the given size.
  304   Offset alongSize(Size other) {
  305     final double centerX = other.width / 2.0;
  306     final double centerY = other.height / 2.0;
  307     return Offset(centerX + x * centerX, centerY + y * centerY);
  308   }
  309 
  310   /// Returns the point that is this fraction within the given rect.
  311   Offset withinRect(Rect rect) {
  312     final double halfWidth = rect.width / 2.0;
  313     final double halfHeight = rect.height / 2.0;
  314     return Offset(
  315       rect.left + halfWidth + x * halfWidth,
  316       rect.top + halfHeight + y * halfHeight,
  317     );
  318   }
  319 
  320   /// Returns a rect of the given size, aligned within given rect as specified
  321   /// by this alignment.
  322   ///
  323   /// For example, a 100×100 size inscribed on a 200×200 rect using
  324   /// [Alignment.topLeft] would be the 100×100 rect at the top left of
  325   /// the 200×200 rect.
  326   Rect inscribe(Size size, Rect rect) {
  327     final double halfWidthDelta = (rect.width - size.width) / 2.0;
  328     final double halfHeightDelta = (rect.height - size.height) / 2.0;
  329     return Rect.fromLTWH(
  330       rect.left + halfWidthDelta + x * halfWidthDelta,
  331       rect.top + halfHeightDelta + y * halfHeightDelta,
  332       size.width,
  333       size.height,
  334     );
  335   }
  336 
  337   /// Linearly interpolate between two [Alignment]s.
  338   ///
  339   /// If either is null, this function interpolates from [Alignment.center].
  340   ///
  341   /// {@macro dart.ui.shadow.lerp}
  342   static Alignment? lerp(Alignment? a, Alignment? b, double t) {
  343     assert(t != null);
  344     if (a == null && b == null) {
  345       return null;
  346     }
  347     if (a == null) {
  348       return Alignment(ui.lerpDouble(0.0, b!.x, t)!, ui.lerpDouble(0.0, b.y, t)!);
  349     }
  350     if (b == null) {
  351       return Alignment(ui.lerpDouble(a.x, 0.0, t)!, ui.lerpDouble(a.y, 0.0, t)!);
  352     }
  353     return Alignment(ui.lerpDouble(a.x, b.x, t)!, ui.lerpDouble(a.y, b.y, t)!);
  354   }
  355 
  356   @override
  357   Alignment resolve(TextDirection? direction) => this;
  358 
  359   static String _stringify(double x, double y) {
  360     if (x == -1.0 && y == -1.0) {
  361       return 'Alignment.topLeft';
  362     }
  363     if (x == 0.0 && y == -1.0) {
  364       return 'Alignment.topCenter';
  365     }
  366     if (x == 1.0 && y == -1.0) {
  367       return 'Alignment.topRight';
  368     }
  369     if (x == -1.0 && y == 0.0) {
  370       return 'Alignment.centerLeft';
  371     }
  372     if (x == 0.0 && y == 0.0) {
  373       return 'Alignment.center';
  374     }
  375     if (x == 1.0 && y == 0.0) {
  376       return 'Alignment.centerRight';
  377     }
  378     if (x == -1.0 && y == 1.0) {
  379       return 'Alignment.bottomLeft';
  380     }
  381     if (x == 0.0 && y == 1.0) {
  382       return 'Alignment.bottomCenter';
  383     }
  384     if (x == 1.0 && y == 1.0) {
  385       return 'Alignment.bottomRight';
  386     }
  387     return 'Alignment(${x.toStringAsFixed(1)}, '
  388                      '${y.toStringAsFixed(1)})';
  389   }
  390 
  391   @override
  392   String toString() => _stringify(x, y);
  393 }
  394 
  395 /// An offset that's expressed as a fraction of a [Size], but whose horizontal
  396 /// component is dependent on the writing direction.
  397 ///
  398 /// This can be used to indicate an offset from the left in [TextDirection.ltr]
  399 /// text and an offset from the right in [TextDirection.rtl] text without having
  400 /// to be aware of the current text direction.
  401 ///
  402 /// See also:
  403 ///
  404 ///  * [Alignment], a variant that is defined in physical terms (i.e.
  405 ///    whose horizontal component does not depend on the text direction).
  406 class AlignmentDirectional extends AlignmentGeometry {
  407   /// Creates a directional alignment.
  408   ///
  409   /// The [start] and [y] arguments must not be null.
  410   const AlignmentDirectional(this.start, this.y)
  411     : assert(start != null),
  412       assert(y != null);
  413 
  414   /// The distance fraction in the horizontal direction.
  415   ///
  416   /// A value of -1.0 corresponds to the edge on the "start" side, which is the
  417   /// left side in [TextDirection.ltr] contexts and the right side in
  418   /// [TextDirection.rtl] contexts. A value of 1.0 corresponds to the opposite
  419   /// edge, the "end" side. Values are not limited to that range; values less
  420   /// than -1.0 represent positions beyond the start edge, and values greater than
  421   /// 1.0 represent positions beyond the end edge.
  422   ///
  423   /// This value is normalized into an [Alignment.x] value by the [resolve]
  424   /// method.
  425   final double start;
  426 
  427   /// The distance fraction in the vertical direction.
  428   ///
  429   /// A value of -1.0 corresponds to the topmost edge. A value of 1.0
  430   /// corresponds to the bottommost edge. Values are not limited to that range;
  431   /// values less than -1.0 represent positions above the top, and values
  432   /// greater than 1.0 represent positions below the bottom.
  433   ///
  434   /// This value is passed through to [Alignment.y] unmodified by the
  435   /// [resolve] method.
  436   final double y;
  437 
  438   @override
  439   double get _x => 0.0;
  440 
  441   @override
  442   double get _start => start;
  443 
  444   @override
  445   double get _y => y;
  446 
  447   /// The top corner on the "start" side.
  448   static const AlignmentDirectional topStart = AlignmentDirectional(-1.0, -1.0);
  449 
  450   /// The center point along the top edge.
  451   ///
  452   /// Consider using [Alignment.topCenter] instead, as it does not need
  453   /// to be [resolve]d to be used.
  454   static const AlignmentDirectional topCenter = AlignmentDirectional(0.0, -1.0);
  455 
  456   /// The top corner on the "end" side.
  457   static const AlignmentDirectional topEnd = AlignmentDirectional(1.0, -1.0);
  458 
  459   /// The center point along the "start" edge.
  460   static const AlignmentDirectional centerStart = AlignmentDirectional(-1.0, 0.0);
  461 
  462   /// The center point, both horizontally and vertically.
  463   ///
  464   /// Consider using [Alignment.center] instead, as it does not need to
  465   /// be [resolve]d to be used.
  466   static const AlignmentDirectional center = AlignmentDirectional(0.0, 0.0);
  467 
  468   /// The center point along the "end" edge.
  469   static const AlignmentDirectional centerEnd = AlignmentDirectional(1.0, 0.0);
  470 
  471   /// The bottom corner on the "start" side.
  472   static const AlignmentDirectional bottomStart = AlignmentDirectional(-1.0, 1.0);
  473 
  474   /// The center point along the bottom edge.
  475   ///
  476   /// Consider using [Alignment.bottomCenter] instead, as it does not
  477   /// need to be [resolve]d to be used.
  478   static const AlignmentDirectional bottomCenter = AlignmentDirectional(0.0, 1.0);
  479 
  480   /// The bottom corner on the "end" side.
  481   static const AlignmentDirectional bottomEnd = AlignmentDirectional(1.0, 1.0);
  482 
  483   @override
  484   AlignmentGeometry add(AlignmentGeometry other) {
  485     if (other is AlignmentDirectional) {
  486       return this + other;
  487     }
  488     return super.add(other);
  489   }
  490 
  491   /// Returns the difference between two [AlignmentDirectional]s.
  492   AlignmentDirectional operator -(AlignmentDirectional other) {
  493     return AlignmentDirectional(start - other.start, y - other.y);
  494   }
  495 
  496   /// Returns the sum of two [AlignmentDirectional]s.
  497   AlignmentDirectional operator +(AlignmentDirectional other) {
  498     return AlignmentDirectional(start + other.start, y + other.y);
  499   }
  500 
  501   /// Returns the negation of the given [AlignmentDirectional].
  502   @override
  503   AlignmentDirectional operator -() {
  504     return AlignmentDirectional(-start, -y);
  505   }
  506 
  507   /// Scales the [AlignmentDirectional] in each dimension by the given factor.
  508   @override
  509   AlignmentDirectional operator *(double other) {
  510     return AlignmentDirectional(start * other, y * other);
  511   }
  512 
  513   /// Divides the [AlignmentDirectional] in each dimension by the given factor.
  514   @override
  515   AlignmentDirectional operator /(double other) {
  516     return AlignmentDirectional(start / other, y / other);
  517   }
  518 
  519   /// Integer divides the [AlignmentDirectional] in each dimension by the given factor.
  520   @override
  521   AlignmentDirectional operator ~/(double other) {
  522     return AlignmentDirectional((start ~/ other).toDouble(), (y ~/ other).toDouble());
  523   }
  524 
  525   /// Computes the remainder in each dimension by the given factor.
  526   @override
  527   AlignmentDirectional operator %(double other) {
  528     return AlignmentDirectional(start % other, y % other);
  529   }
  530 
  531   /// Linearly interpolate between two [AlignmentDirectional]s.
  532   ///
  533   /// If either is null, this function interpolates from [AlignmentDirectional.center].
  534   ///
  535   /// {@macro dart.ui.shadow.lerp}
  536   static AlignmentDirectional? lerp(AlignmentDirectional? a, AlignmentDirectional? b, double t) {
  537     assert(t != null);
  538     if (a == null && b == null) {
  539       return null;
  540     }
  541     if (a == null) {
  542       return AlignmentDirectional(ui.lerpDouble(0.0, b!.start, t)!, ui.lerpDouble(0.0, b.y, t)!);
  543     }
  544     if (b == null) {
  545       return AlignmentDirectional(ui.lerpDouble(a.start, 0.0, t)!, ui.lerpDouble(a.y, 0.0, t)!);
  546     }
  547     return AlignmentDirectional(ui.lerpDouble(a.start, b.start, t)!, ui.lerpDouble(a.y, b.y, t)!);
  548   }
  549 
  550   @override
  551   Alignment resolve(TextDirection? direction) {
  552     assert(direction != null, 'Cannot resolve $runtimeType without a TextDirection.');
  553     switch (direction!) {
  554       case TextDirection.rtl:
  555         return Alignment(-start, y);
  556       case TextDirection.ltr:
  557         return Alignment(start, y);
  558     }
  559   }
  560 
  561   static String _stringify(double start, double y) {
  562     if (start == -1.0 && y == -1.0) {
  563       return 'AlignmentDirectional.topStart';
  564     }
  565     if (start == 0.0 && y == -1.0) {
  566       return 'AlignmentDirectional.topCenter';
  567     }
  568     if (start == 1.0 && y == -1.0) {
  569       return 'AlignmentDirectional.topEnd';
  570     }
  571     if (start == -1.0 && y == 0.0) {
  572       return 'AlignmentDirectional.centerStart';
  573     }
  574     if (start == 0.0 && y == 0.0) {
  575       return 'AlignmentDirectional.center';
  576     }
  577     if (start == 1.0 && y == 0.0) {
  578       return 'AlignmentDirectional.centerEnd';
  579     }
  580     if (start == -1.0 && y == 1.0) {
  581       return 'AlignmentDirectional.bottomStart';
  582     }
  583     if (start == 0.0 && y == 1.0) {
  584       return 'AlignmentDirectional.bottomCenter';
  585     }
  586     if (start == 1.0 && y == 1.0) {
  587       return 'AlignmentDirectional.bottomEnd';
  588     }
  589     return 'AlignmentDirectional(${start.toStringAsFixed(1)}, '
  590                                 '${y.toStringAsFixed(1)})';
  591   }
  592 
  593   @override
  594   String toString() => _stringify(start, y);
  595 }
  596 
  597 class _MixedAlignment extends AlignmentGeometry {
  598   const _MixedAlignment(this._x, this._start, this._y);
  599 
  600   @override
  601   final double _x;
  602 
  603   @override
  604   final double _start;
  605 
  606   @override
  607   final double _y;
  608 
  609   @override
  610   _MixedAlignment operator -() {
  611     return _MixedAlignment(
  612       -_x,
  613       -_start,
  614       -_y,
  615     );
  616   }
  617 
  618   @override
  619   _MixedAlignment operator *(double other) {
  620     return _MixedAlignment(
  621       _x * other,
  622       _start * other,
  623       _y * other,
  624     );
  625   }
  626 
  627   @override
  628   _MixedAlignment operator /(double other) {
  629     return _MixedAlignment(
  630       _x / other,
  631       _start / other,
  632       _y / other,
  633     );
  634   }
  635 
  636   @override
  637   _MixedAlignment operator ~/(double other) {
  638     return _MixedAlignment(
  639       (_x ~/ other).toDouble(),
  640       (_start ~/ other).toDouble(),
  641       (_y ~/ other).toDouble(),
  642     );
  643   }
  644 
  645   @override
  646   _MixedAlignment operator %(double other) {
  647     return _MixedAlignment(
  648       _x % other,
  649       _start % other,
  650       _y % other,
  651     );
  652   }
  653 
  654   @override
  655   Alignment resolve(TextDirection? direction) {
  656     assert(direction != null, 'Cannot resolve $runtimeType without a TextDirection.');
  657     switch (direction!) {
  658       case TextDirection.rtl:
  659         return Alignment(_x - _start, _y);
  660       case TextDirection.ltr:
  661         return Alignment(_x + _start, _y);
  662     }
  663   }
  664 }
  665 
  666 /// The vertical alignment of text within an input box.
  667 ///
  668 /// A single [y] value that can range from -1.0 to 1.0. -1.0 aligns to the top
  669 /// of an input box so that the top of the first line of text fits within the
  670 /// box and its padding. 0.0 aligns to the center of the box. 1.0 aligns so that
  671 /// the bottom of the last line of text aligns with the bottom interior edge of
  672 /// the input box.
  673 ///
  674 /// See also:
  675 ///
  676 ///  * [TextField.textAlignVertical], which is passed on to the [InputDecorator].
  677 ///  * [CupertinoTextField.textAlignVertical], which behaves in the same way as
  678 ///    the parameter in TextField.
  679 ///  * [InputDecorator.textAlignVertical], which defines the alignment of
  680 ///    prefix, input, and suffix within an [InputDecorator].
  681 class TextAlignVertical {
  682   /// Creates a TextAlignVertical from any y value between -1.0 and 1.0.
  683   const TextAlignVertical({
  684     required this.y,
  685   }) : assert(y != null),
  686        assert(y >= -1.0 && y <= 1.0);
  687 
  688   /// A value ranging from -1.0 to 1.0 that defines the topmost and bottommost
  689   /// locations of the top and bottom of the input box.
  690   final double y;
  691 
  692   /// Aligns a TextField's input Text with the topmost location within a
  693   /// TextField's input box.
  694   static const TextAlignVertical top = TextAlignVertical(y: -1.0);
  695   /// Aligns a TextField's input Text to the center of the TextField.
  696   static const TextAlignVertical center = TextAlignVertical(y: 0.0);
  697   /// Aligns a TextField's input Text with the bottommost location within a
  698   /// TextField.
  699   static const TextAlignVertical bottom = TextAlignVertical(y: 1.0);
  700 
  701   @override
  702   String toString() {
  703     return '${objectRuntimeType(this, 'TextAlignVertical')}(y: $y)';
  704   }
  705 }