mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Prepare for RenderDecorator.computeBaseline changes. (#146363)
Minor changes to make the `RenderDecorator.computeBaseline` change a bit easier to make. No semantic changes.
This commit is contained in:
parent
2c038e6cbb
commit
98d23f709f
@ -30,6 +30,9 @@ const Duration _kTransitionDuration = Duration(milliseconds: 167);
|
|||||||
const Curve _kTransitionCurve = Curves.fastOutSlowIn;
|
const Curve _kTransitionCurve = Curves.fastOutSlowIn;
|
||||||
const double _kFinalLabelScale = 0.75;
|
const double _kFinalLabelScale = 0.75;
|
||||||
|
|
||||||
|
typedef _SubtextSize = ({ double ascent, double bottomHeight, double subtextHeight });
|
||||||
|
typedef _ChildBaselineGetter = double Function(RenderBox child, BoxConstraints constraints);
|
||||||
|
|
||||||
// The default duration for hint fade in/out transitions.
|
// The default duration for hint fade in/out transitions.
|
||||||
//
|
//
|
||||||
// Animating hint is not mentioned in the Material specification.
|
// Animating hint is not mentioned in the Material specification.
|
||||||
@ -614,7 +617,7 @@ class _Decoration {
|
|||||||
this.container,
|
this.container,
|
||||||
});
|
});
|
||||||
|
|
||||||
final EdgeInsetsGeometry contentPadding;
|
final EdgeInsetsDirectional contentPadding;
|
||||||
final bool isCollapsed;
|
final bool isCollapsed;
|
||||||
final double floatingLabelHeight;
|
final double floatingLabelHeight;
|
||||||
final double floatingLabelProgress;
|
final double floatingLabelProgress;
|
||||||
@ -698,20 +701,16 @@ class _Decoration {
|
|||||||
// all of the renderer children of a _RenderDecoration.
|
// all of the renderer children of a _RenderDecoration.
|
||||||
class _RenderDecorationLayout {
|
class _RenderDecorationLayout {
|
||||||
const _RenderDecorationLayout({
|
const _RenderDecorationLayout({
|
||||||
required this.boxToBaseline,
|
required this.baseline,
|
||||||
required this.inputBaseline, // for InputBorderType.underline
|
|
||||||
required this.outlineBaseline, // for InputBorderType.outline
|
|
||||||
required this.subtextBaseline,
|
|
||||||
required this.containerHeight,
|
required this.containerHeight,
|
||||||
required this.subtextHeight,
|
required this.subtextSize,
|
||||||
|
required this.size,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Map<RenderBox?, double> boxToBaseline;
|
final double baseline;
|
||||||
final double inputBaseline;
|
|
||||||
final double outlineBaseline;
|
|
||||||
final double subtextBaseline; // helper/error counter
|
|
||||||
final double containerHeight;
|
final double containerHeight;
|
||||||
final double subtextHeight;
|
final _SubtextSize? subtextSize;
|
||||||
|
final Size size;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The workhorse: layout and paint a _Decorator widget's _Decoration.
|
// The workhorse: layout and paint a _Decorator widget's _Decoration.
|
||||||
@ -742,13 +741,14 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
|
|||||||
RenderBox? get suffix => childForSlot(_DecorationSlot.suffix);
|
RenderBox? get suffix => childForSlot(_DecorationSlot.suffix);
|
||||||
RenderBox? get prefixIcon => childForSlot(_DecorationSlot.prefixIcon);
|
RenderBox? get prefixIcon => childForSlot(_DecorationSlot.prefixIcon);
|
||||||
RenderBox? get suffixIcon => childForSlot(_DecorationSlot.suffixIcon);
|
RenderBox? get suffixIcon => childForSlot(_DecorationSlot.suffixIcon);
|
||||||
RenderBox? get helperError => childForSlot(_DecorationSlot.helperError);
|
RenderBox get helperError => childForSlot(_DecorationSlot.helperError)!;
|
||||||
RenderBox? get counter => childForSlot(_DecorationSlot.counter);
|
RenderBox? get counter => childForSlot(_DecorationSlot.counter);
|
||||||
RenderBox? get container => childForSlot(_DecorationSlot.container);
|
RenderBox? get container => childForSlot(_DecorationSlot.container);
|
||||||
|
|
||||||
// The returned list is ordered for hit testing.
|
// The returned list is ordered for hit testing.
|
||||||
@override
|
@override
|
||||||
Iterable<RenderBox> get children {
|
Iterable<RenderBox> get children {
|
||||||
|
final RenderBox? helperError = childForSlot(_DecorationSlot.helperError);
|
||||||
return <RenderBox>[
|
return <RenderBox>[
|
||||||
if (icon != null)
|
if (icon != null)
|
||||||
icon!,
|
icon!,
|
||||||
@ -767,7 +767,7 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
|
|||||||
if (hint != null)
|
if (hint != null)
|
||||||
hint!,
|
hint!,
|
||||||
if (helperError != null)
|
if (helperError != null)
|
||||||
helperError!,
|
helperError,
|
||||||
if (counter != null)
|
if (counter != null)
|
||||||
counter!,
|
counter!,
|
||||||
if (container != null)
|
if (container != null)
|
||||||
@ -894,70 +894,62 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
|
|||||||
if (container != null) {
|
if (container != null) {
|
||||||
visitor(container!);
|
visitor(container!);
|
||||||
}
|
}
|
||||||
if (helperError != null) {
|
visitor(helperError);
|
||||||
visitor(helperError!);
|
|
||||||
}
|
|
||||||
if (counter != null) {
|
if (counter != null) {
|
||||||
visitor(counter!);
|
visitor(counter!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
static double _minWidth(RenderBox? box, double height) => box?.getMinIntrinsicWidth(height) ?? 0.0;
|
||||||
bool get sizedByParent => false;
|
static double _maxWidth(RenderBox? box, double height) => box?.getMaxIntrinsicWidth(height) ?? 0.0 ;
|
||||||
|
static double _minHeight(RenderBox? box, double width) => box?.getMinIntrinsicHeight(width) ?? 0.0;
|
||||||
static double _minWidth(RenderBox? box, double height) {
|
static Size _boxSize(RenderBox? box) => box?.size ?? Size.zero;
|
||||||
return box == null ? 0.0 : box.getMinIntrinsicWidth(height);
|
static double _getBaseline(RenderBox box, BoxConstraints boxConstraints) {
|
||||||
|
return ChildLayoutHelper.getBaseline(box, boxConstraints, TextBaseline.alphabetic) ?? box.size.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
static double _maxWidth(RenderBox? box, double height) {
|
|
||||||
return box == null ? 0.0 : box.getMaxIntrinsicWidth(height);
|
|
||||||
}
|
|
||||||
|
|
||||||
static double _minHeight(RenderBox? box, double width) {
|
|
||||||
return box == null ? 0.0 : box.getMinIntrinsicHeight(width);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Size _boxSize(RenderBox? box) => box == null ? Size.zero : box.size;
|
|
||||||
|
|
||||||
static BoxParentData _boxParentData(RenderBox box) => box.parentData! as BoxParentData;
|
static BoxParentData _boxParentData(RenderBox box) => box.parentData! as BoxParentData;
|
||||||
|
|
||||||
EdgeInsets get contentPadding => decoration.contentPadding as EdgeInsets;
|
EdgeInsetsDirectional get contentPadding => decoration.contentPadding;
|
||||||
|
|
||||||
// Lay out the given box if needed, and return its baseline.
|
_SubtextSize? _computeSubtextSizes({
|
||||||
double _layoutLineBox(RenderBox? box, BoxConstraints constraints) {
|
required BoxConstraints constraints,
|
||||||
if (box == null) {
|
required ChildLayouter layoutChild,
|
||||||
return 0.0;
|
required _ChildBaselineGetter getBaseline,
|
||||||
|
}) {
|
||||||
|
final RenderBox? counter = this.counter;
|
||||||
|
Size counterSize;
|
||||||
|
final double counterAscent;
|
||||||
|
if (counter != null) {
|
||||||
|
counterSize = layoutChild(counter, constraints);
|
||||||
|
counterAscent = getBaseline(counter, constraints);
|
||||||
|
} else {
|
||||||
|
counterSize = Size.zero;
|
||||||
|
counterAscent = 0.0;
|
||||||
}
|
}
|
||||||
box.layout(constraints, parentUsesSize: true);
|
|
||||||
// Since internally, all layout is performed against the alphabetic baseline,
|
|
||||||
// (eg, ascents/descents are all relative to alphabetic, even if the font is
|
|
||||||
// an ideographic or hanging font), we should always obtain the reference
|
|
||||||
// baseline from the alphabetic baseline. The ideographic baseline is for
|
|
||||||
// use post-layout and is derived from the alphabetic baseline combined with
|
|
||||||
// the font metrics.
|
|
||||||
final double baseline = box.getDistanceToBaseline(TextBaseline.alphabetic)!;
|
|
||||||
|
|
||||||
assert(() {
|
final BoxConstraints helperErrorConstraints = constraints.deflate(EdgeInsets.only(left: counterSize.width));
|
||||||
if (baseline >= 0) {
|
final double helperErrorHeight = layoutChild(helperError, helperErrorConstraints).height;
|
||||||
return true;
|
|
||||||
}
|
if (helperErrorHeight == 0.0 && counterSize.height == 0.0) {
|
||||||
throw FlutterError.fromParts(<DiagnosticsNode>[
|
return null;
|
||||||
ErrorSummary("One of InputDecorator's children reported a negative baseline offset."),
|
}
|
||||||
ErrorDescription(
|
|
||||||
'${box.runtimeType}, of size ${box.size}, reported a negative '
|
// TODO(LongCatIsLooong): the bottomHeight expression doesn't make much sense.
|
||||||
'alphabetic baseline of $baseline.',
|
// Use the real descent and make sure the subtext line box is tall enough for both children.
|
||||||
),
|
// See https://github.com/flutter/flutter/issues/13715
|
||||||
]);
|
final double ascent = math.max(counterAscent, getBaseline(helperError, helperErrorConstraints)) + subtextGap;
|
||||||
}());
|
final double bottomHeight = math.max(counterAscent, helperErrorHeight) + subtextGap;
|
||||||
return baseline;
|
final double subtextHeight = math.max(counterSize.height, helperErrorHeight) + subtextGap;
|
||||||
|
return (ascent: ascent, bottomHeight: bottomHeight, subtextHeight: subtextHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a value used by performLayout to position all of the renderers.
|
// Returns a value used by performLayout to position all of the renderers.
|
||||||
// This method applies layout to all of the renderers except the container.
|
// This method applies layout to all of the renderers except the container.
|
||||||
// For convenience, the container is laid out in performLayout().
|
// For convenience, the container is laid out in performLayout().
|
||||||
_RenderDecorationLayout _layout(BoxConstraints layoutConstraints) {
|
_RenderDecorationLayout _layout(BoxConstraints constraints) {
|
||||||
assert(
|
assert(
|
||||||
layoutConstraints.maxWidth < double.infinity,
|
constraints.maxWidth < double.infinity,
|
||||||
'An InputDecorator, which is typically created by a TextField, cannot '
|
'An InputDecorator, which is typically created by a TextField, cannot '
|
||||||
'have an unbounded width.\n'
|
'have an unbounded width.\n'
|
||||||
'This happens when the parent widget does not provide a finite width '
|
'This happens when the parent widget does not provide a finite width '
|
||||||
@ -967,122 +959,82 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
|
|||||||
'TextField that contains it.',
|
'TextField that contains it.',
|
||||||
);
|
);
|
||||||
|
|
||||||
// Margin on each side of subtext (counter and helperError)
|
final BoxConstraints boxConstraints = constraints.loosen();
|
||||||
final Map<RenderBox?, double> boxToBaseline = <RenderBox?, double>{};
|
|
||||||
final BoxConstraints boxConstraints = layoutConstraints.loosen();
|
|
||||||
|
|
||||||
// Layout all the widgets used by InputDecorator
|
// Layout all the widgets used by InputDecorator
|
||||||
boxToBaseline[icon] = _layoutLineBox(icon, boxConstraints);
|
final double iconWidth = (icon?..layout(boxConstraints, parentUsesSize: true))?.size.width ?? 0.0;
|
||||||
final BoxConstraints containerConstraints = boxConstraints.copyWith(
|
final BoxConstraints containerConstraints = boxConstraints.deflate(EdgeInsets.only(left: iconWidth));
|
||||||
maxWidth: boxConstraints.maxWidth - _boxSize(icon).width,
|
final BoxConstraints contentConstraints = containerConstraints.deflate(EdgeInsets.only(left: contentPadding.horizontal));
|
||||||
);
|
|
||||||
boxToBaseline[prefixIcon] = _layoutLineBox(prefixIcon, containerConstraints);
|
|
||||||
boxToBaseline[suffixIcon] = _layoutLineBox(suffixIcon, containerConstraints);
|
|
||||||
final BoxConstraints contentConstraints = containerConstraints.copyWith(
|
|
||||||
maxWidth: math.max(0.0, containerConstraints.maxWidth - contentPadding.horizontal),
|
|
||||||
);
|
|
||||||
boxToBaseline[prefix] = _layoutLineBox(prefix, contentConstraints);
|
|
||||||
boxToBaseline[suffix] = _layoutLineBox(suffix, contentConstraints);
|
|
||||||
|
|
||||||
final double inputWidth = math.max(
|
|
||||||
0.0,
|
|
||||||
constraints.maxWidth - (
|
|
||||||
_boxSize(icon).width
|
|
||||||
+ (prefixIcon != null ? 0 : (textDirection == TextDirection.ltr ? contentPadding.left : contentPadding.right))
|
|
||||||
+ _boxSize(prefixIcon).width
|
|
||||||
+ _boxSize(prefix).width
|
|
||||||
+ _boxSize(suffix).width
|
|
||||||
+ _boxSize(suffixIcon).width
|
|
||||||
+ (suffixIcon != null ? 0 : (textDirection == TextDirection.ltr ? contentPadding.right : contentPadding.left))),
|
|
||||||
);
|
|
||||||
// Increase the available width for the label when it is scaled down.
|
|
||||||
final double invertedLabelScale = lerpDouble(1.00, 1 / _kFinalLabelScale, decoration.floatingLabelProgress)!;
|
|
||||||
double suffixIconWidth = _boxSize(suffixIcon).width;
|
|
||||||
if (decoration.border.isOutline) {
|
|
||||||
suffixIconWidth = lerpDouble(suffixIconWidth, 0.0, decoration.floatingLabelProgress)!;
|
|
||||||
}
|
|
||||||
final double labelWidth = math.max(
|
|
||||||
0.0,
|
|
||||||
constraints.maxWidth - (
|
|
||||||
_boxSize(icon).width
|
|
||||||
+ contentPadding.left
|
|
||||||
+ _boxSize(prefixIcon).width
|
|
||||||
+ suffixIconWidth
|
|
||||||
+ contentPadding.right),
|
|
||||||
);
|
|
||||||
boxToBaseline[label] = _layoutLineBox(
|
|
||||||
label,
|
|
||||||
boxConstraints.copyWith(maxWidth: labelWidth * invertedLabelScale),
|
|
||||||
);
|
|
||||||
boxToBaseline[hint] = _layoutLineBox(
|
|
||||||
hint,
|
|
||||||
boxConstraints.copyWith(minWidth: inputWidth, maxWidth: inputWidth),
|
|
||||||
);
|
|
||||||
boxToBaseline[counter] = _layoutLineBox(counter, contentConstraints);
|
|
||||||
|
|
||||||
// The helper or error text can occupy the full width less the space
|
// The helper or error text can occupy the full width less the space
|
||||||
// occupied by the icon and counter.
|
// occupied by the icon and counter.
|
||||||
boxToBaseline[helperError] = _layoutLineBox(
|
final _SubtextSize? subtextSize = _computeSubtextSizes(
|
||||||
helperError,
|
constraints: contentConstraints,
|
||||||
contentConstraints.copyWith(
|
layoutChild: ChildLayoutHelper.layoutChild,
|
||||||
maxWidth: math.max(0.0, contentConstraints.maxWidth - _boxSize(counter).width),
|
getBaseline: _getBaseline,
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final RenderBox? prefixIcon = this.prefixIcon;
|
||||||
|
final RenderBox? suffixIcon = this.suffixIcon;
|
||||||
|
final Size prefixIconSize = (prefixIcon?..layout(containerConstraints, parentUsesSize: true))?.size ?? Size.zero;
|
||||||
|
final Size suffixIconSize = (suffixIcon?..layout(containerConstraints, parentUsesSize: true))?.size ?? Size.zero;
|
||||||
|
final RenderBox? prefix = this.prefix;
|
||||||
|
final RenderBox? suffix = this.suffix;
|
||||||
|
final Size prefixSize = (prefix?..layout(contentConstraints, parentUsesSize: true))?.size ?? Size.zero;
|
||||||
|
final Size suffixSize = (suffix?..layout(contentConstraints, parentUsesSize: true))?.size ?? Size.zero;
|
||||||
|
|
||||||
|
final EdgeInsetsDirectional accessoryHorizontalInsets = EdgeInsetsDirectional.only(
|
||||||
|
start: iconWidth + prefixSize.width + (prefixIcon == null ? contentPadding.start : prefixIcon.size.width),
|
||||||
|
end: suffixSize.width + (suffixIcon == null ? contentPadding.end : suffixIcon.size.width),
|
||||||
|
);
|
||||||
|
|
||||||
|
final double inputWidth = math.max(0.0, constraints.maxWidth - accessoryHorizontalInsets.horizontal);
|
||||||
|
final RenderBox? label = this.label;
|
||||||
|
if (label != null) {
|
||||||
|
final double suffixIconSpace = decoration.border.isOutline
|
||||||
|
? lerpDouble(suffixIconSize.width, 0.0, decoration.floatingLabelProgress)!
|
||||||
|
: suffixIconSize.width;
|
||||||
|
final double labelWidth = math.max(
|
||||||
|
0.0,
|
||||||
|
constraints.maxWidth - (iconWidth + contentPadding.horizontal + prefixIconSize.width + suffixIconSpace),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Increase the available width for the label when it is scaled down.
|
||||||
|
final double invertedLabelScale = lerpDouble(1.00, 1 / _kFinalLabelScale, decoration.floatingLabelProgress)!;
|
||||||
|
final BoxConstraints labelConstraints = boxConstraints.copyWith(maxWidth: labelWidth * invertedLabelScale);
|
||||||
|
label.layout(labelConstraints, parentUsesSize: true);
|
||||||
|
}
|
||||||
|
|
||||||
// The height of the input needs to accommodate label above and counter and
|
// The height of the input needs to accommodate label above and counter and
|
||||||
// helperError below, when they exist.
|
// helperError below, when they exist.
|
||||||
final double labelHeight = label == null
|
final double labelHeight = label == null ? 0 : decoration.floatingLabelHeight;
|
||||||
? 0
|
|
||||||
: decoration.floatingLabelHeight;
|
|
||||||
final double topHeight = decoration.border.isOutline
|
final double topHeight = decoration.border.isOutline
|
||||||
? math.max(labelHeight - boxToBaseline[label]!, 0)
|
? math.max(labelHeight - (label?.getDistanceToBaseline(TextBaseline.alphabetic) ?? 0.0), 0.0)
|
||||||
: labelHeight;
|
: labelHeight;
|
||||||
final double counterHeight = counter == null
|
final double bottomHeight = subtextSize?.bottomHeight ?? 0.0;
|
||||||
? 0
|
|
||||||
: boxToBaseline[counter]! + subtextGap;
|
|
||||||
final bool helperErrorExists = helperError?.size != null
|
|
||||||
&& helperError!.size.height > 0;
|
|
||||||
final double helperErrorHeight = !helperErrorExists
|
|
||||||
? 0
|
|
||||||
: helperError!.size.height + subtextGap;
|
|
||||||
final double bottomHeight = math.max(
|
|
||||||
counterHeight,
|
|
||||||
helperErrorHeight,
|
|
||||||
);
|
|
||||||
final Offset densityOffset = decoration.visualDensity.baseSizeAdjustment;
|
final Offset densityOffset = decoration.visualDensity.baseSizeAdjustment;
|
||||||
boxToBaseline[input] = _layoutLineBox(
|
final BoxConstraints inputConstraints = boxConstraints
|
||||||
input,
|
.deflate(EdgeInsets.only(top: contentPadding.vertical + topHeight + bottomHeight + densityOffset.dy))
|
||||||
boxConstraints.deflate(EdgeInsets.only(
|
.tighten(width: inputWidth);
|
||||||
top: contentPadding.top + topHeight + densityOffset.dy / 2,
|
|
||||||
bottom: contentPadding.bottom + bottomHeight + densityOffset.dy / 2,
|
final RenderBox? input = this.input;
|
||||||
)).copyWith(
|
final RenderBox? hint = this.hint;
|
||||||
minWidth: inputWidth,
|
final Size inputSize = (input?..layout(inputConstraints, parentUsesSize: true))?.size ?? Size.zero;
|
||||||
maxWidth: inputWidth,
|
final Size hintSize = (hint?..layout(boxConstraints.tighten(width: inputWidth), parentUsesSize: true))?.size ?? Size.zero;
|
||||||
),
|
final double inputBaseline = input == null ? 0.0 : _getBaseline(input, inputConstraints);
|
||||||
);
|
final double hintBaseline = hint == null ? 0.0 : _getBaseline(hint, boxConstraints.tighten(width: inputWidth));
|
||||||
|
|
||||||
// The field can be occupied by a hint or by the input itself
|
// The field can be occupied by a hint or by the input itself
|
||||||
final double hintHeight = hint?.size.height ?? 0;
|
final double inputHeight = math.max(hintSize.height, inputSize.height);
|
||||||
final double inputDirectHeight = input?.size.height ?? 0;
|
final double inputInternalBaseline = math.max(inputBaseline, hintBaseline);
|
||||||
final double inputHeight = math.max(hintHeight, inputDirectHeight);
|
|
||||||
final double inputInternalBaseline = math.max(
|
|
||||||
boxToBaseline[input]!,
|
|
||||||
boxToBaseline[hint]!,
|
|
||||||
);
|
|
||||||
|
|
||||||
|
final double prefixBaseline = prefix == null ? 0.0 : _getBaseline(prefix, contentConstraints);
|
||||||
|
final double suffixBaseline = suffix == null ? 0.0 : _getBaseline(suffix, contentConstraints);
|
||||||
// Calculate the amount that prefix/suffix affects height above and below
|
// Calculate the amount that prefix/suffix affects height above and below
|
||||||
// the input.
|
// the input.
|
||||||
final double prefixHeight = prefix?.size.height ?? 0;
|
final double fixHeight = math.max(prefixBaseline, suffixBaseline);
|
||||||
final double suffixHeight = suffix?.size.height ?? 0;
|
|
||||||
final double fixHeight = math.max(
|
|
||||||
boxToBaseline[prefix]!,
|
|
||||||
boxToBaseline[suffix]!,
|
|
||||||
);
|
|
||||||
final double fixAboveInput = math.max(0, fixHeight - inputInternalBaseline);
|
final double fixAboveInput = math.max(0, fixHeight - inputInternalBaseline);
|
||||||
final double fixBelowBaseline = math.max(
|
final double fixBelowBaseline = math.max(prefixSize.height - prefixBaseline, suffixSize.height - suffixBaseline);
|
||||||
prefixHeight - boxToBaseline[prefix]!,
|
|
||||||
suffixHeight - boxToBaseline[suffix]!,
|
|
||||||
);
|
|
||||||
// TODO(justinmc): fixBelowInput should have no effect when there is no
|
// TODO(justinmc): fixBelowInput should have no effect when there is no
|
||||||
// prefix/suffix below the input.
|
// prefix/suffix below the input.
|
||||||
// https://github.com/flutter/flutter/issues/66050
|
// https://github.com/flutter/flutter/issues/66050
|
||||||
@ -1092,9 +1044,7 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Calculate the height of the input text container.
|
// Calculate the height of the input text container.
|
||||||
final double prefixIconHeight = prefixIcon?.size.height ?? 0;
|
final double fixIconHeight = math.max(prefixIconSize.height, suffixIconSize.height);
|
||||||
final double suffixIconHeight = suffixIcon?.size.height ?? 0;
|
|
||||||
final double fixIconHeight = math.max(prefixIconHeight, suffixIconHeight);
|
|
||||||
final double contentHeight = math.max(
|
final double contentHeight = math.max(
|
||||||
fixIconHeight,
|
fixIconHeight,
|
||||||
topHeight
|
topHeight
|
||||||
@ -1141,62 +1091,40 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
|
|||||||
final double maxContentHeight = containerHeight - contentPadding.vertical - topHeight - densityOffset.dy;
|
final double maxContentHeight = containerHeight - contentPadding.vertical - topHeight - densityOffset.dy;
|
||||||
final double alignableHeight = fixAboveInput + inputHeight + fixBelowInput;
|
final double alignableHeight = fixAboveInput + inputHeight + fixBelowInput;
|
||||||
final double maxVerticalOffset = maxContentHeight - alignableHeight;
|
final double maxVerticalOffset = maxContentHeight - alignableHeight;
|
||||||
final double textAlignVerticalOffset = maxVerticalOffset * textAlignVerticalFactor;
|
|
||||||
final double inputBaseline = topInputBaseline + textAlignVerticalOffset;
|
|
||||||
|
|
||||||
// The three main alignments for the baseline when an outline is present are
|
final double baseline;
|
||||||
//
|
if (_isOutlineAligned) {
|
||||||
// * top (-1.0): topmost point considering padding.
|
// The three main alignments for the baseline when an outline is present are
|
||||||
// * center (0.0): the absolute center of the input ignoring padding but
|
//
|
||||||
// accommodating the border and floating label.
|
// * top (-1.0): topmost point considering padding.
|
||||||
// * bottom (1.0): bottommost point considering padding.
|
// * center (0.0): the absolute center of the input ignoring padding but
|
||||||
//
|
// accommodating the border and floating label.
|
||||||
// That means that if the padding is uneven, center is not the exact
|
// * bottom (1.0): bottommost point considering padding.
|
||||||
// midpoint of top and bottom. To account for this, the above center and
|
//
|
||||||
// below center alignments are interpolated independently.
|
// That means that if the padding is uneven, center is not the exact
|
||||||
final double outlineCenterBaseline = inputInternalBaseline
|
// midpoint of top and bottom. To account for this, the above center and
|
||||||
+ baselineAdjustment / 2.0
|
// below center alignments are interpolated independently.
|
||||||
+ (containerHeight - inputHeight) / 2.0;
|
final double outlineCenterBaseline = inputInternalBaseline
|
||||||
final double outlineTopBaseline = topInputBaseline;
|
+ baselineAdjustment / 2.0
|
||||||
final double outlineBottomBaseline = topInputBaseline + maxVerticalOffset;
|
+ (containerHeight - inputHeight) / 2.0;
|
||||||
final double outlineBaseline = _interpolateThree(
|
final double outlineTopBaseline = topInputBaseline;
|
||||||
outlineTopBaseline,
|
final double outlineBottomBaseline = topInputBaseline + maxVerticalOffset;
|
||||||
outlineCenterBaseline,
|
baseline = _interpolateThree(
|
||||||
outlineBottomBaseline,
|
outlineTopBaseline,
|
||||||
textAlignVertical,
|
outlineCenterBaseline,
|
||||||
);
|
outlineBottomBaseline,
|
||||||
|
textAlignVertical,
|
||||||
// Find the positions of the text below the input when it exists.
|
);
|
||||||
double subtextCounterBaseline = 0;
|
} else {
|
||||||
double subtextHelperBaseline = 0;
|
final double textAlignVerticalOffset = maxVerticalOffset * textAlignVerticalFactor;
|
||||||
double subtextCounterHeight = 0;
|
baseline = topInputBaseline + textAlignVerticalOffset;
|
||||||
double subtextHelperHeight = 0;
|
|
||||||
if (counter != null) {
|
|
||||||
subtextCounterBaseline =
|
|
||||||
containerHeight + subtextGap + boxToBaseline[counter]!;
|
|
||||||
subtextCounterHeight = counter!.size.height + subtextGap;
|
|
||||||
}
|
}
|
||||||
if (helperErrorExists) {
|
|
||||||
subtextHelperBaseline =
|
|
||||||
containerHeight + subtextGap + boxToBaseline[helperError]!;
|
|
||||||
subtextHelperHeight = helperErrorHeight;
|
|
||||||
}
|
|
||||||
final double subtextBaseline = math.max(
|
|
||||||
subtextCounterBaseline,
|
|
||||||
subtextHelperBaseline,
|
|
||||||
);
|
|
||||||
final double subtextHeight = math.max(
|
|
||||||
subtextCounterHeight,
|
|
||||||
subtextHelperHeight,
|
|
||||||
);
|
|
||||||
|
|
||||||
return _RenderDecorationLayout(
|
return _RenderDecorationLayout(
|
||||||
boxToBaseline: boxToBaseline,
|
|
||||||
containerHeight: containerHeight,
|
containerHeight: containerHeight,
|
||||||
inputBaseline: inputBaseline,
|
baseline: baseline,
|
||||||
outlineBaseline: outlineBaseline,
|
subtextSize: subtextSize,
|
||||||
subtextBaseline: subtextBaseline,
|
size: Size(constraints.maxWidth, containerHeight + (subtextSize?.subtextHeight ?? 0.0)),
|
||||||
subtextHeight: subtextHeight,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1207,50 +1135,37 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
|
|||||||
// alignment is greater than zero, it interpolates between the centered box's
|
// alignment is greater than zero, it interpolates between the centered box's
|
||||||
// top and the position that would align the bottom of the box with the bottom
|
// top and the position that would align the bottom of the box with the bottom
|
||||||
// padding.
|
// padding.
|
||||||
double _interpolateThree(double begin, double middle, double end, TextAlignVertical textAlignVertical) {
|
static double _interpolateThree(double begin, double middle, double end, TextAlignVertical textAlignVertical) {
|
||||||
if (textAlignVertical.y <= 0) {
|
// It's possible for begin, middle, and end to not be in order because of
|
||||||
// It's possible for begin, middle, and end to not be in order because of
|
// excessive padding. Those cases are handled by using middle.
|
||||||
// excessive padding. Those cases are handled by using middle.
|
final double basis = textAlignVertical.y <= 0
|
||||||
if (begin >= middle) {
|
? math.max(middle - begin, 0)
|
||||||
return middle;
|
: math.max(end - middle, 0);
|
||||||
}
|
return middle + basis * textAlignVertical.y;
|
||||||
// Do a standard linear interpolation on the first half, between begin and
|
|
||||||
// middle.
|
|
||||||
final double t = textAlignVertical.y + 1;
|
|
||||||
return begin + (middle - begin) * t;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (middle >= end) {
|
|
||||||
return middle;
|
|
||||||
}
|
|
||||||
// Do a standard linear interpolation on the second half, between middle and
|
|
||||||
// end.
|
|
||||||
final double t = textAlignVertical.y;
|
|
||||||
return middle + (end - middle) * t;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
double computeMinIntrinsicWidth(double height) {
|
double computeMinIntrinsicWidth(double height) {
|
||||||
return _minWidth(icon, height)
|
return _minWidth(icon, height)
|
||||||
+ (prefixIcon != null ? 0.0 : (textDirection == TextDirection.ltr ? contentPadding.left : contentPadding.right))
|
+ (prefixIcon != null ? 0.0 : contentPadding.start)
|
||||||
+ _minWidth(prefixIcon, height)
|
+ _minWidth(prefixIcon, height)
|
||||||
+ _minWidth(prefix, height)
|
+ _minWidth(prefix, height)
|
||||||
+ math.max(_minWidth(input, height), _minWidth(hint, height))
|
+ math.max(_minWidth(input, height), _minWidth(hint, height))
|
||||||
+ _minWidth(suffix, height)
|
+ _minWidth(suffix, height)
|
||||||
+ _minWidth(suffixIcon, height)
|
+ _minWidth(suffixIcon, height)
|
||||||
+ (suffixIcon != null ? 0.0 : (textDirection == TextDirection.ltr ? contentPadding.right : contentPadding.left));
|
+ (suffixIcon != null ? 0.0 : contentPadding.end);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
double computeMaxIntrinsicWidth(double height) {
|
double computeMaxIntrinsicWidth(double height) {
|
||||||
return _maxWidth(icon, height)
|
return _maxWidth(icon, height)
|
||||||
+ (prefixIcon != null ? 0.0 : (textDirection == TextDirection.ltr ? contentPadding.left : contentPadding.right))
|
+ (prefixIcon != null ? 0.0 : contentPadding.start)
|
||||||
+ _maxWidth(prefixIcon, height)
|
+ _maxWidth(prefixIcon, height)
|
||||||
+ _maxWidth(prefix, height)
|
+ _maxWidth(prefix, height)
|
||||||
+ math.max(_maxWidth(input, height), _maxWidth(hint, height))
|
+ math.max(_maxWidth(input, height), _maxWidth(hint, height))
|
||||||
+ _maxWidth(suffix, height)
|
+ _maxWidth(suffix, height)
|
||||||
+ _maxWidth(suffixIcon, height)
|
+ _maxWidth(suffixIcon, height)
|
||||||
+ (suffixIcon != null ? 0.0 : (textDirection == TextDirection.ltr ? contentPadding.right : contentPadding.left));
|
+ (suffixIcon != null ? 0.0 : contentPadding.end);
|
||||||
}
|
}
|
||||||
|
|
||||||
double _lineHeight(double width, List<RenderBox?> boxes) {
|
double _lineHeight(double width, List<RenderBox?> boxes) {
|
||||||
@ -1282,6 +1197,8 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
|
|||||||
|
|
||||||
width = math.max(width - contentPadding.horizontal, 0.0);
|
width = math.max(width - contentPadding.horizontal, 0.0);
|
||||||
|
|
||||||
|
// TODO(LongCatIsLooong): use _computeSubtextSizes for subtext intrinsic sizes.
|
||||||
|
// See https://github.com/flutter/flutter/issues/13715.
|
||||||
final double counterHeight = _minHeight(counter, width);
|
final double counterHeight = _minHeight(counter, width);
|
||||||
final double counterWidth = _minWidth(counter, counterHeight);
|
final double counterWidth = _minWidth(counter, counterHeight);
|
||||||
|
|
||||||
@ -1312,6 +1229,7 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
|
|||||||
final double minContainerHeight = decoration.isDense! || expands
|
final double minContainerHeight = decoration.isDense! || expands
|
||||||
? 0.0
|
? 0.0
|
||||||
: kMinInteractiveDimension;
|
: kMinInteractiveDimension;
|
||||||
|
|
||||||
return math.max(containerHeight, minContainerHeight) + subtextHeight;
|
return math.max(containerHeight, minContainerHeight) + subtextHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1339,43 +1257,16 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
|
|||||||
return Size.zero;
|
return Size.zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChildSemanticsConfigurationsResult _childSemanticsConfigurationDelegate(List<SemanticsConfiguration> childConfigs) {
|
|
||||||
final ChildSemanticsConfigurationsResultBuilder builder = ChildSemanticsConfigurationsResultBuilder();
|
|
||||||
List<SemanticsConfiguration>? prefixMergeGroup;
|
|
||||||
List<SemanticsConfiguration>? suffixMergeGroup;
|
|
||||||
for (final SemanticsConfiguration childConfig in childConfigs) {
|
|
||||||
if (childConfig.tagsChildrenWith(_InputDecoratorState._kPrefixSemanticsTag)) {
|
|
||||||
prefixMergeGroup ??= <SemanticsConfiguration>[];
|
|
||||||
prefixMergeGroup.add(childConfig);
|
|
||||||
} else if (childConfig.tagsChildrenWith(_InputDecoratorState._kSuffixSemanticsTag)) {
|
|
||||||
suffixMergeGroup ??= <SemanticsConfiguration>[];
|
|
||||||
suffixMergeGroup.add(childConfig);
|
|
||||||
} else {
|
|
||||||
builder.markAsMergeUp(childConfig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (prefixMergeGroup != null) {
|
|
||||||
builder.markAsSiblingMergeGroup(prefixMergeGroup);
|
|
||||||
}
|
|
||||||
if (suffixMergeGroup != null) {
|
|
||||||
builder.markAsSiblingMergeGroup(suffixMergeGroup);
|
|
||||||
}
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void describeSemanticsConfiguration(SemanticsConfiguration config) {
|
|
||||||
config.childConfigurationsDelegate = _childSemanticsConfigurationDelegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void performLayout() {
|
void performLayout() {
|
||||||
final BoxConstraints constraints = this.constraints;
|
final BoxConstraints constraints = this.constraints;
|
||||||
_labelTransform = null;
|
_labelTransform = null;
|
||||||
final _RenderDecorationLayout layout = _layout(constraints);
|
final _RenderDecorationLayout layout = _layout(constraints);
|
||||||
|
size = constraints.constrain(layout.size);
|
||||||
|
assert(size.width == constraints.constrainWidth(layout.size.width));
|
||||||
|
assert(size.height == constraints.constrainHeight(layout.size.height));
|
||||||
|
|
||||||
final double overallWidth = constraints.maxWidth;
|
final double overallWidth = layout.size.width;
|
||||||
final double overallHeight = layout.containerHeight + layout.subtextHeight;
|
|
||||||
|
|
||||||
final RenderBox? container = this.container;
|
final RenderBox? container = this.container;
|
||||||
if (container != null) {
|
if (container != null) {
|
||||||
@ -1391,24 +1282,12 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
|
|||||||
_boxParentData(container).offset = Offset(x, 0.0);
|
_boxParentData(container).offset = Offset(x, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
late double height;
|
final double height = layout.containerHeight;
|
||||||
double centerLayout(RenderBox box, double x) {
|
double centerLayout(RenderBox box, double x) {
|
||||||
_boxParentData(box).offset = Offset(x, (height - box.size.height) / 2.0);
|
_boxParentData(box).offset = Offset(x, (height - box.size.height) / 2.0);
|
||||||
return box.size.width;
|
return box.size.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
late double baseline;
|
|
||||||
double baselineLayout(RenderBox box, double x) {
|
|
||||||
_boxParentData(box).offset = Offset(x, baseline - layout.boxToBaseline[box]!);
|
|
||||||
return box.size.width;
|
|
||||||
}
|
|
||||||
|
|
||||||
final double left = contentPadding.left;
|
|
||||||
final double right = overallWidth - contentPadding.right;
|
|
||||||
|
|
||||||
height = layout.containerHeight;
|
|
||||||
baseline = _isOutlineAligned ? layout.outlineBaseline : layout.inputBaseline;
|
|
||||||
|
|
||||||
if (icon != null) {
|
if (icon != null) {
|
||||||
final double x = switch (textDirection) {
|
final double x = switch (textDirection) {
|
||||||
TextDirection.rtl => overallWidth - icon!.size.width,
|
TextDirection.rtl => overallWidth - icon!.size.width,
|
||||||
@ -1417,12 +1296,39 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
|
|||||||
centerLayout(icon!, x);
|
centerLayout(icon!, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final double subtextBaseline = (layout.subtextSize?.ascent ?? 0.0) + layout.containerHeight;
|
||||||
|
final RenderBox? counter = this.counter;
|
||||||
|
final double helperErrorBaseline = helperError.getDistanceToBaseline(TextBaseline.alphabetic)!;
|
||||||
|
final double counterBaseline = counter?.getDistanceToBaseline(TextBaseline.alphabetic)! ?? 0.0;
|
||||||
|
|
||||||
|
double start, end;
|
||||||
|
switch (textDirection) {
|
||||||
|
case TextDirection.ltr:
|
||||||
|
start = contentPadding.start + _boxSize(icon).width;
|
||||||
|
end = overallWidth - contentPadding.end;
|
||||||
|
_boxParentData(helperError).offset = Offset(start, subtextBaseline - helperErrorBaseline);
|
||||||
|
if (counter != null) {
|
||||||
|
_boxParentData(counter).offset = Offset(end - counter.size.width, subtextBaseline - counterBaseline);
|
||||||
|
}
|
||||||
|
case TextDirection.rtl:
|
||||||
|
start = overallWidth - contentPadding.start - _boxSize(icon).width;
|
||||||
|
end = contentPadding.end;
|
||||||
|
_boxParentData(helperError).offset = Offset(start - helperError.size.width, subtextBaseline - helperErrorBaseline);
|
||||||
|
if (counter != null) {
|
||||||
|
_boxParentData(counter).offset = Offset(end, subtextBaseline - counterBaseline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final double baseline = layout.baseline;
|
||||||
|
double baselineLayout(RenderBox box, double x) {
|
||||||
|
_boxParentData(box).offset = Offset(x, baseline - box.getDistanceToBaseline(TextBaseline.alphabetic)!);
|
||||||
|
return box.size.width;
|
||||||
|
}
|
||||||
|
|
||||||
switch (textDirection) {
|
switch (textDirection) {
|
||||||
case TextDirection.rtl: {
|
case TextDirection.rtl: {
|
||||||
double start = right - _boxSize(icon).width;
|
|
||||||
double end = left;
|
|
||||||
if (prefixIcon != null) {
|
if (prefixIcon != null) {
|
||||||
start += contentPadding.right;
|
start += contentPadding.start;
|
||||||
start -= centerLayout(prefixIcon!, start - prefixIcon!.size.width);
|
start -= centerLayout(prefixIcon!, start - prefixIcon!.size.width);
|
||||||
}
|
}
|
||||||
if (label != null) {
|
if (label != null) {
|
||||||
@ -1442,7 +1348,7 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
|
|||||||
baselineLayout(hint!, start - hint!.size.width);
|
baselineLayout(hint!, start - hint!.size.width);
|
||||||
}
|
}
|
||||||
if (suffixIcon != null) {
|
if (suffixIcon != null) {
|
||||||
end -= contentPadding.left;
|
end -= contentPadding.end;
|
||||||
end += centerLayout(suffixIcon!, end);
|
end += centerLayout(suffixIcon!, end);
|
||||||
}
|
}
|
||||||
if (suffix != null) {
|
if (suffix != null) {
|
||||||
@ -1451,10 +1357,8 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TextDirection.ltr: {
|
case TextDirection.ltr: {
|
||||||
double start = left + _boxSize(icon).width;
|
|
||||||
double end = right;
|
|
||||||
if (prefixIcon != null) {
|
if (prefixIcon != null) {
|
||||||
start -= contentPadding.left;
|
start -= contentPadding.start;
|
||||||
start += centerLayout(prefixIcon!, start);
|
start += centerLayout(prefixIcon!, start);
|
||||||
}
|
}
|
||||||
if (label != null) {
|
if (label != null) {
|
||||||
@ -1474,7 +1378,7 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
|
|||||||
baselineLayout(hint!, start);
|
baselineLayout(hint!, start);
|
||||||
}
|
}
|
||||||
if (suffixIcon != null) {
|
if (suffixIcon != null) {
|
||||||
end += contentPadding.right;
|
end += contentPadding.end;
|
||||||
end -= centerLayout(suffixIcon!, end - suffixIcon!.size.width);
|
end -= centerLayout(suffixIcon!, end - suffixIcon!.size.width);
|
||||||
}
|
}
|
||||||
if (suffix != null) {
|
if (suffix != null) {
|
||||||
@ -1484,28 +1388,6 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (helperError != null || counter != null) {
|
|
||||||
height = layout.subtextHeight;
|
|
||||||
baseline = layout.subtextBaseline;
|
|
||||||
|
|
||||||
switch (textDirection) {
|
|
||||||
case TextDirection.rtl:
|
|
||||||
if (helperError != null) {
|
|
||||||
baselineLayout(helperError!, right - helperError!.size.width - _boxSize(icon).width);
|
|
||||||
}
|
|
||||||
if (counter != null) {
|
|
||||||
baselineLayout(counter!, left);
|
|
||||||
}
|
|
||||||
case TextDirection.ltr:
|
|
||||||
if (helperError != null) {
|
|
||||||
baselineLayout(helperError!, left + _boxSize(icon).width);
|
|
||||||
}
|
|
||||||
if (counter != null) {
|
|
||||||
baselineLayout(counter!, right - counter!.size.width);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (label != null) {
|
if (label != null) {
|
||||||
final double labelX = _boxParentData(label!).offset.dx;
|
final double labelX = _boxParentData(label!).offset.dx;
|
||||||
// +1 shifts the range of x from (-1.0, 1.0) to (0.0, 2.0).
|
// +1 shifts the range of x from (-1.0, 1.0) to (0.0, 2.0).
|
||||||
@ -1517,7 +1399,7 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
|
|||||||
case TextDirection.rtl:
|
case TextDirection.rtl:
|
||||||
double offsetToPrefixIcon = 0.0;
|
double offsetToPrefixIcon = 0.0;
|
||||||
if (prefixIcon != null && !decoration.alignLabelWithHint) {
|
if (prefixIcon != null && !decoration.alignLabelWithHint) {
|
||||||
offsetToPrefixIcon = material3 ? _boxSize(prefixIcon).width - left : 0;
|
offsetToPrefixIcon = material3 ? _boxSize(prefixIcon).width - contentPadding.end : 0;
|
||||||
}
|
}
|
||||||
decoration.borderGap.start = lerpDouble(labelX + _boxSize(label).width + offsetToPrefixIcon,
|
decoration.borderGap.start = lerpDouble(labelX + _boxSize(label).width + offsetToPrefixIcon,
|
||||||
_boxSize(container).width / 2.0 + floatWidth / 2.0,
|
_boxSize(container).width / 2.0 + floatWidth / 2.0,
|
||||||
@ -1529,7 +1411,7 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
|
|||||||
// floating label is centered, it's already relative to _BorderContainer.
|
// floating label is centered, it's already relative to _BorderContainer.
|
||||||
double offsetToPrefixIcon = 0.0;
|
double offsetToPrefixIcon = 0.0;
|
||||||
if (prefixIcon != null && !decoration.alignLabelWithHint) {
|
if (prefixIcon != null && !decoration.alignLabelWithHint) {
|
||||||
offsetToPrefixIcon = material3 ? (-_boxSize(prefixIcon).width + left) : 0;
|
offsetToPrefixIcon = material3 ? (-_boxSize(prefixIcon).width + contentPadding.start) : 0;
|
||||||
}
|
}
|
||||||
decoration.borderGap.start = lerpDouble(labelX - _boxSize(icon).width + offsetToPrefixIcon,
|
decoration.borderGap.start = lerpDouble(labelX - _boxSize(icon).width + offsetToPrefixIcon,
|
||||||
_boxSize(container).width / 2.0 - floatWidth / 2.0,
|
_boxSize(container).width / 2.0 - floatWidth / 2.0,
|
||||||
@ -1540,10 +1422,6 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
|
|||||||
decoration.borderGap.start = null;
|
decoration.borderGap.start = null;
|
||||||
decoration.borderGap.extent = 0.0;
|
decoration.borderGap.extent = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size = constraints.constrain(Size(overallWidth, overallHeight));
|
|
||||||
assert(size.width == constraints.constrainWidth(overallWidth));
|
|
||||||
assert(size.height == constraints.constrainHeight(overallHeight));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _paintLabel(PaintingContext context, Offset offset) {
|
void _paintLabel(PaintingContext context, Offset offset) {
|
||||||
@ -1584,13 +1462,13 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
|
|||||||
startX = labelOffset.dx + labelWidth * (1.0 - scale);
|
startX = labelOffset.dx + labelWidth * (1.0 - scale);
|
||||||
floatStartX = startX;
|
floatStartX = startX;
|
||||||
if (prefixIcon != null && !decoration.alignLabelWithHint && isOutlineBorder) {
|
if (prefixIcon != null && !decoration.alignLabelWithHint && isOutlineBorder) {
|
||||||
floatStartX += material3 ? _boxSize(prefixIcon).width - contentPadding.left : 0.0;
|
floatStartX += material3 ? _boxSize(prefixIcon).width - contentPadding.end : 0.0;
|
||||||
}
|
}
|
||||||
case TextDirection.ltr: // origin on the left
|
case TextDirection.ltr: // origin on the left
|
||||||
startX = labelOffset.dx;
|
startX = labelOffset.dx;
|
||||||
floatStartX = startX;
|
floatStartX = startX;
|
||||||
if (prefixIcon != null && !decoration.alignLabelWithHint && isOutlineBorder) {
|
if (prefixIcon != null && !decoration.alignLabelWithHint && isOutlineBorder) {
|
||||||
floatStartX += material3 ? -_boxSize(prefixIcon).width + contentPadding.left : 0.0;
|
floatStartX += material3 ? -_boxSize(prefixIcon).width + contentPadding.start : 0.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final double floatEndX = lerpDouble(floatStartX, centeredFloatX, floatAlign)!;
|
final double floatEndX = lerpDouble(floatStartX, centeredFloatX, floatAlign)!;
|
||||||
@ -1621,6 +1499,17 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
|
|||||||
doPaint(counter);
|
doPaint(counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void applyPaintTransform(RenderObject child, Matrix4 transform) {
|
||||||
|
if (child == label && _labelTransform != null) {
|
||||||
|
final Offset labelOffset = _boxParentData(label!).offset;
|
||||||
|
transform
|
||||||
|
..multiply(_labelTransform!)
|
||||||
|
..translate(-labelOffset.dx, -labelOffset.dy);
|
||||||
|
}
|
||||||
|
super.applyPaintTransform(child, transform);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool hitTestSelf(Offset position) => true;
|
bool hitTestSelf(Offset position) => true;
|
||||||
|
|
||||||
@ -1644,15 +1533,33 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
ChildSemanticsConfigurationsResult _childSemanticsConfigurationDelegate(List<SemanticsConfiguration> childConfigs) {
|
||||||
void applyPaintTransform(RenderObject child, Matrix4 transform) {
|
final ChildSemanticsConfigurationsResultBuilder builder = ChildSemanticsConfigurationsResultBuilder();
|
||||||
if (child == label && _labelTransform != null) {
|
List<SemanticsConfiguration>? prefixMergeGroup;
|
||||||
final Offset labelOffset = _boxParentData(label!).offset;
|
List<SemanticsConfiguration>? suffixMergeGroup;
|
||||||
transform
|
for (final SemanticsConfiguration childConfig in childConfigs) {
|
||||||
..multiply(_labelTransform!)
|
if (childConfig.tagsChildrenWith(_InputDecoratorState._kPrefixSemanticsTag)) {
|
||||||
..translate(-labelOffset.dx, -labelOffset.dy);
|
prefixMergeGroup ??= <SemanticsConfiguration>[];
|
||||||
|
prefixMergeGroup.add(childConfig);
|
||||||
|
} else if (childConfig.tagsChildrenWith(_InputDecoratorState._kSuffixSemanticsTag)) {
|
||||||
|
suffixMergeGroup ??= <SemanticsConfiguration>[];
|
||||||
|
suffixMergeGroup.add(childConfig);
|
||||||
|
} else {
|
||||||
|
builder.markAsMergeUp(childConfig);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
super.applyPaintTransform(child, transform);
|
if (prefixMergeGroup != null) {
|
||||||
|
builder.markAsSiblingMergeGroup(prefixMergeGroup);
|
||||||
|
}
|
||||||
|
if (suffixMergeGroup != null) {
|
||||||
|
builder.markAsSiblingMergeGroup(suffixMergeGroup);
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void describeSemanticsConfiguration(SemanticsConfiguration config) {
|
||||||
|
config.childConfigurationsDelegate = _childSemanticsConfigurationDelegate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2416,46 +2323,58 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
|
|||||||
// The _Decoration widget and _RenderDecoration assume that contentPadding
|
// The _Decoration widget and _RenderDecoration assume that contentPadding
|
||||||
// has been resolved to EdgeInsets.
|
// has been resolved to EdgeInsets.
|
||||||
final TextDirection textDirection = Directionality.of(context);
|
final TextDirection textDirection = Directionality.of(context);
|
||||||
final EdgeInsets? decorationContentPadding = decoration.contentPadding?.resolve(textDirection);
|
final bool flipHorizontal = switch (textDirection) {
|
||||||
|
TextDirection.ltr => false,
|
||||||
|
TextDirection.rtl => true,
|
||||||
|
};
|
||||||
|
final EdgeInsets? resolvedPadding = decoration.contentPadding?.resolve(textDirection);
|
||||||
|
final EdgeInsetsDirectional? decorationContentPadding = resolvedPadding == null
|
||||||
|
? null
|
||||||
|
: EdgeInsetsDirectional.fromSTEB(
|
||||||
|
flipHorizontal ? resolvedPadding.right : resolvedPadding.left,
|
||||||
|
resolvedPadding.top,
|
||||||
|
flipHorizontal ? resolvedPadding.left : resolvedPadding.right,
|
||||||
|
resolvedPadding.bottom,
|
||||||
|
);
|
||||||
|
|
||||||
final EdgeInsets contentPadding;
|
final EdgeInsetsDirectional contentPadding;
|
||||||
final double floatingLabelHeight;
|
final double floatingLabelHeight;
|
||||||
|
|
||||||
if (decoration.isCollapsed ?? themeData.inputDecorationTheme.isCollapsed) {
|
if (decoration.isCollapsed ?? themeData.inputDecorationTheme.isCollapsed) {
|
||||||
floatingLabelHeight = 0.0;
|
floatingLabelHeight = 0.0;
|
||||||
contentPadding = decorationContentPadding ?? EdgeInsets.zero;
|
contentPadding = decorationContentPadding ?? EdgeInsetsDirectional.zero;
|
||||||
} else if (!border.isOutline) {
|
} else if (!border.isOutline) {
|
||||||
// 4.0: the vertical gap between the inline elements and the floating label.
|
// 4.0: the vertical gap between the inline elements and the floating label.
|
||||||
floatingLabelHeight = MediaQuery.textScalerOf(context).scale(4.0 + 0.75 * labelStyle.fontSize!);
|
floatingLabelHeight = MediaQuery.textScalerOf(context).scale(4.0 + 0.75 * labelStyle.fontSize!);
|
||||||
if (decoration.filled ?? false) {
|
if (decoration.filled ?? false) {
|
||||||
contentPadding = decorationContentPadding ?? (Theme.of(context).useMaterial3
|
contentPadding = decorationContentPadding ?? (Theme.of(context).useMaterial3
|
||||||
? decorationIsDense
|
? decorationIsDense
|
||||||
? const EdgeInsets.fromLTRB(12.0, 4.0, 12.0, 4.0)
|
? const EdgeInsetsDirectional.fromSTEB(12.0, 4.0, 12.0, 4.0)
|
||||||
: const EdgeInsets.fromLTRB(12.0, 8.0, 12.0, 8.0)
|
: const EdgeInsetsDirectional.fromSTEB(12.0, 8.0, 12.0, 8.0)
|
||||||
: decorationIsDense
|
: decorationIsDense
|
||||||
? const EdgeInsets.fromLTRB(12.0, 8.0, 12.0, 8.0)
|
? const EdgeInsetsDirectional.fromSTEB(12.0, 8.0, 12.0, 8.0)
|
||||||
: const EdgeInsets.fromLTRB(12.0, 12.0, 12.0, 12.0));
|
: const EdgeInsetsDirectional.fromSTEB(12.0, 12.0, 12.0, 12.0));
|
||||||
} else {
|
} else {
|
||||||
// No left or right padding for underline borders that aren't filled
|
// No left or right padding for underline borders that aren't filled
|
||||||
// is a small concession to backwards compatibility. This eliminates
|
// is a small concession to backwards compatibility. This eliminates
|
||||||
// the most noticeable layout change introduced by #13734.
|
// the most noticeable layout change introduced by #13734.
|
||||||
contentPadding = decorationContentPadding ?? (Theme.of(context).useMaterial3
|
contentPadding = decorationContentPadding ?? (Theme.of(context).useMaterial3
|
||||||
? decorationIsDense
|
? decorationIsDense
|
||||||
? const EdgeInsets.fromLTRB(0.0, 4.0, 0.0, 4.0)
|
? const EdgeInsetsDirectional.fromSTEB(0.0, 4.0, 0.0, 4.0)
|
||||||
: const EdgeInsets.fromLTRB(0.0, 8.0, 0.0, 8.0)
|
: const EdgeInsetsDirectional.fromSTEB(0.0, 8.0, 0.0, 8.0)
|
||||||
: decorationIsDense
|
: decorationIsDense
|
||||||
? const EdgeInsets.fromLTRB(0.0, 8.0, 0.0, 8.0)
|
? const EdgeInsetsDirectional.fromSTEB(0.0, 8.0, 0.0, 8.0)
|
||||||
: const EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 12.0));
|
: const EdgeInsetsDirectional.fromSTEB(0.0, 12.0, 0.0, 12.0));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
floatingLabelHeight = 0.0;
|
floatingLabelHeight = 0.0;
|
||||||
contentPadding = decorationContentPadding ?? (Theme.of(context).useMaterial3
|
contentPadding = decorationContentPadding ?? (Theme.of(context).useMaterial3
|
||||||
? decorationIsDense
|
? decorationIsDense
|
||||||
? const EdgeInsets.fromLTRB(12.0, 16.0, 12.0, 8.0)
|
? const EdgeInsetsDirectional.fromSTEB(12.0, 16.0, 12.0, 8.0)
|
||||||
: const EdgeInsets.fromLTRB(12.0, 20.0, 12.0, 12.0)
|
: const EdgeInsetsDirectional.fromSTEB(12.0, 20.0, 12.0, 12.0)
|
||||||
: decorationIsDense
|
: decorationIsDense
|
||||||
? const EdgeInsets.fromLTRB(12.0, 20.0, 12.0, 12.0)
|
? const EdgeInsetsDirectional.fromSTEB(12.0, 20.0, 12.0, 12.0)
|
||||||
: const EdgeInsets.fromLTRB(12.0, 24.0, 12.0, 16.0));
|
: const EdgeInsetsDirectional.fromSTEB(12.0, 24.0, 12.0, 16.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
final _Decorator decorator = _Decorator(
|
final _Decorator decorator = _Decorator(
|
||||||
|
@ -4533,43 +4533,6 @@ void main() {
|
|||||||
expect(intrinsicHeight, equals(height));
|
expect(intrinsicHeight, equals(height));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Error message for negative baseline', (WidgetTester tester) async {
|
|
||||||
FlutterErrorDetails? errorDetails;
|
|
||||||
final FlutterExceptionHandler? oldHandler = FlutterError.onError;
|
|
||||||
FlutterError.onError = (FlutterErrorDetails details) {
|
|
||||||
errorDetails ??= details;
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
await tester.pumpWidget(
|
|
||||||
const MaterialApp(
|
|
||||||
home: Center(
|
|
||||||
child: Directionality(
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
child: InputDecorator(
|
|
||||||
decoration: InputDecoration(),
|
|
||||||
child: Stack(
|
|
||||||
children: <Widget>[
|
|
||||||
SizedBox(height: 0),
|
|
||||||
Positioned(
|
|
||||||
bottom: 5,
|
|
||||||
child: Text('ok'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
phase: EnginePhase.layout,
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
FlutterError.onError = oldHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(errorDetails?.toString(), contains("InputDecorator's children reported a negative baseline"));
|
|
||||||
expect(errorDetails?.toString(), contains('RenderStack'));
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('Min intrinsic height for TextField with no content padding', (WidgetTester tester) async {
|
testWidgets('Min intrinsic height for TextField with no content padding', (WidgetTester tester) async {
|
||||||
// Regression test for: https://github.com/flutter/flutter/issues/75509
|
// Regression test for: https://github.com/flutter/flutter/issues/75509
|
||||||
await tester.pumpWidget(const MaterialApp(
|
await tester.pumpWidget(const MaterialApp(
|
||||||
|
Loading…
Reference in New Issue
Block a user