LCOV - code coverage report
Current view: top level - editing - mongol_input_decorator.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 860 1026 83.8 %
Date: 2021-07-30 09:13:58 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright 2014 The Flutter Authors.
       2             : // Copyright 2021 Suragch.
       3             : // All rights reserved.
       4             : // Use of this source code is governed by a BSD-style license that can be
       5             : // found in the LICENSE file.
       6             : 
       7             : // ignore_for_file: omit_local_variable_types
       8             : 
       9             : import 'dart:math' as math;
      10             : import 'dart:ui' show lerpDouble;
      11             : 
      12             : import 'package:flutter/foundation.dart';
      13             : import 'package:flutter/material.dart'
      14             :     show
      15             :         InputBorder,
      16             :         InputDecoration,
      17             :         Colors,
      18             :         VisualDensity,
      19             :         kMinInteractiveDimension,
      20             :         FloatingLabelBehavior,
      21             :         Theme,
      22             :         ThemeData,
      23             :         Brightness;
      24             : import 'package:flutter/rendering.dart';
      25             : import 'package:flutter/widgets.dart';
      26             : 
      27             : import '../base/mongol_text_align.dart';
      28             : import '../text/mongol_text.dart';
      29             : import 'alignment.dart';
      30             : import 'input_border.dart';
      31             : 
      32             : const Duration _kTransitionDuration = Duration(milliseconds: 200);
      33             : const Curve _kTransitionCurve = Curves.fastOutSlowIn;
      34             : const double _kFinalLabelScale = 0.75;
      35             : 
      36             : // Defines the gap in the MongolInputDecorator's outline border where the
      37             : // floating label will appear.
      38             : class _InputBorderGap extends ChangeNotifier {
      39             :   double? _start;
      40           2 :   double? get start => _start;
      41           1 :   set start(double? value) {
      42           2 :     if (value != _start) {
      43           1 :       _start = value;
      44           1 :       notifyListeners();
      45             :     }
      46             :   }
      47             : 
      48             :   double _extent = 0.0;
      49           2 :   double get extent => _extent;
      50           1 :   set extent(double value) {
      51           2 :     if (value != _extent) {
      52           1 :       _extent = value;
      53           1 :       notifyListeners();
      54             :     }
      55             :   }
      56             : 
      57           1 :   @override
      58             :   // ignore: avoid_equals_and_hash_code_on_mutable_classes, this class is not used in collection
      59             :   bool operator ==(Object other) {
      60             :     if (identical(this, other)) {
      61             :       return true;
      62             :     }
      63           0 :     if (other.runtimeType != runtimeType) {
      64             :       return false;
      65             :     }
      66           0 :     return other is _InputBorderGap &&
      67           0 :         other.start == start &&
      68           0 :         other.extent == extent;
      69             :   }
      70             : 
      71           0 :   @override
      72             :   // ignore: avoid_equals_and_hash_code_on_mutable_classes, this class is not used in collection
      73           0 :   int get hashCode => hashValues(start, extent);
      74             : }
      75             : 
      76             : // Used to interpolate between two InputBorders.
      77             : class _InputBorderTween extends Tween<InputBorder> {
      78           1 :   _InputBorderTween({InputBorder? begin, InputBorder? end})
      79           1 :       : super(begin: begin, end: end);
      80             : 
      81           1 :   @override
      82           3 :   InputBorder lerp(double t) => ShapeBorder.lerp(begin, end, t)! as InputBorder;
      83             : }
      84             : 
      85             : // Passes the _InputBorderGap parameters along to an InputBorder's paint method.
      86             : class _InputBorderPainter extends CustomPainter {
      87           1 :   _InputBorderPainter({
      88             :     required Listenable repaint,
      89             :     required this.borderAnimation,
      90             :     required this.border,
      91             :     required this.gapAnimation,
      92             :     required this.gap,
      93             :     required this.fillColor,
      94             :     required this.hoverAnimation,
      95             :     required this.hoverColorTween,
      96           1 :   }) : super(repaint: repaint);
      97             : 
      98             :   final Animation<double> borderAnimation;
      99             :   final _InputBorderTween border;
     100             :   final Animation<double> gapAnimation;
     101             :   final _InputBorderGap gap;
     102             :   final Color fillColor;
     103             :   final ColorTween hoverColorTween;
     104             :   final Animation<double> hoverAnimation;
     105             : 
     106           1 :   Color get blendedColor =>
     107           5 :       Color.alphaBlend(hoverColorTween.evaluate(hoverAnimation)!, fillColor);
     108             : 
     109           1 :   @override
     110             :   void paint(Canvas canvas, Size size) {
     111           3 :     final borderValue = border.evaluate(borderAnimation);
     112           1 :     final canvasRect = Offset.zero & size;
     113           1 :     final blendedFillColor = blendedColor;
     114           2 :     if (blendedFillColor.alpha > 0) {
     115           0 :       canvas.drawPath(
     116           0 :         borderValue.getOuterPath(canvasRect, textDirection: TextDirection.ltr),
     117           0 :         Paint()
     118           0 :           ..color = blendedFillColor
     119           0 :           ..style = PaintingStyle.fill,
     120             :       );
     121             :     }
     122             : 
     123           1 :     borderValue.paint(
     124             :       canvas,
     125             :       canvasRect,
     126           2 :       gapStart: gap.start,
     127           2 :       gapExtent: gap.extent,
     128           2 :       gapPercentage: gapAnimation.value,
     129             :       textDirection: TextDirection.ltr,
     130             :     );
     131             :   }
     132             : 
     133           1 :   @override
     134             :   bool shouldRepaint(_InputBorderPainter oldPainter) {
     135           3 :     return borderAnimation != oldPainter.borderAnimation ||
     136           3 :         hoverAnimation != oldPainter.hoverAnimation ||
     137           3 :         gapAnimation != oldPainter.gapAnimation ||
     138           3 :         border != oldPainter.border ||
     139           3 :         gap != oldPainter.gap;
     140             :   }
     141             : }
     142             : 
     143             : // An analog of AnimatedContainer, which can animate its shaped border, for
     144             : // _InputBorder. This specialized animated container is needed because the
     145             : // _InputBorderGap, which is computed at layout time, is required by the
     146             : // _InputBorder's paint method.
     147             : class _BorderContainer extends StatefulWidget {
     148           1 :   const _BorderContainer({
     149             :     Key? key,
     150             :     required this.border,
     151             :     required this.gap,
     152             :     required this.gapAnimation,
     153             :     required this.fillColor,
     154             :     required this.hoverColor,
     155             :     required this.isHovering,
     156             :     this.child,
     157           1 :   }) : super(key: key);
     158             : 
     159             :   final InputBorder border;
     160             :   final _InputBorderGap gap;
     161             :   final Animation<double> gapAnimation;
     162             :   final Color fillColor;
     163             :   final Color hoverColor;
     164             :   final bool isHovering;
     165             :   final Widget? child;
     166             : 
     167           1 :   @override
     168           1 :   _BorderContainerState createState() => _BorderContainerState();
     169             : }
     170             : 
     171             : class _BorderContainerState extends State<_BorderContainer>
     172             :     with TickerProviderStateMixin {
     173             :   static const Duration _kHoverDuration = Duration(milliseconds: 15);
     174             : 
     175             :   late AnimationController _controller;
     176             :   late AnimationController _hoverColorController;
     177             :   late Animation<double> _borderAnimation;
     178             :   late _InputBorderTween _border;
     179             :   late Animation<double> _hoverAnimation;
     180             :   late ColorTween _hoverColorTween;
     181             : 
     182           1 :   @override
     183             :   void initState() {
     184           1 :     super.initState();
     185           2 :     _hoverColorController = AnimationController(
     186             :       duration: _kHoverDuration,
     187           2 :       value: widget.isHovering ? 1.0 : 0.0,
     188             :       vsync: this,
     189             :     );
     190           2 :     _controller = AnimationController(
     191             :       duration: _kTransitionDuration,
     192             :       vsync: this,
     193             :     );
     194           2 :     _borderAnimation = CurvedAnimation(
     195           1 :       parent: _controller,
     196             :       curve: _kTransitionCurve,
     197             :     );
     198           2 :     _border = _InputBorderTween(
     199           2 :       begin: widget.border,
     200           2 :       end: widget.border,
     201             :     );
     202           2 :     _hoverAnimation = CurvedAnimation(
     203           1 :       parent: _hoverColorController,
     204             :       curve: Curves.linear,
     205             :     );
     206           1 :     _hoverColorTween =
     207           3 :         ColorTween(begin: Colors.transparent, end: widget.hoverColor);
     208             :   }
     209             : 
     210           1 :   @override
     211             :   void dispose() {
     212           2 :     _controller.dispose();
     213           2 :     _hoverColorController.dispose();
     214           1 :     super.dispose();
     215             :   }
     216             : 
     217           1 :   @override
     218             :   void didUpdateWidget(_BorderContainer oldWidget) {
     219           1 :     super.didUpdateWidget(oldWidget);
     220           4 :     if (widget.border != oldWidget.border) {
     221           2 :       _border = _InputBorderTween(
     222           1 :         begin: oldWidget.border,
     223           2 :         end: widget.border,
     224             :       );
     225           1 :       _controller
     226           1 :         ..value = 0.0
     227           1 :         ..forward();
     228             :     }
     229           4 :     if (widget.hoverColor != oldWidget.hoverColor) {
     230           0 :       _hoverColorTween =
     231           0 :           ColorTween(begin: Colors.transparent, end: widget.hoverColor);
     232             :     }
     233           4 :     if (widget.isHovering != oldWidget.isHovering) {
     234           2 :       if (widget.isHovering) {
     235           2 :         _hoverColorController.forward();
     236             :       } else {
     237           2 :         _hoverColorController.reverse();
     238             :       }
     239             :     }
     240             :   }
     241             : 
     242           1 :   @override
     243             :   Widget build(BuildContext context) {
     244           1 :     return CustomPaint(
     245           1 :       foregroundPainter: _InputBorderPainter(
     246           2 :         repaint: Listenable.merge(<Listenable>[
     247           1 :           _borderAnimation,
     248           2 :           widget.gap,
     249           1 :           _hoverColorController,
     250             :         ]),
     251           1 :         borderAnimation: _borderAnimation,
     252           1 :         border: _border,
     253           2 :         gapAnimation: widget.gapAnimation,
     254           2 :         gap: widget.gap,
     255           2 :         fillColor: widget.fillColor,
     256           1 :         hoverColorTween: _hoverColorTween,
     257           1 :         hoverAnimation: _hoverAnimation,
     258             :       ),
     259           2 :       child: widget.child,
     260             :     );
     261             :   }
     262             : }
     263             : 
     264             : // Used to "shake" the floating label to the up and down
     265             : // when the errorText first appears.
     266             : class _Shaker extends AnimatedWidget {
     267           1 :   const _Shaker({
     268             :     Key? key,
     269             :     required Animation<double> animation,
     270             :     this.child,
     271           1 :   }) : super(key: key, listenable: animation);
     272             : 
     273             :   final Widget? child;
     274             : 
     275           2 :   Animation<double> get animation => listenable as Animation<double>;
     276             : 
     277           1 :   double get translateY {
     278             :     const shakeDelta = 4.0;
     279           2 :     final t = animation.value;
     280           1 :     if (t <= 0.25) {
     281           2 :       return -t * shakeDelta;
     282           0 :     } else if (t < 0.75) {
     283           0 :       return (t - 0.5) * shakeDelta;
     284             :     } else {
     285           0 :       return (1.0 - t) * 4.0 * shakeDelta;
     286             :     }
     287             :   }
     288             : 
     289           1 :   @override
     290             :   Widget build(BuildContext context) {
     291           1 :     return Transform(
     292           2 :       transform: Matrix4.translationValues(0.0, translateY, 0.0),
     293           1 :       child: child,
     294             :     );
     295             :   }
     296             : }
     297             : 
     298             : // Display the helper and error text. When the error text appears
     299             : // it fades and the helper text fades out. The error text also
     300             : // slides leftwards a little when it first appears.
     301             : class _HelperError extends StatefulWidget {
     302           1 :   const _HelperError({
     303             :     Key? key,
     304             :     this.textAlign,
     305             :     this.helperText,
     306             :     this.helperStyle,
     307             :     this.helperMaxLines,
     308             :     this.errorText,
     309             :     this.errorStyle,
     310             :     this.errorMaxLines,
     311           1 :   }) : super(key: key);
     312             : 
     313             :   final MongolTextAlign? textAlign;
     314             :   final String? helperText;
     315             :   final TextStyle? helperStyle;
     316             :   final int? helperMaxLines;
     317             :   final String? errorText;
     318             :   final TextStyle? errorStyle;
     319             :   final int? errorMaxLines;
     320             : 
     321           1 :   @override
     322           1 :   _HelperErrorState createState() => _HelperErrorState();
     323             : }
     324             : 
     325             : class _HelperErrorState extends State<_HelperError>
     326             :     with SingleTickerProviderStateMixin {
     327             :   // If the width of this widget and the counter are zero ("empty") at
     328             :   // layout time, no space is allocated for the subtext.
     329             :   static const Widget empty = SizedBox();
     330             : 
     331             :   late AnimationController _controller;
     332             :   Widget? _helper;
     333             :   Widget? _error;
     334             : 
     335           1 :   @override
     336             :   void initState() {
     337           1 :     super.initState();
     338           2 :     _controller = AnimationController(
     339             :       duration: _kTransitionDuration,
     340             :       vsync: this,
     341             :     );
     342           2 :     if (widget.errorText != null) {
     343           2 :       _error = _buildError();
     344           2 :       _controller.value = 1.0;
     345           2 :     } else if (widget.helperText != null) {
     346           2 :       _helper = _buildHelper();
     347             :     }
     348           3 :     _controller.addListener(_handleChange);
     349             :   }
     350             : 
     351           1 :   @override
     352             :   void dispose() {
     353           2 :     _controller.dispose();
     354           1 :     super.dispose();
     355             :   }
     356             : 
     357           0 :   void _handleChange() {
     358           0 :     setState(() {
     359             :       // The _controller's value has changed.
     360             :     });
     361             :   }
     362             : 
     363           1 :   @override
     364             :   void didUpdateWidget(_HelperError old) {
     365           1 :     super.didUpdateWidget(old);
     366             : 
     367           2 :     final newErrorText = widget.errorText;
     368           2 :     final newHelperText = widget.helperText;
     369           1 :     final oldErrorText = old.errorText;
     370           1 :     final oldHelperText = old.helperText;
     371             : 
     372             :     final errorTextStateChanged =
     373           1 :         (newErrorText != null) != (oldErrorText != null);
     374             :     final helperTextStateChanged = newErrorText == null &&
     375           1 :         (newHelperText != null) != (oldHelperText != null);
     376             : 
     377             :     if (errorTextStateChanged || helperTextStateChanged) {
     378             :       if (newErrorText != null) {
     379           0 :         _error = _buildError();
     380           0 :         _controller.forward();
     381             :       } else if (newHelperText != null) {
     382           0 :         _helper = _buildHelper();
     383           0 :         _controller.reverse();
     384             :       } else {
     385           0 :         _controller.reverse();
     386             :       }
     387             :     }
     388             :   }
     389             : 
     390           1 :   Widget _buildHelper() {
     391           2 :     assert(widget.helperText != null);
     392           1 :     return Semantics(
     393             :       container: true,
     394           1 :       child: Opacity(
     395           3 :         opacity: 1.0 - _controller.value,
     396           1 :         child: MongolText(
     397           2 :           widget.helperText!,
     398           2 :           style: widget.helperStyle,
     399           2 :           textAlign: widget.textAlign,
     400             :           overflow: TextOverflow.ellipsis,
     401           2 :           maxLines: widget.helperMaxLines,
     402             :         ),
     403             :       ),
     404             :     );
     405             :   }
     406             : 
     407           1 :   Widget _buildError() {
     408           2 :     assert(widget.errorText != null);
     409           1 :     return Semantics(
     410             :       container: true,
     411             :       liveRegion: true,
     412           1 :       child: Opacity(
     413           2 :         opacity: _controller.value,
     414           1 :         child: FractionalTranslation(
     415           1 :           translation: Tween<Offset>(
     416             :             begin: const Offset(-0.25, 0.0),
     417             :             end: const Offset(0.0, 0.0),
     418           3 :           ).evaluate(_controller.view),
     419           1 :           child: MongolText(
     420           2 :             widget.errorText!,
     421           2 :             style: widget.errorStyle,
     422           2 :             textAlign: widget.textAlign,
     423             :             overflow: TextOverflow.ellipsis,
     424           2 :             maxLines: widget.errorMaxLines,
     425             :           ),
     426             :         ),
     427             :       ),
     428             :     );
     429             :   }
     430             : 
     431           1 :   @override
     432             :   Widget build(BuildContext context) {
     433           2 :     if (_controller.isDismissed) {
     434           1 :       _error = null;
     435           2 :       if (widget.helperText != null) {
     436           2 :         return _helper = _buildHelper();
     437             :       } else {
     438           1 :         _helper = null;
     439             :         return empty;
     440             :       }
     441             :     }
     442             : 
     443           2 :     if (_controller.isCompleted) {
     444           1 :       _helper = null;
     445           2 :       if (widget.errorText != null) {
     446           2 :         return _error = _buildError();
     447             :       } else {
     448           0 :         _error = null;
     449             :         return empty;
     450             :       }
     451             :     }
     452             : 
     453           0 :     if (_helper == null && widget.errorText != null) return _buildError();
     454             : 
     455           0 :     if (_error == null && widget.helperText != null) return _buildHelper();
     456             : 
     457           0 :     if (widget.errorText != null) {
     458           0 :       return Stack(
     459           0 :         children: <Widget>[
     460           0 :           Opacity(
     461           0 :             opacity: 1.0 - _controller.value,
     462           0 :             child: _helper,
     463             :           ),
     464           0 :           _buildError(),
     465             :         ],
     466             :       );
     467             :     }
     468             : 
     469           0 :     if (widget.helperText != null) {
     470           0 :       return Stack(
     471           0 :         children: <Widget>[
     472           0 :           _buildHelper(),
     473           0 :           Opacity(
     474           0 :             opacity: _controller.value,
     475           0 :             child: _error,
     476             :           ),
     477             :         ],
     478             :       );
     479             :     }
     480             : 
     481             :     return empty;
     482             :   }
     483             : }
     484             : 
     485             : // Identifies the children of a _RenderDecorationElement.
     486           8 : enum _DecorationSlot {
     487             :   icon,
     488             :   input,
     489             :   label,
     490             :   hint,
     491             :   prefix,
     492             :   suffix,
     493             :   prefixIcon,
     494             :   suffixIcon,
     495             :   helperError,
     496             :   counter,
     497             :   container,
     498             : }
     499             : 
     500             : // An analog of InputDecoration for the _Decorator widget.
     501             : @immutable
     502             : class _Decoration {
     503           1 :   const _Decoration({
     504             :     required this.contentPadding,
     505             :     required this.isCollapsed,
     506             :     required this.floatingLabelWidth,
     507             :     required this.floatingLabelProgress,
     508             :     this.border,
     509             :     this.borderGap,
     510             :     required this.alignLabelWithHint,
     511             :     required this.isDense,
     512             :     this.visualDensity,
     513             :     this.icon,
     514             :     this.input,
     515             :     this.label,
     516             :     this.hint,
     517             :     this.prefix,
     518             :     this.suffix,
     519             :     this.prefixIcon,
     520             :     this.suffixIcon,
     521             :     this.helperError,
     522             :     this.counter,
     523             :     this.container,
     524             :     this.fixTextFieldOutlineLabel = false,
     525             :   });
     526             : 
     527             :   final EdgeInsetsGeometry contentPadding;
     528             :   final bool isCollapsed;
     529             :   final double floatingLabelWidth;
     530             :   final double floatingLabelProgress;
     531             :   final InputBorder? border;
     532             :   final _InputBorderGap? borderGap;
     533             :   final bool alignLabelWithHint;
     534             :   final bool? isDense;
     535             :   final VisualDensity? visualDensity;
     536             :   final Widget? icon;
     537             :   final Widget? input;
     538             :   final Widget? label;
     539             :   final Widget? hint;
     540             :   final Widget? prefix;
     541             :   final Widget? suffix;
     542             :   final Widget? prefixIcon;
     543             :   final Widget? suffixIcon;
     544             :   final Widget? helperError;
     545             :   final Widget? counter;
     546             :   final Widget? container;
     547             :   final bool fixTextFieldOutlineLabel;
     548             : 
     549           1 :   @override
     550             :   bool operator ==(Object other) {
     551             :     if (identical(this, other)) return true;
     552           3 :     if (other.runtimeType != runtimeType) return false;
     553           1 :     return other is _Decoration &&
     554           3 :         other.contentPadding == contentPadding &&
     555           3 :         other.isCollapsed == isCollapsed &&
     556           3 :         other.floatingLabelWidth == floatingLabelWidth &&
     557           3 :         other.floatingLabelProgress == floatingLabelProgress &&
     558           3 :         other.border == border &&
     559           3 :         other.borderGap == borderGap &&
     560           3 :         other.alignLabelWithHint == alignLabelWithHint &&
     561           3 :         other.isDense == isDense &&
     562           3 :         other.visualDensity == visualDensity &&
     563           3 :         other.icon == icon &&
     564           3 :         other.input == input &&
     565           3 :         other.label == label &&
     566           3 :         other.hint == hint &&
     567           3 :         other.prefix == prefix &&
     568           3 :         other.suffix == suffix &&
     569           3 :         other.prefixIcon == prefixIcon &&
     570           3 :         other.suffixIcon == suffixIcon &&
     571           3 :         other.helperError == helperError &&
     572           0 :         other.counter == counter &&
     573           0 :         other.container == container &&
     574           0 :         other.fixTextFieldOutlineLabel == fixTextFieldOutlineLabel;
     575             :   }
     576             : 
     577           0 :   @override
     578             :   int get hashCode {
     579           0 :     return hashValues(
     580           0 :       contentPadding,
     581           0 :       floatingLabelWidth,
     582           0 :       floatingLabelProgress,
     583           0 :       border,
     584           0 :       borderGap,
     585           0 :       alignLabelWithHint,
     586           0 :       isDense,
     587           0 :       visualDensity,
     588           0 :       icon,
     589           0 :       input,
     590           0 :       label,
     591           0 :       hint,
     592           0 :       prefix,
     593           0 :       suffix,
     594           0 :       prefixIcon,
     595           0 :       suffixIcon,
     596           0 :       helperError,
     597           0 :       counter,
     598           0 :       container,
     599           0 :       fixTextFieldOutlineLabel,
     600             :     );
     601             :   }
     602             : }
     603             : 
     604             : // A container for the layout values computed by _RenderDecoration._layout.
     605             : // These values are used by _RenderDecoration.performLayout to position
     606             : // all of the renderer children of a _RenderDecoration.
     607             : class _RenderDecorationLayout {
     608           1 :   const _RenderDecorationLayout({
     609             :     required this.boxToBaseline,
     610             :     required this.inputBaseline,
     611             :     required this.outlineBaseline,
     612             :     required this.subtextBaseline,
     613             :     required this.containerWidth,
     614             :     required this.subtextWidth,
     615             :   });
     616             : 
     617             :   final Map<RenderBox?, double> boxToBaseline;
     618             :   final double inputBaseline;
     619             :   final double outlineBaseline;
     620             :   final double subtextBaseline; // helper/error counter
     621             :   final double containerWidth;
     622             :   final double subtextWidth;
     623             : }
     624             : 
     625             : // The workhorse: layout and paint a _Decorator widget's _Decoration.
     626             : class _RenderDecoration extends RenderBox {
     627           1 :   _RenderDecoration({
     628             :     required _Decoration decoration,
     629             :     //required TextDirection textDirection,
     630             :     required TextBaseline textBaseline,
     631             :     required bool isFocused,
     632             :     required bool expands,
     633             :     TextAlignHorizontal? textAlignHorizontal,
     634             :   })  : _decoration = decoration,
     635             :         _textBaseline = textBaseline,
     636             :         _textAlignHorizontal = textAlignHorizontal,
     637             :         _isFocused = isFocused,
     638             :         _expands = expands;
     639             : 
     640             :   static const double subtextGap = 8.0;
     641             :   final Map<_DecorationSlot, RenderBox> children =
     642             :       <_DecorationSlot, RenderBox>{};
     643             : 
     644           1 :   RenderBox? _updateChild(
     645             :       RenderBox? oldChild, RenderBox? newChild, _DecorationSlot slot) {
     646             :     if (oldChild != null) {
     647           0 :       dropChild(oldChild);
     648           0 :       children.remove(slot);
     649             :     }
     650             :     if (newChild != null) {
     651           2 :       children[slot] = newChild;
     652           1 :       adoptChild(newChild);
     653             :     }
     654             :     return newChild;
     655             :   }
     656             : 
     657             :   RenderBox? _icon;
     658           2 :   RenderBox? get icon => _icon;
     659           1 :   set icon(RenderBox? value) {
     660           3 :     _icon = _updateChild(_icon, value, _DecorationSlot.icon);
     661             :   }
     662             : 
     663             :   RenderBox? _input;
     664           2 :   RenderBox? get input => _input;
     665           1 :   set input(RenderBox? value) {
     666           3 :     _input = _updateChild(_input, value, _DecorationSlot.input);
     667             :   }
     668             : 
     669             :   RenderBox? _label;
     670           2 :   RenderBox? get label => _label;
     671           1 :   set label(RenderBox? value) {
     672           3 :     _label = _updateChild(_label, value, _DecorationSlot.label);
     673             :   }
     674             : 
     675             :   RenderBox? _hint;
     676           2 :   RenderBox? get hint => _hint;
     677           1 :   set hint(RenderBox? value) {
     678           3 :     _hint = _updateChild(_hint, value, _DecorationSlot.hint);
     679             :   }
     680             : 
     681             :   RenderBox? _prefix;
     682           2 :   RenderBox? get prefix => _prefix;
     683           1 :   set prefix(RenderBox? value) {
     684           3 :     _prefix = _updateChild(_prefix, value, _DecorationSlot.prefix);
     685             :   }
     686             : 
     687             :   RenderBox? _suffix;
     688           2 :   RenderBox? get suffix => _suffix;
     689           1 :   set suffix(RenderBox? value) {
     690           3 :     _suffix = _updateChild(_suffix, value, _DecorationSlot.suffix);
     691             :   }
     692             : 
     693             :   RenderBox? _prefixIcon;
     694           2 :   RenderBox? get prefixIcon => _prefixIcon;
     695           0 :   set prefixIcon(RenderBox? value) {
     696           0 :     _prefixIcon = _updateChild(_prefixIcon, value, _DecorationSlot.prefixIcon);
     697             :   }
     698             : 
     699             :   RenderBox? _suffixIcon;
     700           2 :   RenderBox? get suffixIcon => _suffixIcon;
     701           1 :   set suffixIcon(RenderBox? value) {
     702           3 :     _suffixIcon = _updateChild(_suffixIcon, value, _DecorationSlot.suffixIcon);
     703             :   }
     704             : 
     705             :   RenderBox? _helperError;
     706           2 :   RenderBox? get helperError => _helperError;
     707           1 :   set helperError(RenderBox? value) {
     708           1 :     _helperError =
     709           2 :         _updateChild(_helperError, value, _DecorationSlot.helperError);
     710             :   }
     711             : 
     712             :   RenderBox? _counter;
     713           2 :   RenderBox? get counter => _counter;
     714           1 :   set counter(RenderBox? value) {
     715           3 :     _counter = _updateChild(_counter, value, _DecorationSlot.counter);
     716             :   }
     717             : 
     718             :   RenderBox? _container;
     719           2 :   RenderBox? get container => _container;
     720           1 :   set container(RenderBox? value) {
     721           3 :     _container = _updateChild(_container, value, _DecorationSlot.container);
     722             :   }
     723             : 
     724             :   // The returned list is ordered for hit testing.
     725             :   Iterable<RenderBox> get _children sync* {
     726             :     if (icon != null) yield icon!;
     727             :     if (input != null) yield input!;
     728             :     if (prefixIcon != null) yield prefixIcon!;
     729             :     if (suffixIcon != null) yield suffixIcon!;
     730             :     if (prefix != null) yield prefix!;
     731             :     if (suffix != null) yield suffix!;
     732             :     if (label != null) yield label!;
     733             :     if (hint != null) yield hint!;
     734             :     if (helperError != null) yield helperError!;
     735             :     if (counter != null) yield counter!;
     736             :     if (container != null) yield container!;
     737             :   }
     738             : 
     739           2 :   _Decoration get decoration => _decoration;
     740             :   _Decoration _decoration;
     741           1 :   set decoration(_Decoration value) {
     742           2 :     if (_decoration == value) return;
     743           1 :     _decoration = value;
     744           1 :     markNeedsLayout();
     745             :   }
     746             : 
     747           0 :   TextBaseline get textBaseline => _textBaseline;
     748             :   TextBaseline _textBaseline;
     749           1 :   set textBaseline(TextBaseline value) {
     750           2 :     if (_textBaseline == value) return;
     751           0 :     _textBaseline = value;
     752           0 :     markNeedsLayout();
     753             :   }
     754             : 
     755           1 :   TextAlignHorizontal get _defaultTextAlignHorizontal =>
     756           1 :       _isOutlineAligned ? TextAlignHorizontal.center : TextAlignHorizontal.left;
     757           1 :   TextAlignHorizontal? get textAlignHorizontal =>
     758           2 :       _textAlignHorizontal ?? _defaultTextAlignHorizontal;
     759             :   TextAlignHorizontal? _textAlignHorizontal;
     760           1 :   set textAlignHorizontal(TextAlignHorizontal? value) {
     761           2 :     if (_textAlignHorizontal == value) {
     762             :       return;
     763             :     }
     764             :     // No need to relayout if the effective value is still the same.
     765           0 :     if (textAlignHorizontal!.x == (value?.x ?? _defaultTextAlignHorizontal.x)) {
     766           0 :       _textAlignHorizontal = value;
     767             :       return;
     768             :     }
     769           0 :     _textAlignHorizontal = value;
     770           0 :     markNeedsLayout();
     771             :   }
     772             : 
     773           2 :   bool get isFocused => _isFocused;
     774             :   bool _isFocused;
     775           1 :   set isFocused(bool value) {
     776           2 :     if (_isFocused == value) return;
     777           1 :     _isFocused = value;
     778           1 :     markNeedsSemanticsUpdate();
     779             :   }
     780             : 
     781           2 :   bool get expands => _expands;
     782             :   bool _expands = false;
     783           1 :   set expands(bool value) {
     784           2 :     if (_expands == value) return;
     785           0 :     _expands = value;
     786           0 :     markNeedsLayout();
     787             :   }
     788             : 
     789             :   // Indicates that the decoration should be aligned to accommodate an outline
     790             :   // border.
     791           1 :   bool get _isOutlineAligned {
     792           5 :     return !decoration.isCollapsed && decoration.border!.isOutline;
     793             :   }
     794             : 
     795           1 :   @override
     796             :   void attach(PipelineOwner owner) {
     797           1 :     super.attach(owner);
     798           1 :     for (final child in _children) {
     799           0 :       child.attach(owner);
     800             :     }
     801             :   }
     802             : 
     803           1 :   @override
     804             :   void detach() {
     805           1 :     super.detach();
     806           2 :     for (final child in _children) {
     807           1 :       child.detach();
     808             :     }
     809             :   }
     810             : 
     811           1 :   @override
     812             :   void redepthChildren() {
     813           3 :     _children.forEach(redepthChild);
     814             :   }
     815             : 
     816           1 :   @override
     817             :   void visitChildren(RenderObjectVisitor visitor) {
     818           2 :     _children.forEach(visitor);
     819             :   }
     820             : 
     821           1 :   @override
     822             :   void visitChildrenForSemantics(RenderObjectVisitor visitor) {
     823           3 :     if (icon != null) visitor(icon!);
     824           3 :     if (prefix != null) visitor(prefix!);
     825           1 :     if (prefixIcon != null) visitor(prefixIcon!);
     826             : 
     827           1 :     if (label != null) {
     828           2 :       visitor(label!);
     829             :     }
     830           1 :     if (hint != null) {
     831           1 :       if (isFocused) {
     832           2 :         visitor(hint!);
     833           1 :       } else if (label == null) {
     834           2 :         visitor(hint!);
     835             :       }
     836             :     }
     837             : 
     838           3 :     if (input != null) visitor(input!);
     839           3 :     if (suffixIcon != null) visitor(suffixIcon!);
     840           3 :     if (suffix != null) visitor(suffix!);
     841           3 :     if (container != null) visitor(container!);
     842           3 :     if (helperError != null) visitor(helperError!);
     843           3 :     if (counter != null) visitor(counter!);
     844             :   }
     845             : 
     846           0 :   @override
     847             :   List<DiagnosticsNode> debugDescribeChildren() {
     848           0 :     final value = <DiagnosticsNode>[];
     849           0 :     void add(RenderBox? child, String name) {
     850           0 :       if (child != null) value.add(child.toDiagnosticsNode(name: name));
     851             :     }
     852             : 
     853           0 :     add(icon, 'icon');
     854           0 :     add(input, 'input');
     855           0 :     add(label, 'label');
     856           0 :     add(hint, 'hint');
     857           0 :     add(prefix, 'prefix');
     858           0 :     add(suffix, 'suffix');
     859           0 :     add(prefixIcon, 'prefixIcon');
     860           0 :     add(suffixIcon, 'suffixIcon');
     861           0 :     add(helperError, 'helperError');
     862           0 :     add(counter, 'counter');
     863           0 :     add(container, 'container');
     864             :     return value;
     865             :   }
     866             : 
     867           1 :   @override
     868             :   bool get sizedByParent => false;
     869             : 
     870           1 :   static double _minHeight(RenderBox? box, double width) {
     871           1 :     return box == null ? 0.0 : box.getMinIntrinsicHeight(width);
     872             :   }
     873             : 
     874           1 :   static double _maxHeight(RenderBox? box, double width) {
     875           1 :     return box == null ? 0.0 : box.getMaxIntrinsicHeight(width);
     876             :   }
     877             : 
     878           1 :   static double _minWidth(RenderBox? box, double height) {
     879           1 :     return box == null ? 0.0 : box.getMinIntrinsicWidth(height);
     880             :   }
     881             : 
     882           2 :   static Size _boxSize(RenderBox? box) => box == null ? Size.zero : box.size;
     883             : 
     884           1 :   static BoxParentData _boxParentData(RenderBox box) =>
     885           1 :       box.parentData! as BoxParentData;
     886             : 
     887           3 :   EdgeInsets get contentPadding => decoration.contentPadding as EdgeInsets;
     888             : 
     889             :   // Lay out the given box if needed, and return its baseline.
     890           1 :   double _layoutLineBox(RenderBox? box, BoxConstraints constraints) {
     891             :     if (box == null) {
     892             :       return 0.0;
     893             :     }
     894           1 :     box.layout(constraints, parentUsesSize: true);
     895             :     // Since internally, all layout is performed against the alphabetic baseline,
     896             :     // (eg, ascents/descents are all relative to alphabetic, even if the font is
     897             :     // an ideographic or hanging font), we should always obtain the reference
     898             :     // baseline from the alphabetic baseline. The ideographic baseline is for
     899             :     // use post-layout and is derived from the alphabetic baseline combined with
     900             :     // the font metrics.
     901           1 :     final baseline = box.getDistanceToBaseline(TextBaseline.alphabetic);
     902           1 :     assert(baseline != null && baseline >= 0.0);
     903             :     return baseline!;
     904             :   }
     905             : 
     906             :   // Returns a value used by performLayout to position all of the renderers.
     907             :   // This method applies layout to all of the renderers except the container.
     908             :   // For convenience, the container is laid out in performLayout().
     909           1 :   _RenderDecorationLayout _layout(BoxConstraints layoutConstraints) {
     910             :     assert(
     911           2 :       layoutConstraints.maxHeight < double.infinity,
     912             :       'A MongolInputDecorator, which is typically created by a MongolTextField, '
     913             :       'cannot have an unbounded height.\n'
     914             :       'This happens when the parent widget does not provide a finite height '
     915             :       'constraint. For example, if the MongolInputDecorator is contained by a Column, '
     916             :       'then its height must be constrained. An Expanded widget or a SizedBox '
     917             :       'can be used to constrain the height of the MongolInputDecorator or the '
     918             :       'MongolTextField that contains it.',
     919             :     );
     920             : 
     921             :     // Margin on each side of subtext (counter and helperError)
     922           1 :     final boxToBaseline = <RenderBox?, double>{};
     923           1 :     final boxConstraints = layoutConstraints.loosen();
     924             : 
     925             :     // Layout all the widgets used by MongolInputDecorator
     926           4 :     boxToBaseline[prefix] = _layoutLineBox(prefix, boxConstraints);
     927           4 :     boxToBaseline[suffix] = _layoutLineBox(suffix, boxConstraints);
     928           4 :     boxToBaseline[icon] = _layoutLineBox(icon, boxConstraints);
     929           4 :     boxToBaseline[prefixIcon] = _layoutLineBox(prefixIcon, boxConstraints);
     930           4 :     boxToBaseline[suffixIcon] = _layoutLineBox(suffixIcon, boxConstraints);
     931             : 
     932           1 :     final inputHeight = math.max(
     933             :         0.0,
     934           3 :         constraints.maxHeight -
     935           4 :             (_boxSize(icon).height +
     936           3 :                 contentPadding.top +
     937           4 :                 _boxSize(prefixIcon).height +
     938           4 :                 _boxSize(prefix).height +
     939           4 :                 _boxSize(suffix).height +
     940           4 :                 _boxSize(suffixIcon).height +
     941           2 :                 contentPadding.bottom));
     942             :     // Increase the available height for the label when it is scaled down.
     943           1 :     final invertedLabelScale = lerpDouble(
     944           3 :         1.00, 1 / _kFinalLabelScale, decoration.floatingLabelProgress)!;
     945           3 :     var suffixIconHeight = _boxSize(suffixIcon).height;
     946           3 :     if (decoration.border!.isOutline) {
     947             :       suffixIconHeight =
     948           0 :           lerpDouble(suffixIconHeight, 0.0, decoration.floatingLabelProgress)!;
     949             :     }
     950           1 :     final labelHeight = math.max(
     951             :         0.0,
     952           3 :         constraints.maxHeight -
     953           4 :             (_boxSize(icon).height +
     954           3 :                 contentPadding.top +
     955           4 :                 _boxSize(prefixIcon).height +
     956           1 :                 suffixIconHeight +
     957           2 :                 contentPadding.bottom));
     958           3 :     boxToBaseline[label] = _layoutLineBox(
     959           1 :       label,
     960           2 :       boxConstraints.copyWith(maxHeight: labelHeight * invertedLabelScale),
     961             :     );
     962           3 :     boxToBaseline[hint] = _layoutLineBox(
     963           1 :       hint,
     964           1 :       boxConstraints.copyWith(minHeight: inputHeight, maxHeight: inputHeight),
     965             :     );
     966           4 :     boxToBaseline[counter] = _layoutLineBox(counter, boxConstraints);
     967             : 
     968             :     // The helper or error text can occupy the full height less the space
     969             :     // occupied by the icon and counter.
     970           3 :     boxToBaseline[helperError] = _layoutLineBox(
     971           1 :       helperError,
     972           1 :       boxConstraints.copyWith(
     973           1 :         maxWidth: math.max(
     974             :           0.0,
     975           2 :           boxConstraints.maxHeight -
     976           4 :               _boxSize(icon).height -
     977           4 :               _boxSize(counter).height -
     978           2 :               contentPadding.vertical,
     979             :         ),
     980             :       ),
     981             :     );
     982             : 
     983             :     // The width of the input needs to accommodate label to the left and counter and
     984             :     // helperError to the right, when they exist.
     985           3 :     final labelWidth = label == null ? 0 : decoration.floatingLabelWidth;
     986           3 :     final leftWidth = decoration.border!.isOutline
     987           0 :         ? math.max(labelWidth - boxToBaseline[label]!, 0)
     988             :         : labelWidth;
     989             :     final counterWidth =
     990           4 :         counter == null ? 0 : boxToBaseline[counter]! + subtextGap;
     991             :     final helperErrorExists =
     992           6 :         helperError?.size != null && helperError!.size.width > 0;
     993             :     final double helperErrorWidth =
     994           4 :         !helperErrorExists ? 0 : helperError!.size.width + subtextGap;
     995           1 :     final rightWidth = math.max(
     996             :       counterWidth,
     997             :       helperErrorWidth,
     998             :     );
     999           3 :     final densityOffset = decoration.visualDensity!.baseSizeAdjustment;
    1000           3 :     boxToBaseline[input] = _layoutLineBox(
    1001           1 :       input,
    1002             :       boxConstraints
    1003           2 :           .deflate(EdgeInsets.only(
    1004           6 :             left: contentPadding.left + leftWidth + densityOffset.dx / 2,
    1005           6 :             right: contentPadding.right + rightWidth + densityOffset.dx / 2,
    1006             :           ))
    1007           1 :           .copyWith(
    1008             :             minHeight: inputHeight,
    1009             :             maxHeight: inputHeight,
    1010             :           ),
    1011             :     );
    1012             : 
    1013             :     // The field can be occupied by a hint or by the input itself
    1014           4 :     final hintWidth = hint == null ? 0 : hint!.size.width;
    1015           4 :     final inputDirectWidth = input == null ? 0 : input!.size.width;
    1016           1 :     final inputWidth = math.max(hintWidth, inputDirectWidth);
    1017           1 :     final inputInternalBaseline = math.max(
    1018           2 :       boxToBaseline[input]!,
    1019           2 :       boxToBaseline[hint]!,
    1020             :     );
    1021             : 
    1022             :     // Calculate the amount that prefix/suffix affects width to the left and right of
    1023             :     // the input.
    1024           3 :     final prefixWidth = prefix?.size.width ?? 0;
    1025           3 :     final suffixWidth = suffix?.size.width ?? 0;
    1026           1 :     final fixWidth = math.max(
    1027           2 :       boxToBaseline[prefix]!,
    1028           2 :       boxToBaseline[suffix]!,
    1029             :     );
    1030           2 :     final fixLeftOfInput = math.max(0, fixWidth - inputInternalBaseline);
    1031           1 :     final fixRightOfBaseline = math.max(
    1032           3 :       prefixWidth - boxToBaseline[prefix]!,
    1033           3 :       suffixWidth - boxToBaseline[suffix]!,
    1034             :     );
    1035           1 :     final fixRightOfInput = math.max(
    1036             :       0,
    1037           2 :       fixRightOfBaseline - (inputWidth - inputInternalBaseline),
    1038             :     );
    1039             : 
    1040             :     // Calculate the width of the input text container.
    1041             :     final double prefixIconWidth =
    1042           1 :         prefixIcon == null ? 0 : prefixIcon!.size.width;
    1043             :     final double suffixIconWidth =
    1044           4 :         suffixIcon == null ? 0 : suffixIcon!.size.width;
    1045           1 :     final double fixIconWidth = math.max(prefixIconWidth, suffixIconWidth);
    1046           1 :     final double contentWidth = math.max(
    1047             :       fixIconWidth,
    1048           1 :       leftWidth +
    1049           3 :           contentPadding.left +
    1050           1 :           fixLeftOfInput +
    1051           1 :           inputWidth +
    1052           1 :           fixRightOfInput +
    1053           3 :           contentPadding.right +
    1054           1 :           densityOffset.dy,
    1055             :     );
    1056             :     final minContainerWidth =
    1057           5 :         decoration.isDense! || decoration.isCollapsed || expands
    1058             :             ? 0.0
    1059             :             : kMinInteractiveDimension;
    1060           2 :     final maxContainerWidth = boxConstraints.maxWidth - rightWidth;
    1061           1 :     final double containerWidth = expands
    1062             :         ? maxContainerWidth
    1063           1 :         : math.min(
    1064           1 :             math.max(contentWidth, minContainerWidth), maxContainerWidth);
    1065             : 
    1066             :     // Ensure the text is horizontally centered in cases where the content is
    1067             :     // shorter than kMinInteractiveDimension.
    1068           1 :     final interactiveAdjustment = minContainerWidth > contentWidth
    1069           2 :         ? (minContainerWidth - contentWidth) / 2.0
    1070             :         : 0.0;
    1071             : 
    1072             :     // Try to consider the prefix/suffix as part of the text when aligning it.
    1073             :     // If the prefix/suffix overflows however, allow it to extend outside of the
    1074             :     // input and align the remaining part of the text and prefix/suffix.
    1075           2 :     final overflow = math.max(0, contentWidth - maxContainerWidth);
    1076             :     // Map textAlignHorizontal from -1:1 to 0:1 so that it can be used to scale
    1077             :     // the baseline from its minimum to maximum values.
    1078           4 :     final textAlignHorizontalFactor = (textAlignHorizontal!.x + 1.0) / 2.0;
    1079             :     // Adjust to try to fit left overflow inside the input on an inverse scale of
    1080             :     // textAlignHorizontal, so that left aligned text adjusts the most and right
    1081             :     // aligned text doesn't adjust at all.
    1082             :     final baselineAdjustment =
    1083           3 :         fixLeftOfInput - overflow * (1 - textAlignHorizontalFactor);
    1084             : 
    1085             :     // The baselines that will be used to draw the actual input text content.
    1086           3 :     final leftInputBaseline = contentPadding.left +
    1087           1 :         leftWidth +
    1088           1 :         inputInternalBaseline +
    1089           1 :         baselineAdjustment +
    1090             :         interactiveAdjustment;
    1091             :     final maxContentWidth =
    1092           7 :         containerWidth - contentPadding.left - leftWidth - contentPadding.right;
    1093           2 :     final alignableWidth = fixLeftOfInput + inputWidth + fixRightOfInput;
    1094           1 :     final maxHorizontalOffset = maxContentWidth - alignableWidth;
    1095             :     final textAlignHorizontalOffset =
    1096           1 :         maxHorizontalOffset * textAlignHorizontalFactor;
    1097             :     final inputBaseline =
    1098           4 :         leftInputBaseline + textAlignHorizontalOffset + densityOffset.dx / 2.0;
    1099             : 
    1100             :     // The three main alignments for the baseline when an outline is present are
    1101             :     //
    1102             :     //  * left (-1.0): leftmost point considering padding.
    1103             :     //  * center (0.0): the absolute center of the input ignoring padding but
    1104             :     //      accommodating the border and floating label.
    1105             :     //  * right (1.0): rightmost point considering padding.
    1106             :     //
    1107             :     // That means that if the padding is uneven, center is not the exact
    1108             :     // midpoint of left and right. To account for this, the left of center and
    1109             :     // right of center alignments are interpolated independently.
    1110           1 :     final outlineCenterBaseline = inputInternalBaseline +
    1111           2 :         baselineAdjustment / 2.0 +
    1112           3 :         (containerWidth - (2.0 + inputWidth)) / 2.0;
    1113             :     final outlineLeftBaseline = leftInputBaseline;
    1114           1 :     final outlineRightBaseline = leftInputBaseline + maxHorizontalOffset;
    1115           1 :     final outlineBaseline = _interpolateThree(
    1116             :       outlineLeftBaseline,
    1117             :       outlineCenterBaseline,
    1118             :       outlineRightBaseline,
    1119           1 :       textAlignHorizontal!,
    1120             :     );
    1121             : 
    1122             :     // Find the positions of the text to the left of the input when it exists.
    1123             :     var subtextCounterBaseline = 0.0;
    1124             :     var subtextHelperBaseline = 0.0;
    1125             :     var subtextCounterWidth = 0.0;
    1126             :     var subtextHelperWidth = 0.0;
    1127           1 :     if (counter != null) {
    1128             :       subtextCounterBaseline =
    1129           4 :           containerWidth + subtextGap + boxToBaseline[counter]!;
    1130           4 :       subtextCounterWidth = counter!.size.width + subtextGap;
    1131             :     }
    1132             :     if (helperErrorExists) {
    1133             :       subtextHelperBaseline =
    1134           4 :           containerWidth + subtextGap + boxToBaseline[helperError]!;
    1135             :       subtextHelperWidth = helperErrorWidth;
    1136             :     }
    1137           1 :     final subtextBaseline = math.max(
    1138             :       subtextCounterBaseline,
    1139             :       subtextHelperBaseline,
    1140             :     );
    1141           1 :     final subtextWidth = math.max(
    1142             :       subtextCounterWidth,
    1143             :       subtextHelperWidth,
    1144             :     );
    1145             : 
    1146           1 :     return _RenderDecorationLayout(
    1147             :       boxToBaseline: boxToBaseline,
    1148             :       containerWidth: containerWidth,
    1149             :       inputBaseline: inputBaseline,
    1150             :       outlineBaseline: outlineBaseline,
    1151             :       subtextBaseline: subtextBaseline,
    1152             :       subtextWidth: subtextWidth,
    1153             :     );
    1154             :   }
    1155             : 
    1156             :   // Interpolate between three stops using textAlignHorizontal. This is used to
    1157             :   // calculate the outline baseline, which ignores padding when the alignment is
    1158             :   // middle. When the alignment is less than zero, it interpolates between the
    1159             :   // centered text box's left side and the left of the content padding. When the
    1160             :   // alignment is greater than zero, it interpolates between the centered box's
    1161             :   // left and the position that would align the right side of the box with the right
    1162             :   // padding.
    1163           1 :   double _interpolateThree(double begin, double middle, double end,
    1164             :       TextAlignHorizontal textAlignHorizontal) {
    1165           2 :     if (textAlignHorizontal.x <= 0) {
    1166             :       // It's possible for begin, middle, and end to not be in order because of
    1167             :       // excessive padding. Those cases are handled by using middle.
    1168           1 :       if (begin >= middle) {
    1169             :         return middle;
    1170             :       }
    1171             :       // Do a standard linear interpolation on the first half, between begin and
    1172             :       // middle.
    1173           2 :       final t = textAlignHorizontal.x + 1;
    1174           3 :       return begin + (middle - begin) * t;
    1175             :     }
    1176             : 
    1177           0 :     if (middle >= end) {
    1178             :       return middle;
    1179             :     }
    1180             :     // Do a standard linear interpolation on the second half, between middle and
    1181             :     // end.
    1182           0 :     final t = textAlignHorizontal.x;
    1183           0 :     return middle + (end - middle) * t;
    1184             :   }
    1185             : 
    1186           1 :   @override
    1187             :   double computeMinIntrinsicHeight(double width) {
    1188           3 :     return _minHeight(icon, width) +
    1189           3 :         contentPadding.top +
    1190           3 :         _minHeight(prefixIcon, width) +
    1191           3 :         _minHeight(prefix, width) +
    1192           6 :         math.max(_minHeight(input, width), _minHeight(hint, width)) +
    1193           3 :         _minHeight(suffix, width) +
    1194           3 :         _minHeight(suffixIcon, width) +
    1195           2 :         contentPadding.bottom;
    1196             :   }
    1197             : 
    1198           1 :   @override
    1199             :   double computeMaxIntrinsicHeight(double width) {
    1200           3 :     return _maxHeight(icon, width) +
    1201           3 :         contentPadding.top +
    1202           3 :         _maxHeight(prefixIcon, width) +
    1203           3 :         _maxHeight(prefix, width) +
    1204           6 :         math.max(_maxHeight(input, width), _maxHeight(hint, width)) +
    1205           3 :         _maxHeight(suffix, width) +
    1206           3 :         _maxHeight(suffixIcon, width) +
    1207           2 :         contentPadding.bottom;
    1208             :   }
    1209             : 
    1210           1 :   double _lineWidth(double height, List<RenderBox?> boxes) {
    1211             :     var width = 0.0;
    1212           2 :     for (final box in boxes) {
    1213             :       if (box == null) continue;
    1214           2 :       width = math.max(_minWidth(box, height), width);
    1215             :     }
    1216             :     return width;
    1217             :   }
    1218             : 
    1219           1 :   @override
    1220             :   double computeMinIntrinsicWidth(double height) {
    1221           4 :     var subtextWidth = _lineWidth(height, <RenderBox?>[helperError, counter]);
    1222           2 :     if (subtextWidth > 0.0) subtextWidth += subtextGap;
    1223           3 :     final densityOffset = decoration.visualDensity!.baseSizeAdjustment;
    1224           3 :     final containerWidth = contentPadding.left +
    1225           4 :         (label == null ? 0.0 : decoration.floatingLabelWidth) +
    1226           6 :         _lineWidth(height, <RenderBox?>[prefix, input, suffix]) +
    1227           1 :         subtextWidth +
    1228           3 :         contentPadding.right +
    1229           1 :         densityOffset.dx;
    1230             :     final minContainerWidth =
    1231           3 :         decoration.isDense! || expands ? 0.0 : kMinInteractiveDimension;
    1232           1 :     return math.max(containerWidth, minContainerWidth);
    1233             :   }
    1234             : 
    1235           1 :   @override
    1236             :   double computeMaxIntrinsicWidth(double height) {
    1237           1 :     return computeMinIntrinsicWidth(height);
    1238             :   }
    1239             : 
    1240           0 :   @override
    1241             :   double computeDistanceToActualBaseline(TextBaseline baseline) {
    1242           0 :     return _boxParentData(input!).offset.dx +
    1243           0 :         input!.computeDistanceToActualBaseline(baseline)!;
    1244             :   }
    1245             : 
    1246             :   // Records where the label was painted.
    1247             :   Matrix4? _labelTransform;
    1248             : 
    1249           1 :   @override
    1250             :   Size computeDryLayout(BoxConstraints constraints) {
    1251           1 :     assert(debugCannotComputeDryLayout(
    1252             :       reason:
    1253             :           'Layout requires baseline metrics, which are only available after a full layout.',
    1254             :     ));
    1255             :     return const Size(0, 0);
    1256             :   }
    1257             : 
    1258           1 :   @override
    1259             :   void performLayout() {
    1260           1 :     final constraints = this.constraints;
    1261           1 :     _labelTransform = null;
    1262           1 :     final layout = _layout(constraints);
    1263             : 
    1264           1 :     final overallHeight = constraints.maxHeight;
    1265           3 :     final overallWidth = layout.containerWidth + layout.subtextWidth;
    1266             : 
    1267           1 :     if (container != null) {
    1268           1 :       final containerConstraints = BoxConstraints.tightFor(
    1269           1 :         width: layout.containerWidth,
    1270           4 :         height: overallHeight - _boxSize(icon).height,
    1271             :       );
    1272           2 :       container!.layout(containerConstraints, parentUsesSize: true);
    1273           3 :       final y = _boxSize(icon).height;
    1274           4 :       _boxParentData(container!).offset = Offset(0.0, y);
    1275             :     }
    1276             : 
    1277             :     double? width;
    1278           1 :     double centerLayout(RenderBox box, double y) {
    1279           7 :       _boxParentData(box).offset = Offset((width! - box.size.width) / 2.0, y);
    1280           2 :       return box.size.height;
    1281             :     }
    1282             : 
    1283             :     double? baseline;
    1284           1 :     double baselineLayout(RenderBox box, double y) {
    1285           2 :       _boxParentData(box).offset =
    1286           4 :           Offset(baseline! - layout.boxToBaseline[box]!, y);
    1287           2 :       return box.size.height;
    1288             :     }
    1289             : 
    1290           2 :     final top = contentPadding.top;
    1291           3 :     final bottom = overallHeight - contentPadding.bottom;
    1292             : 
    1293           1 :     width = layout.containerWidth;
    1294             :     baseline =
    1295           2 :         _isOutlineAligned ? layout.outlineBaseline : layout.inputBaseline;
    1296             : 
    1297           1 :     if (icon != null) {
    1298             :       const y = 0.0;
    1299           2 :       centerLayout(icon!, y);
    1300             :     }
    1301             : 
    1302           4 :     var start = top + _boxSize(icon).height;
    1303             :     var end = bottom;
    1304           1 :     if (prefixIcon != null) {
    1305           0 :       start -= contentPadding.top;
    1306           0 :       start += centerLayout(prefixIcon!, start);
    1307             :     }
    1308           1 :     if (label != null) {
    1309           2 :       if (decoration.alignLabelWithHint) {
    1310           0 :         baselineLayout(label!, start);
    1311             :       } else {
    1312           2 :         centerLayout(label!, start);
    1313             :       }
    1314             :     }
    1315           4 :     if (prefix != null) start += baselineLayout(prefix!, start);
    1316           3 :     if (input != null) baselineLayout(input!, start);
    1317           3 :     if (hint != null) baselineLayout(hint!, start);
    1318           1 :     if (suffixIcon != null) {
    1319           3 :       end += contentPadding.bottom;
    1320           7 :       end -= centerLayout(suffixIcon!, end - suffixIcon!.size.height);
    1321             :     }
    1322           1 :     if (suffix != null) {
    1323           7 :       end -= baselineLayout(suffix!, end - suffix!.size.height);
    1324             :     }
    1325             : 
    1326           1 :     if (helperError != null || counter != null) {
    1327           1 :       width = layout.subtextWidth;
    1328           1 :       baseline = layout.subtextBaseline;
    1329             : 
    1330           1 :       if (helperError != null) {
    1331           6 :         baselineLayout(helperError!, top + _boxSize(icon).height);
    1332             :       }
    1333           1 :       if (counter != null) {
    1334           6 :         baselineLayout(counter!, bottom - counter!.size.height);
    1335             :       }
    1336             :     }
    1337             : 
    1338           1 :     if (label != null) {
    1339           4 :       final labelY = _boxParentData(label!).offset.dy;
    1340             :       // The value of _InputBorderGap.start is relative to the origin of the
    1341             :       // _BorderContainer which is inset by the icon's height.
    1342           7 :       decoration.borderGap!.start = labelY - _boxSize(icon).height;
    1343           7 :       decoration.borderGap!.extent = label!.size.height * 0.75;
    1344             :     } else {
    1345           3 :       decoration.borderGap!.start = null;
    1346           3 :       decoration.borderGap!.extent = 0.0;
    1347             :     }
    1348             : 
    1349           3 :     size = constraints.constrain(Size(overallWidth, overallHeight));
    1350           4 :     assert(size.height == constraints.constrainHeight(overallHeight));
    1351           4 :     assert(size.width == constraints.constrainWidth(overallWidth));
    1352             :   }
    1353             : 
    1354           1 :   void _paintLabel(PaintingContext context, Offset offset) {
    1355           2 :     context.paintChild(label!, offset);
    1356             :   }
    1357             : 
    1358           1 :   @override
    1359             :   void paint(PaintingContext context, Offset offset) {
    1360           1 :     void doPaint(RenderBox? child) {
    1361             :       if (child != null) {
    1362           4 :         context.paintChild(child, _boxParentData(child).offset + offset);
    1363             :       }
    1364             :     }
    1365             : 
    1366           2 :     doPaint(container);
    1367             : 
    1368           1 :     if (label != null) {
    1369           3 :       final labelOffset = _boxParentData(label!).offset;
    1370           3 :       final labelWidth = label!.size.width;
    1371           4 :       final borderWeight = decoration.border!.borderSide.width;
    1372           2 :       final t = decoration.floatingLabelProgress;
    1373             :       // The center of the outline border label ends up a little below the
    1374             :       // center of the top border line.
    1375             :       final isOutlineBorder =
    1376           5 :           decoration.border != null && decoration.border!.isOutline;
    1377             :       // Temporary opt-in fix for https://github.com/flutter/flutter/issues/54028
    1378             :       // Center the scaled label relative to the border.
    1379           2 :       final floatingX = decoration.fixTextFieldOutlineLabel
    1380             :           ? isOutlineBorder
    1381           0 :               ? (-labelWidth * _kFinalLabelScale) / 2.0 + borderWeight / 2.0
    1382           0 :               : contentPadding.left
    1383             :           : isOutlineBorder
    1384           0 :               ? -labelWidth * 0.25
    1385           2 :               : contentPadding.left;
    1386           1 :       final scale = lerpDouble(1.0, _kFinalLabelScale, t)!;
    1387           1 :       final dy = labelOffset.dy;
    1388           3 :       final dx = lerpDouble(0.0, floatingX - labelOffset.dx, t)!;
    1389           2 :       _labelTransform = Matrix4.identity()
    1390           3 :         ..translate(labelOffset.dx + dx, dy)
    1391           1 :         ..scale(scale);
    1392           2 :       _transformLayer = context.pushTransform(
    1393           3 :           needsCompositing, offset, _labelTransform!, _paintLabel,
    1394           1 :           oldLayer: _transformLayer);
    1395             :     } else {
    1396           1 :       _transformLayer = null;
    1397             :     }
    1398             : 
    1399           2 :     doPaint(icon);
    1400           2 :     doPaint(prefix);
    1401           2 :     doPaint(suffix);
    1402           2 :     doPaint(prefixIcon);
    1403           2 :     doPaint(suffixIcon);
    1404           2 :     doPaint(hint);
    1405           2 :     doPaint(input);
    1406           2 :     doPaint(helperError);
    1407           2 :     doPaint(counter);
    1408             :   }
    1409             : 
    1410             :   TransformLayer? _transformLayer;
    1411             : 
    1412           1 :   @override
    1413             :   bool hitTestSelf(Offset position) => true;
    1414             : 
    1415           1 :   @override
    1416             :   bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
    1417           2 :     for (final child in _children) {
    1418             :       // The label must be handled specially since we've transformed it.
    1419           2 :       final offset = _boxParentData(child).offset;
    1420           1 :       final isHit = result.addWithPaintOffset(
    1421             :         offset: offset,
    1422             :         position: position,
    1423           1 :         hitTest: (BoxHitTestResult result, Offset transformed) {
    1424           2 :           assert(transformed == position - offset);
    1425           1 :           return child.hitTest(result, position: transformed);
    1426             :         },
    1427             :       );
    1428             :       if (isHit) return true;
    1429             :     }
    1430             :     return false;
    1431             :   }
    1432             : 
    1433           1 :   @override
    1434             :   void applyPaintTransform(RenderObject child, Matrix4 transform) {
    1435           3 :     if (child == label && _labelTransform != null) {
    1436           3 :       final labelOffset = _boxParentData(label!).offset;
    1437             :       transform
    1438           2 :         ..multiply(_labelTransform!)
    1439           5 :         ..translate(-labelOffset.dx, -labelOffset.dy);
    1440             :     }
    1441           1 :     super.applyPaintTransform(child, transform);
    1442             :   }
    1443             : }
    1444             : 
    1445             : class _DecorationElement extends RenderObjectElement {
    1446           2 :   _DecorationElement(_Decorator widget) : super(widget);
    1447             : 
    1448             :   final Map<_DecorationSlot, Element> slotToChild =
    1449             :       <_DecorationSlot, Element>{};
    1450             : 
    1451           1 :   @override
    1452           1 :   _Decorator get widget => super.widget as _Decorator;
    1453             : 
    1454           1 :   @override
    1455           1 :   _RenderDecoration get renderObject => super.renderObject as _RenderDecoration;
    1456             : 
    1457           1 :   @override
    1458             :   void visitChildren(ElementVisitor visitor) {
    1459           3 :     slotToChild.values.forEach(visitor);
    1460             :   }
    1461             : 
    1462           0 :   @override
    1463             :   void forgetChild(Element child) {
    1464           0 :     assert(slotToChild.containsValue(child));
    1465           0 :     assert(child.slot is _DecorationSlot);
    1466           0 :     assert(slotToChild.containsKey(child.slot));
    1467           0 :     slotToChild.remove(child.slot);
    1468           0 :     super.forgetChild(child);
    1469             :   }
    1470             : 
    1471           1 :   void _mountChild(Widget? widget, _DecorationSlot slot) {
    1472           2 :     final oldChild = slotToChild[slot];
    1473           1 :     final newChild = updateChild(oldChild, widget, slot);
    1474             :     if (oldChild != null) {
    1475           0 :       slotToChild.remove(slot);
    1476             :     }
    1477             :     if (newChild != null) {
    1478           2 :       slotToChild[slot] = newChild;
    1479             :     }
    1480             :   }
    1481             : 
    1482           1 :   @override
    1483             :   void mount(Element? parent, dynamic newSlot) {
    1484           1 :     super.mount(parent, newSlot);
    1485           4 :     _mountChild(widget.decoration.icon, _DecorationSlot.icon);
    1486           4 :     _mountChild(widget.decoration.input, _DecorationSlot.input);
    1487           4 :     _mountChild(widget.decoration.label, _DecorationSlot.label);
    1488           4 :     _mountChild(widget.decoration.hint, _DecorationSlot.hint);
    1489           4 :     _mountChild(widget.decoration.prefix, _DecorationSlot.prefix);
    1490           4 :     _mountChild(widget.decoration.suffix, _DecorationSlot.suffix);
    1491           4 :     _mountChild(widget.decoration.prefixIcon, _DecorationSlot.prefixIcon);
    1492           4 :     _mountChild(widget.decoration.suffixIcon, _DecorationSlot.suffixIcon);
    1493           4 :     _mountChild(widget.decoration.helperError, _DecorationSlot.helperError);
    1494           4 :     _mountChild(widget.decoration.counter, _DecorationSlot.counter);
    1495           4 :     _mountChild(widget.decoration.container, _DecorationSlot.container);
    1496             :   }
    1497             : 
    1498           1 :   void _updateChild(Widget? widget, _DecorationSlot slot) {
    1499           2 :     final oldChild = slotToChild[slot];
    1500           1 :     final newChild = updateChild(oldChild, widget, slot);
    1501             :     if (oldChild != null) {
    1502           2 :       slotToChild.remove(slot);
    1503             :     }
    1504             :     if (newChild != null) {
    1505           2 :       slotToChild[slot] = newChild;
    1506             :     }
    1507             :   }
    1508             : 
    1509           1 :   @override
    1510             :   void update(_Decorator newWidget) {
    1511           1 :     super.update(newWidget);
    1512           2 :     assert(widget == newWidget);
    1513           4 :     _updateChild(widget.decoration.icon, _DecorationSlot.icon);
    1514           4 :     _updateChild(widget.decoration.input, _DecorationSlot.input);
    1515           4 :     _updateChild(widget.decoration.label, _DecorationSlot.label);
    1516           4 :     _updateChild(widget.decoration.hint, _DecorationSlot.hint);
    1517           4 :     _updateChild(widget.decoration.prefix, _DecorationSlot.prefix);
    1518           4 :     _updateChild(widget.decoration.suffix, _DecorationSlot.suffix);
    1519           4 :     _updateChild(widget.decoration.prefixIcon, _DecorationSlot.prefixIcon);
    1520           4 :     _updateChild(widget.decoration.suffixIcon, _DecorationSlot.suffixIcon);
    1521           4 :     _updateChild(widget.decoration.helperError, _DecorationSlot.helperError);
    1522           4 :     _updateChild(widget.decoration.counter, _DecorationSlot.counter);
    1523           4 :     _updateChild(widget.decoration.container, _DecorationSlot.container);
    1524             :   }
    1525             : 
    1526           1 :   void _updateRenderObject(RenderBox? child, _DecorationSlot slot) {
    1527             :     switch (slot) {
    1528           1 :       case _DecorationSlot.icon:
    1529           2 :         renderObject.icon = child;
    1530             :         break;
    1531           1 :       case _DecorationSlot.input:
    1532           2 :         renderObject.input = child;
    1533             :         break;
    1534           1 :       case _DecorationSlot.label:
    1535           2 :         renderObject.label = child;
    1536             :         break;
    1537           1 :       case _DecorationSlot.hint:
    1538           2 :         renderObject.hint = child;
    1539             :         break;
    1540           1 :       case _DecorationSlot.prefix:
    1541           2 :         renderObject.prefix = child;
    1542             :         break;
    1543           1 :       case _DecorationSlot.suffix:
    1544           2 :         renderObject.suffix = child;
    1545             :         break;
    1546           1 :       case _DecorationSlot.prefixIcon:
    1547           0 :         renderObject.prefixIcon = child;
    1548             :         break;
    1549           1 :       case _DecorationSlot.suffixIcon:
    1550           2 :         renderObject.suffixIcon = child;
    1551             :         break;
    1552           1 :       case _DecorationSlot.helperError:
    1553           2 :         renderObject.helperError = child;
    1554             :         break;
    1555           1 :       case _DecorationSlot.counter:
    1556           2 :         renderObject.counter = child;
    1557             :         break;
    1558           1 :       case _DecorationSlot.container:
    1559           2 :         renderObject.container = child;
    1560             :         break;
    1561             :     }
    1562             :   }
    1563             : 
    1564           1 :   @override
    1565             :   void insertRenderObjectChild(RenderObject child, _DecorationSlot slot) {
    1566           1 :     assert(child is RenderBox);
    1567           1 :     _updateRenderObject(child as RenderBox, slot);
    1568           4 :     assert(renderObject.children.keys.contains(slot));
    1569             :   }
    1570             : 
    1571           0 :   @override
    1572             :   void removeRenderObjectChild(RenderObject child, _DecorationSlot slot) {
    1573           0 :     assert(child is RenderBox);
    1574           0 :     assert(renderObject.children[slot] == child);
    1575           0 :     _updateRenderObject(null, slot);
    1576           0 :     assert(!renderObject.children.keys.contains(slot));
    1577             :   }
    1578             : 
    1579           0 :   @override
    1580             :   void moveRenderObjectChild(
    1581             :       RenderObject child, dynamic oldSlot, dynamic newSlot) {
    1582           0 :     assert(false, 'not reachable');
    1583             :   }
    1584             : }
    1585             : 
    1586             : class _Decorator extends RenderObjectWidget {
    1587           1 :   const _Decorator({
    1588             :     Key? key,
    1589             :     required this.textAlignHorizontal,
    1590             :     required this.decoration,
    1591             :     required this.textBaseline,
    1592             :     required this.isFocused,
    1593             :     required this.expands,
    1594           1 :   }) : super(key: key);
    1595             : 
    1596             :   final _Decoration decoration;
    1597             :   final TextBaseline textBaseline;
    1598             :   final TextAlignHorizontal? textAlignHorizontal;
    1599             :   final bool isFocused;
    1600             :   final bool expands;
    1601             : 
    1602           1 :   @override
    1603           1 :   _DecorationElement createElement() => _DecorationElement(this);
    1604             : 
    1605           1 :   @override
    1606             :   _RenderDecoration createRenderObject(BuildContext context) {
    1607           1 :     return _RenderDecoration(
    1608           1 :       decoration: decoration,
    1609           1 :       textBaseline: textBaseline,
    1610           1 :       textAlignHorizontal: textAlignHorizontal,
    1611           1 :       isFocused: isFocused,
    1612           1 :       expands: expands,
    1613             :     );
    1614             :   }
    1615             : 
    1616           1 :   @override
    1617             :   void updateRenderObject(
    1618             :       BuildContext context, _RenderDecoration renderObject) {
    1619             :     renderObject
    1620           2 :       ..decoration = decoration
    1621           2 :       ..expands = expands
    1622           2 :       ..isFocused = isFocused
    1623           2 :       ..textAlignHorizontal = textAlignHorizontal
    1624           2 :       ..textBaseline = textBaseline;
    1625             :   }
    1626             : }
    1627             : 
    1628             : class _AffixText extends StatelessWidget {
    1629           1 :   const _AffixText({
    1630             :     required this.labelIsFloating,
    1631             :     this.text,
    1632             :     this.style,
    1633             :     this.child,
    1634             :   });
    1635             : 
    1636             :   final bool labelIsFloating;
    1637             :   final String? text;
    1638             :   final TextStyle? style;
    1639             :   final Widget? child;
    1640             : 
    1641           1 :   @override
    1642             :   Widget build(BuildContext context) {
    1643           1 :     return DefaultTextStyle.merge(
    1644           1 :       style: style,
    1645           1 :       child: AnimatedOpacity(
    1646             :         duration: _kTransitionDuration,
    1647             :         curve: _kTransitionCurve,
    1648           1 :         opacity: labelIsFloating ? 1.0 : 0.0,
    1649           1 :         child: child ??
    1650           1 :             (text == null
    1651             :                 ? null
    1652           1 :                 : MongolText(
    1653           1 :                     text!,
    1654           1 :                     style: style,
    1655             :                   )),
    1656             :       ),
    1657             :     );
    1658             :   }
    1659             : }
    1660             : 
    1661             : /// Defines the appearance of a Material Design Mongol text field.
    1662             : ///
    1663             : /// [MongolInputDecorator] displays the visual elements of a Material Design text
    1664             : /// Mongol field around its input [child]. The visual elements themselves are defined
    1665             : /// by an [InputDecoration] object and their layout and appearance depend
    1666             : /// on the `baseStyle`, `textAlign`, `isFocused`, and `isEmpty` parameters.
    1667             : ///
    1668             : /// [MongolTextField] uses this widget to decorate its [MongolEditableText] child.
    1669             : ///
    1670             : /// [MongolInputDecorator] can be used to create widgets that look and behave like a
    1671             : /// [MongolTextField] but support other kinds of input.
    1672             : ///
    1673             : /// Requires one of its ancestors to be a [Material] widget.
    1674             : ///
    1675             : /// See also:
    1676             : ///
    1677             : ///  * [MongolTextField], which uses a [MongolInputDecorator] to display a border,
    1678             : ///    labels, and icons, around its [MongolEditableText] child.
    1679             : ///  * [Decoration] and [DecoratedBox], for drawing arbitrary decorations
    1680             : ///    around other widgets.
    1681             : class MongolInputDecorator extends StatefulWidget {
    1682             :   /// Creates a widget that displays a border, labels, and icons,
    1683             :   /// for a [MongolTextField].
    1684             :   ///
    1685             :   /// The [isFocused], [isHovering], [expands], and [isEmpty] arguments must not
    1686             :   /// be null.
    1687           1 :   const MongolInputDecorator({
    1688             :     Key? key,
    1689             :     required this.decoration,
    1690             :     this.baseStyle,
    1691             :     this.textAlign,
    1692             :     this.textAlignHorizontal,
    1693             :     this.isFocused = false,
    1694             :     this.isHovering = false,
    1695             :     this.expands = false,
    1696             :     this.isEmpty = false,
    1697             :     this.child,
    1698           1 :   }) : super(key: key);
    1699             : 
    1700             :   /// The text and styles to use when decorating the child.
    1701             :   ///
    1702             :   /// Null [InputDecoration] properties are initialized with the corresponding
    1703             :   /// values from [ThemeData.inputDecorationTheme].
    1704             :   ///
    1705             :   /// Must not be null.
    1706             :   final InputDecoration decoration;
    1707             : 
    1708             :   /// The style on which to base the label, hint, counter, and error styles
    1709             :   /// if the [decoration] does not provide explicit styles.
    1710             :   ///
    1711             :   /// If null, `baseStyle` defaults to the `subtitle1` style from the
    1712             :   /// current [Theme], see [ThemeData.textTheme].
    1713             :   ///
    1714             :   /// The [TextStyle.textBaseline] of the [baseStyle] is used to determine
    1715             :   /// the baseline used for text alignment.
    1716             :   final TextStyle? baseStyle;
    1717             : 
    1718             :   /// How the text in the decoration should be aligned vertically.
    1719             :   final MongolTextAlign? textAlign;
    1720             : 
    1721             :   /// How the text should be aligned horizontally.
    1722             :   ///
    1723             :   /// Determines the alignment of the baseline within the available space of
    1724             :   /// the input (typically a MongolTextField). For example, TextAlignHorizontal.left will
    1725             :   /// place the baseline such that the text, and any attached decoration like
    1726             :   /// prefix and suffix, is as close to the left side of the input as possible without
    1727             :   /// overflowing. The widths of the prefix and suffix are similarly included
    1728             :   /// for other alignment values. If the width is greater than the width
    1729             :   /// available, then the prefix and suffix will be allowed to overflow first
    1730             :   /// before the text scrolls.
    1731             :   final TextAlignHorizontal? textAlignHorizontal;
    1732             : 
    1733             :   /// Whether the input field has focus.
    1734             :   ///
    1735             :   /// Determines the position of the label text and the color and weight of the
    1736             :   /// border.
    1737             :   ///
    1738             :   /// Defaults to false.
    1739             :   ///
    1740             :   /// See also:
    1741             :   ///
    1742             :   ///  * [InputDecoration.hoverColor], which is also blended into the focus
    1743             :   ///    color and fill color when the [isHovering] is true to produce the final
    1744             :   ///    color.
    1745             :   final bool isFocused;
    1746             : 
    1747             :   /// Whether the input field is being hovered over by a mouse pointer.
    1748             :   ///
    1749             :   /// Determines the container fill color, which is a blend of
    1750             :   /// [InputDecoration.hoverColor] with [InputDecoration.fillColor] when
    1751             :   /// true, and [InputDecoration.fillColor] when not.
    1752             :   ///
    1753             :   /// Defaults to false.
    1754             :   final bool isHovering;
    1755             : 
    1756             :   /// If true, the width of the input field will be as large as possible.
    1757             :   ///
    1758             :   /// If wrapped in a widget that constrains its child's width, like Expanded
    1759             :   /// or SizedBox, the input field will only be affected if [expands] is set to
    1760             :   /// true.
    1761             :   ///
    1762             :   /// See [MongolTextField.minLines] and [MongolTextField.maxLines] for related ways to
    1763             :   /// affect the width of an input. When [expands] is true, both must be null
    1764             :   /// in order to avoid ambiguity in determining the width.
    1765             :   ///
    1766             :   /// Defaults to false.
    1767             :   final bool expands;
    1768             : 
    1769             :   /// Whether the input field is empty.
    1770             :   ///
    1771             :   /// Determines the position of the label text and whether to display the hint
    1772             :   /// text.
    1773             :   ///
    1774             :   /// Defaults to false.
    1775             :   final bool isEmpty;
    1776             : 
    1777             :   /// The widget below this widget in the tree.
    1778             :   ///
    1779             :   /// Typically a [MongolEditableText], [DropdownButton], or [InkWell].
    1780             :   final Widget? child;
    1781             : 
    1782             :   /// Whether the label needs to get out of the way of the input, either by
    1783             :   /// floating or disappearing.
    1784             :   ///
    1785             :   /// Will withdraw when not empty, or when focused while enabled.
    1786           1 :   bool get _labelShouldWithdraw =>
    1787           4 :       !isEmpty || (isFocused && decoration.enabled);
    1788             : 
    1789           1 :   @override
    1790           1 :   _InputDecoratorState createState() => _InputDecoratorState();
    1791             : 
    1792             :   /// The RenderBox that defines this decorator's "container". That's the
    1793             :   /// area which is filled if [InputDecoration.filled] is true. It's the area
    1794             :   /// adjacent to [InputDecoration.icon] and to the left of the widgets that contain
    1795             :   /// [InputDecoration.helperText], [InputDecoration.errorText], and
    1796             :   /// [InputDecoration.counterText].
    1797             :   ///
    1798             :   /// [MongolTextField] renders ink splashes within the container.
    1799           0 :   static RenderBox? containerOf(BuildContext context) {
    1800           0 :     final result = context.findAncestorRenderObjectOfType<_RenderDecoration>();
    1801           0 :     return result?.container;
    1802             :   }
    1803             : 
    1804           0 :   @override
    1805             :   void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    1806           0 :     super.debugFillProperties(properties);
    1807             :     properties
    1808           0 :         .add(DiagnosticsProperty<InputDecoration>('decoration', decoration));
    1809           0 :     properties.add(DiagnosticsProperty<TextStyle>('baseStyle', baseStyle,
    1810             :         defaultValue: null));
    1811           0 :     properties.add(DiagnosticsProperty<bool>('isFocused', isFocused));
    1812           0 :     properties.add(
    1813           0 :         DiagnosticsProperty<bool>('expands', expands, defaultValue: false));
    1814           0 :     properties.add(DiagnosticsProperty<bool>('isEmpty', isEmpty));
    1815             :   }
    1816             : }
    1817             : 
    1818             : class _InputDecoratorState extends State<MongolInputDecorator>
    1819             :     with TickerProviderStateMixin {
    1820             :   late AnimationController _floatingLabelController;
    1821             :   late AnimationController _shakingLabelController;
    1822             :   final _InputBorderGap _borderGap = _InputBorderGap();
    1823             : 
    1824           1 :   @override
    1825             :   void initState() {
    1826           1 :     super.initState();
    1827             : 
    1828           4 :     final labelIsInitiallyFloating = widget.decoration.floatingLabelBehavior ==
    1829             :             FloatingLabelBehavior.always ||
    1830           4 :         (widget.decoration.floatingLabelBehavior !=
    1831             :                 FloatingLabelBehavior.never &&
    1832           2 :             widget._labelShouldWithdraw);
    1833             : 
    1834           2 :     _floatingLabelController = AnimationController(
    1835             :         duration: _kTransitionDuration,
    1836             :         vsync: this,
    1837             :         value: labelIsInitiallyFloating ? 1.0 : 0.0);
    1838           3 :     _floatingLabelController.addListener(_handleChange);
    1839             : 
    1840           2 :     _shakingLabelController = AnimationController(
    1841             :       duration: _kTransitionDuration,
    1842             :       vsync: this,
    1843             :     );
    1844             :   }
    1845             : 
    1846           1 :   @override
    1847             :   void didChangeDependencies() {
    1848           1 :     super.didChangeDependencies();
    1849           1 :     _effectiveDecoration = null;
    1850             :   }
    1851             : 
    1852           1 :   @override
    1853             :   void dispose() {
    1854           2 :     _floatingLabelController.dispose();
    1855           2 :     _shakingLabelController.dispose();
    1856           1 :     super.dispose();
    1857             :   }
    1858             : 
    1859           1 :   void _handleChange() {
    1860           2 :     setState(() {
    1861             :       // The _floatingLabelController's value has changed.
    1862             :     });
    1863             :   }
    1864             : 
    1865             :   InputDecoration? _effectiveDecoration;
    1866           1 :   InputDecoration? get decoration {
    1867           4 :     _effectiveDecoration ??= widget.decoration.applyDefaults(
    1868           3 :       Theme.of(context).inputDecorationTheme,
    1869             :     );
    1870           1 :     return _effectiveDecoration;
    1871             :   }
    1872             : 
    1873           3 :   MongolTextAlign? get textAlign => widget.textAlign;
    1874           3 :   bool get isFocused => widget.isFocused;
    1875           5 :   bool get isHovering => widget.isHovering && decoration!.enabled;
    1876           3 :   bool get isEmpty => widget.isEmpty;
    1877           1 :   bool get _floatingLabelEnabled {
    1878           3 :     return decoration!.floatingLabelBehavior != FloatingLabelBehavior.never;
    1879             :   }
    1880             : 
    1881           1 :   @override
    1882             :   void didUpdateWidget(MongolInputDecorator old) {
    1883           1 :     super.didUpdateWidget(old);
    1884           5 :     if (widget.decoration != old.decoration) _effectiveDecoration = null;
    1885             : 
    1886           4 :     final floatBehaviorChanged = widget.decoration.floatingLabelBehavior !=
    1887           2 :         old.decoration.floatingLabelBehavior;
    1888             : 
    1889           4 :     if (widget._labelShouldWithdraw != old._labelShouldWithdraw ||
    1890             :         floatBehaviorChanged) {
    1891           1 :       if (_floatingLabelEnabled &&
    1892           2 :           (widget._labelShouldWithdraw ||
    1893           4 :               widget.decoration.floatingLabelBehavior ==
    1894             :                   FloatingLabelBehavior.always)) {
    1895           2 :         _floatingLabelController.forward();
    1896             :       } else {
    1897           2 :         _floatingLabelController.reverse();
    1898             :       }
    1899             :     }
    1900             : 
    1901           2 :     final String? errorText = decoration!.errorText;
    1902           2 :     final String? oldErrorText = old.decoration.errorText;
    1903             : 
    1904           2 :     if (_floatingLabelController.isCompleted &&
    1905             :         errorText != null &&
    1906           0 :         errorText != oldErrorText) {
    1907           0 :       _shakingLabelController
    1908           0 :         ..value = 0.0
    1909           0 :         ..forward();
    1910             :     }
    1911             :   }
    1912             : 
    1913           1 :   Color _getActiveColor(ThemeData themeData) {
    1914           1 :     if (isFocused) {
    1915           1 :       switch (themeData.brightness) {
    1916           1 :         case Brightness.dark:
    1917           0 :           return themeData.accentColor;
    1918           1 :         case Brightness.light:
    1919           1 :           return themeData.primaryColor;
    1920             :       }
    1921             :     }
    1922           1 :     return themeData.hintColor;
    1923             :   }
    1924             : 
    1925           1 :   Color _getDefaultBorderColor(ThemeData themeData) {
    1926           1 :     if (isFocused) {
    1927           1 :       switch (themeData.brightness) {
    1928           1 :         case Brightness.dark:
    1929           0 :           return themeData.accentColor;
    1930           1 :         case Brightness.light:
    1931           1 :           return themeData.primaryColor;
    1932             :       }
    1933             :     }
    1934           2 :     if (decoration!.filled!) {
    1935           0 :       return themeData.hintColor;
    1936             :     }
    1937           3 :     final enabledColor = themeData.colorScheme.onSurface.withOpacity(0.38);
    1938           1 :     if (isHovering) {
    1939           2 :       final hoverColor = decoration!.hoverColor ??
    1940           2 :           themeData.inputDecorationTheme.hoverColor ??
    1941           1 :           themeData.hoverColor;
    1942           2 :       return Color.alphaBlend(hoverColor.withOpacity(0.12), enabledColor);
    1943             :     }
    1944             :     return enabledColor;
    1945             :   }
    1946             : 
    1947           1 :   Color _getFillColor(ThemeData themeData) {
    1948           3 :     if (decoration!.filled != true) // filled == null same as filled == false
    1949             :     {
    1950             :       return Colors.transparent;
    1951             :     }
    1952           0 :     if (decoration!.fillColor != null) return decoration!.fillColor!;
    1953             : 
    1954             :     // dark theme: 10% white (enabled), 5% white (disabled)
    1955             :     // light theme: 4% black (enabled), 2% black (disabled)
    1956             :     const darkEnabled = Color(0x1AFFFFFF);
    1957             :     const darkDisabled = Color(0x0DFFFFFF);
    1958             :     const lightEnabled = Color(0x0A000000);
    1959             :     const lightDisabled = Color(0x05000000);
    1960             : 
    1961           0 :     switch (themeData.brightness) {
    1962           0 :       case Brightness.dark:
    1963           0 :         return decoration!.enabled ? darkEnabled : darkDisabled;
    1964           0 :       case Brightness.light:
    1965           0 :         return decoration!.enabled ? lightEnabled : lightDisabled;
    1966             :     }
    1967             :   }
    1968             : 
    1969           1 :   Color _getHoverColor(ThemeData themeData) {
    1970           2 :     if (decoration!.filled == null ||
    1971           2 :         !decoration!.filled! ||
    1972           0 :         isFocused ||
    1973           0 :         !decoration!.enabled) return Colors.transparent;
    1974           0 :     return decoration!.hoverColor ??
    1975           0 :         themeData.inputDecorationTheme.hoverColor ??
    1976           0 :         themeData.hoverColor;
    1977             :   }
    1978             : 
    1979           1 :   Color _getDefaultIconColor(ThemeData themeData) {
    1980           4 :     if (!decoration!.enabled && !isFocused) return themeData.disabledColor;
    1981             : 
    1982           1 :     switch (themeData.brightness) {
    1983           1 :       case Brightness.dark:
    1984             :         return Colors.white70;
    1985           1 :       case Brightness.light:
    1986             :         return Colors.black45;
    1987             :     }
    1988             :   }
    1989             : 
    1990             :   // True if the label will be shown and the hint will not.
    1991             :   // If we're not focused, there's no value, labelText was provided, and
    1992             :   // floatingLabelBehavior isn't set to always, then the label appears where the
    1993             :   // hint would.
    1994           1 :   bool get _hasInlineLabel {
    1995           2 :     return !widget._labelShouldWithdraw &&
    1996           2 :         decoration!.labelText != null &&
    1997           3 :         decoration!.floatingLabelBehavior != FloatingLabelBehavior.always;
    1998             :   }
    1999             : 
    2000             :   // If the label is a floating placeholder, it's always shown.
    2001           3 :   bool get _shouldShowLabel => _hasInlineLabel || _floatingLabelEnabled;
    2002             : 
    2003             :   // The base style for the inline label or hint when they're displayed "inline",
    2004             :   // i.e. when they appear in place of the empty text field.
    2005           1 :   TextStyle _getInlineStyle(ThemeData themeData) {
    2006           6 :     return themeData.textTheme.subtitle1!.merge(widget.baseStyle).copyWith(
    2007           2 :         color: decoration!.enabled
    2008           1 :             ? themeData.hintColor
    2009           1 :             : themeData.disabledColor);
    2010             :   }
    2011             : 
    2012           1 :   TextStyle _getFloatingLabelStyle(ThemeData themeData) {
    2013           2 :     final Color color = decoration!.errorText != null
    2014           0 :         ? decoration!.errorStyle?.color ?? themeData.errorColor
    2015           1 :         : _getActiveColor(themeData);
    2016           5 :     final style = themeData.textTheme.subtitle1!.merge(widget.baseStyle);
    2017             :     // Temporary opt-in fix for https://github.com/flutter/flutter/issues/54028
    2018             :     // Setting TextStyle.height to 1 ensures that the label's width (in
    2019             :     // vertical orientation) will equal its font size.
    2020           1 :     return themeData.fixTextFieldOutlineLabel
    2021             :         ? style
    2022           0 :             .copyWith(
    2023             :                 height: 1,
    2024           0 :                 color: decoration!.enabled ? color : themeData.disabledColor)
    2025           0 :             .merge(decoration!.labelStyle)
    2026             :         : style
    2027           1 :             .copyWith(
    2028           2 :                 color: decoration!.enabled ? color : themeData.disabledColor)
    2029           3 :             .merge(decoration!.labelStyle);
    2030             :   }
    2031             : 
    2032           1 :   TextStyle _getHelperStyle(ThemeData themeData) {
    2033             :     final color =
    2034           3 :         decoration!.enabled ? themeData.hintColor : Colors.transparent;
    2035           2 :     return themeData.textTheme.caption!
    2036           1 :         .copyWith(color: color)
    2037           3 :         .merge(decoration!.helperStyle);
    2038             :   }
    2039             : 
    2040           1 :   TextStyle _getErrorStyle(ThemeData themeData) {
    2041             :     final color =
    2042           3 :         decoration!.enabled ? themeData.errorColor : Colors.transparent;
    2043           2 :     return themeData.textTheme.caption!
    2044           1 :         .copyWith(color: color)
    2045           3 :         .merge(decoration!.errorStyle);
    2046             :   }
    2047             : 
    2048           1 :   InputBorder _getDefaultBorder(ThemeData themeData) {
    2049           4 :     if (decoration!.border?.borderSide == BorderSide.none) {
    2050           0 :       return decoration!.border!;
    2051             :     }
    2052             : 
    2053             :     final Color borderColor;
    2054           3 :     if (decoration!.enabled || isFocused) {
    2055           2 :       borderColor = decoration!.errorText == null
    2056           1 :           ? _getDefaultBorderColor(themeData)
    2057           1 :           : themeData.errorColor;
    2058             :     } else {
    2059             :       borderColor =
    2060           3 :           (decoration!.filled == true && decoration!.border?.isOutline != true)
    2061             :               ? Colors.transparent
    2062           1 :               : themeData.disabledColor;
    2063             :     }
    2064             : 
    2065             :     final double borderWeight;
    2066           2 :     if (decoration!.isCollapsed ||
    2067           3 :         decoration?.border == InputBorder.none ||
    2068           2 :         !decoration!.enabled) {
    2069             :       borderWeight = 0.0;
    2070             :     } else {
    2071           1 :       borderWeight = isFocused ? 2.0 : 1.0;
    2072             :     }
    2073             : 
    2074           2 :     final border = decoration!.border ?? const SidelineInputBorder();
    2075           1 :     return border.copyWith(
    2076           1 :         borderSide: BorderSide(color: borderColor, width: borderWeight));
    2077             :   }
    2078             : 
    2079           1 :   @override
    2080             :   Widget build(BuildContext context) {
    2081           1 :     final themeData = Theme.of(context);
    2082           1 :     final inlineStyle = _getInlineStyle(themeData);
    2083           1 :     final textBaseline = inlineStyle.textBaseline!;
    2084             : 
    2085           3 :     final hintStyle = inlineStyle.merge(decoration!.hintStyle);
    2086           2 :     final Widget? hint = decoration!.hintText == null
    2087             :         ? null
    2088           1 :         : AnimatedOpacity(
    2089           2 :             opacity: (isEmpty && !_hasInlineLabel) ? 1.0 : 0.0,
    2090             :             duration: _kTransitionDuration,
    2091             :             curve: _kTransitionCurve,
    2092             :             alwaysIncludeSemantics: true,
    2093           1 :             child: MongolText(
    2094           2 :               decoration!.hintText!,
    2095             :               style: hintStyle,
    2096             :               overflow: TextOverflow.ellipsis,
    2097           1 :               textAlign: textAlign,
    2098           2 :               maxLines: decoration!.hintMaxLines,
    2099             :             ),
    2100             :           );
    2101             : 
    2102           2 :     final isError = decoration!.errorText != null;
    2103             :     InputBorder? border;
    2104           2 :     if (!decoration!.enabled) {
    2105           2 :       border = isError ? decoration!.errorBorder : decoration!.disabledBorder;
    2106           1 :     } else if (isFocused) {
    2107             :       border =
    2108           2 :           isError ? decoration!.focusedErrorBorder : decoration!.focusedBorder;
    2109             :     } else {
    2110           4 :       border = isError ? decoration!.errorBorder : decoration!.enabledBorder;
    2111             :     }
    2112           1 :     border ??= _getDefaultBorder(themeData);
    2113             : 
    2114           1 :     final Widget container = _BorderContainer(
    2115             :       border: border,
    2116           1 :       gap: _borderGap,
    2117           2 :       gapAnimation: _floatingLabelController.view,
    2118           1 :       fillColor: _getFillColor(themeData),
    2119           1 :       hoverColor: _getHoverColor(themeData),
    2120           1 :       isHovering: isHovering,
    2121             :     );
    2122             : 
    2123             :     // Temporary opt-in fix for https://github.com/flutter/flutter/issues/54028
    2124             :     // Setting TextStyle.height to 1 ensures that the label's width (
    2125             :     // in vertical orientation) will equal its font size.
    2126           1 :     final inlineLabelStyle = themeData.fixTextFieldOutlineLabel
    2127           0 :         ? inlineStyle.merge(decoration!.labelStyle).copyWith(height: 1)
    2128           3 :         : inlineStyle.merge(decoration!.labelStyle);
    2129           2 :     final Widget? label = decoration!.labelText == null
    2130             :         ? null
    2131           1 :         : _Shaker(
    2132           2 :             animation: _shakingLabelController.view,
    2133           1 :             child: AnimatedOpacity(
    2134             :               duration: _kTransitionDuration,
    2135             :               curve: _kTransitionCurve,
    2136           1 :               opacity: _shouldShowLabel ? 1.0 : 0.0,
    2137           1 :               child: AnimatedDefaultTextStyle(
    2138             :                 duration: _kTransitionDuration,
    2139             :                 curve: _kTransitionCurve,
    2140           2 :                 style: widget._labelShouldWithdraw
    2141           1 :                     ? _getFloatingLabelStyle(themeData)
    2142             :                     : inlineLabelStyle,
    2143           1 :                 child: MongolText(
    2144           2 :                   decoration!.labelText!,
    2145             :                   overflow: TextOverflow.ellipsis,
    2146           1 :                   textAlign: textAlign,
    2147             :                 ),
    2148             :               ),
    2149             :             ),
    2150             :           );
    2151             : 
    2152             :     final Widget? prefix =
    2153           4 :         decoration!.prefix == null && decoration!.prefixText == null
    2154             :             ? null
    2155           1 :             : _AffixText(
    2156           2 :                 labelIsFloating: widget._labelShouldWithdraw,
    2157           2 :                 text: decoration!.prefixText,
    2158           2 :                 style: decoration!.prefixStyle ?? hintStyle,
    2159           2 :                 child: decoration!.prefix,
    2160             :               );
    2161             : 
    2162             :     final Widget? suffix =
    2163           4 :         decoration!.suffix == null && decoration!.suffixText == null
    2164             :             ? null
    2165           1 :             : _AffixText(
    2166           2 :                 labelIsFloating: widget._labelShouldWithdraw,
    2167           2 :                 text: decoration!.suffixText,
    2168           2 :                 style: decoration!.suffixStyle ?? hintStyle,
    2169           2 :                 child: decoration!.suffix,
    2170             :               );
    2171             : 
    2172           1 :     final activeColor = _getActiveColor(themeData);
    2173             :     final decorationIsDense =
    2174           3 :         decoration!.isDense == true; // isDense == null, same as false
    2175             :     final iconSize = decorationIsDense ? 18.0 : 24.0;
    2176           2 :     final iconColor = isFocused ? activeColor : _getDefaultIconColor(themeData);
    2177             : 
    2178           2 :     final Widget? icon = decoration!.icon == null
    2179             :         ? null
    2180           1 :         : Padding(
    2181             :             padding: const EdgeInsetsDirectional.only(end: 16.0),
    2182           1 :             child: IconTheme.merge(
    2183           1 :               data: IconThemeData(
    2184             :                 color: iconColor,
    2185             :                 size: iconSize,
    2186             :               ),
    2187           2 :               child: decoration!.icon!,
    2188             :             ),
    2189             :           );
    2190             : 
    2191           2 :     final Widget? prefixIcon = decoration!.prefixIcon == null
    2192             :         ? null
    2193           0 :         : Center(
    2194             :             widthFactor: 1.0,
    2195             :             heightFactor: 1.0,
    2196           0 :             child: ConstrainedBox(
    2197           0 :               constraints: decoration!.prefixIconConstraints ??
    2198           0 :                   themeData.visualDensity.effectiveConstraints(
    2199             :                     const BoxConstraints(
    2200             :                       minWidth: kMinInteractiveDimension,
    2201             :                       minHeight: kMinInteractiveDimension,
    2202             :                     ),
    2203             :                   ),
    2204           0 :               child: IconTheme.merge(
    2205           0 :                 data: IconThemeData(
    2206             :                   color: iconColor,
    2207             :                   size: iconSize,
    2208             :                 ),
    2209           0 :                 child: decoration!.prefixIcon!,
    2210             :               ),
    2211             :             ),
    2212             :           );
    2213             : 
    2214           2 :     final Widget? suffixIcon = decoration!.suffixIcon == null
    2215             :         ? null
    2216           1 :         : Center(
    2217             :             widthFactor: 1.0,
    2218             :             heightFactor: 1.0,
    2219           1 :             child: ConstrainedBox(
    2220           2 :               constraints: decoration!.suffixIconConstraints ??
    2221           2 :                   themeData.visualDensity.effectiveConstraints(
    2222             :                     const BoxConstraints(
    2223             :                       minWidth: kMinInteractiveDimension,
    2224             :                       minHeight: kMinInteractiveDimension,
    2225             :                     ),
    2226             :                   ),
    2227           1 :               child: IconTheme.merge(
    2228           1 :                 data: IconThemeData(
    2229             :                   color: iconColor,
    2230             :                   size: iconSize,
    2231             :                 ),
    2232           2 :                 child: decoration!.suffixIcon!,
    2233             :               ),
    2234             :             ),
    2235             :           );
    2236             : 
    2237           1 :     final Widget helperError = _HelperError(
    2238           1 :       textAlign: textAlign,
    2239           2 :       helperText: decoration!.helperText,
    2240           1 :       helperStyle: _getHelperStyle(themeData),
    2241           2 :       helperMaxLines: decoration!.helperMaxLines,
    2242           2 :       errorText: decoration!.errorText,
    2243           1 :       errorStyle: _getErrorStyle(themeData),
    2244           2 :       errorMaxLines: decoration!.errorMaxLines,
    2245             :     );
    2246             : 
    2247             :     Widget? counter;
    2248           2 :     if (decoration!.counter != null) {
    2249           0 :       counter = decoration!.counter;
    2250           2 :     } else if (decoration!.counterText != null &&
    2251           3 :         decoration!.counterText != '') {
    2252           1 :       counter = Semantics(
    2253             :         container: true,
    2254           1 :         liveRegion: isFocused,
    2255           1 :         child: Text(
    2256           2 :           decoration!.counterText!,
    2257           4 :           style: _getHelperStyle(themeData).merge(decoration!.counterStyle),
    2258             :           overflow: TextOverflow.ellipsis,
    2259           2 :           semanticsLabel: decoration!.semanticCounterText,
    2260             :         ),
    2261             :       );
    2262             :     }
    2263             : 
    2264             :     // The _Decoration widget and _RenderDecoration assume that contentPadding
    2265             :     // has been resolved to EdgeInsets.
    2266             :     const textDirection = TextDirection.ltr;
    2267             :     final decorationContentPadding =
    2268           2 :         decoration!.contentPadding?.resolve(textDirection);
    2269             : 
    2270             :     final EdgeInsets contentPadding;
    2271             :     final double floatingLabelWidth;
    2272           2 :     if (decoration!.isCollapsed) {
    2273             :       floatingLabelWidth = 0.0;
    2274             :       contentPadding = decorationContentPadding ?? EdgeInsets.zero;
    2275           1 :     } else if (!border.isOutline) {
    2276             :       // 4.0: the horizontal gap between the inline elements and the floating label.
    2277           4 :       floatingLabelWidth = (4.0 + 0.75 * inlineLabelStyle.fontSize!) *
    2278           1 :           MediaQuery.textScaleFactorOf(context);
    2279           3 :       if (decoration!.filled == true) {
    2280             :         // filled == null same as filled == false
    2281             :         contentPadding = decorationContentPadding ??
    2282             :             (decorationIsDense
    2283             :                 ? const EdgeInsets.fromLTRB(8.0, 12.0, 8.0, 12.0)
    2284             :                 : const EdgeInsets.fromLTRB(12.0, 12.0, 12.0, 12.0));
    2285             :       } else {
    2286             :         // Not top or bottom padding for sideline borders that aren't filled
    2287             :         // is a small concession to backwards compatibility. This eliminates
    2288             :         // the most noticeable layout change introduced by #13734.
    2289             :         contentPadding = decorationContentPadding ??
    2290             :             (decorationIsDense
    2291             :                 ? const EdgeInsets.fromLTRB(8.0, 0.0, 8.0, 0.0)
    2292             :                 : const EdgeInsets.fromLTRB(12.0, 0.0, 12.0, 0.0));
    2293             :       }
    2294             :     } else {
    2295             :       floatingLabelWidth = 0.0;
    2296             :       contentPadding = decorationContentPadding ??
    2297             :           (decorationIsDense
    2298             :               ? const EdgeInsets.fromLTRB(20.0, 12.0, 12.0, 12.0)
    2299             :               : const EdgeInsets.fromLTRB(24.0, 12.0, 16.0, 12.0));
    2300             :     }
    2301             : 
    2302           1 :     return _Decorator(
    2303           1 :       decoration: _Decoration(
    2304             :         contentPadding: contentPadding,
    2305           2 :         isCollapsed: decoration!.isCollapsed,
    2306             :         floatingLabelWidth: floatingLabelWidth,
    2307           2 :         floatingLabelProgress: _floatingLabelController.value,
    2308             :         border: border,
    2309           1 :         borderGap: _borderGap,
    2310           2 :         alignLabelWithHint: decoration!.alignLabelWithHint ?? false,
    2311           2 :         isDense: decoration!.isDense,
    2312           1 :         visualDensity: themeData.visualDensity,
    2313             :         icon: icon,
    2314           2 :         input: widget.child,
    2315             :         label: label,
    2316             :         hint: hint,
    2317             :         prefix: prefix,
    2318             :         suffix: suffix,
    2319             :         prefixIcon: prefixIcon,
    2320             :         suffixIcon: suffixIcon,
    2321             :         helperError: helperError,
    2322             :         counter: counter,
    2323             :         container: container,
    2324           1 :         fixTextFieldOutlineLabel: themeData.fixTextFieldOutlineLabel,
    2325             :       ),
    2326             :       textBaseline: textBaseline,
    2327           2 :       textAlignHorizontal: widget.textAlignHorizontal,
    2328           1 :       isFocused: isFocused,
    2329           2 :       expands: widget.expands,
    2330             :     );
    2331             :   }
    2332             : }

Generated by: LCOV version 1.15