forked from firka/flutter
OutlineInputBorder adjusts for borderRadius that is too large (#34515)
* Implement OutlineInputBorder BorderRadius scaling via RRect.scaleRadii * Add regression test to test for border scaling
This commit is contained in:
@@ -385,63 +385,68 @@ class OutlineInputBorder extends InputBorder {
|
||||
}
|
||||
|
||||
Path _gapBorderPath(Canvas canvas, RRect center, double start, double extent) {
|
||||
// When the corner radii on any side add up to be greater than the
|
||||
// given height, each radius has to be scaled to not exceed the
|
||||
// size of the width/height of the RRect.
|
||||
final RRect scaledRRect = center.scaleRadii();
|
||||
|
||||
final Rect tlCorner = Rect.fromLTWH(
|
||||
center.left,
|
||||
center.top,
|
||||
center.tlRadiusX * 2.0,
|
||||
center.tlRadiusY * 2.0,
|
||||
scaledRRect.left,
|
||||
scaledRRect.top,
|
||||
scaledRRect.tlRadiusX * 2.0,
|
||||
scaledRRect.tlRadiusY * 2.0,
|
||||
);
|
||||
final Rect trCorner = Rect.fromLTWH(
|
||||
center.right - center.trRadiusX * 2.0,
|
||||
center.top,
|
||||
center.trRadiusX * 2.0,
|
||||
center.trRadiusY * 2.0,
|
||||
scaledRRect.right - scaledRRect.trRadiusX * 2.0,
|
||||
scaledRRect.top,
|
||||
scaledRRect.trRadiusX * 2.0,
|
||||
scaledRRect.trRadiusY * 2.0,
|
||||
);
|
||||
final Rect brCorner = Rect.fromLTWH(
|
||||
center.right - center.brRadiusX * 2.0,
|
||||
center.bottom - center.brRadiusY * 2.0,
|
||||
center.brRadiusX * 2.0,
|
||||
center.brRadiusY * 2.0,
|
||||
scaledRRect.right - scaledRRect.brRadiusX * 2.0,
|
||||
scaledRRect.bottom - scaledRRect.brRadiusY * 2.0,
|
||||
scaledRRect.brRadiusX * 2.0,
|
||||
scaledRRect.brRadiusY * 2.0,
|
||||
);
|
||||
final Rect blCorner = Rect.fromLTWH(
|
||||
center.left,
|
||||
center.bottom - center.brRadiusY * 2.0,
|
||||
center.blRadiusX * 2.0,
|
||||
center.blRadiusY * 2.0,
|
||||
scaledRRect.left,
|
||||
scaledRRect.bottom - scaledRRect.blRadiusY * 2.0,
|
||||
scaledRRect.blRadiusX * 2.0,
|
||||
scaledRRect.blRadiusX * 2.0,
|
||||
);
|
||||
|
||||
const double cornerArcSweep = math.pi / 2.0;
|
||||
final double tlCornerArcSweep = start < center.tlRadiusX
|
||||
? math.asin((start / center.tlRadiusX).clamp(-1.0, 1.0))
|
||||
final double tlCornerArcSweep = start < scaledRRect.tlRadiusX
|
||||
? math.asin((start / scaledRRect.tlRadiusX).clamp(-1.0, 1.0))
|
||||
: math.pi / 2.0;
|
||||
|
||||
final Path path = Path()
|
||||
..addArc(tlCorner, math.pi, tlCornerArcSweep)
|
||||
..moveTo(center.left + center.tlRadiusX, center.top);
|
||||
..moveTo(scaledRRect.left + scaledRRect.tlRadiusX, scaledRRect.top);
|
||||
|
||||
if (start > center.tlRadiusX)
|
||||
path.lineTo(center.left + start, center.top);
|
||||
if (start > scaledRRect.tlRadiusX)
|
||||
path.lineTo(scaledRRect.left + start, scaledRRect.top);
|
||||
|
||||
const double trCornerArcStart = (3 * math.pi) / 2.0;
|
||||
const double trCornerArcSweep = cornerArcSweep;
|
||||
if (start + extent < center.width - center.trRadiusX) {
|
||||
if (start + extent < scaledRRect.width - scaledRRect.trRadiusX) {
|
||||
path
|
||||
..relativeMoveTo(extent, 0.0)
|
||||
..lineTo(center.right - center.trRadiusX, center.top)
|
||||
..lineTo(scaledRRect.right - scaledRRect.trRadiusX, scaledRRect.top)
|
||||
..addArc(trCorner, trCornerArcStart, trCornerArcSweep);
|
||||
} else if (start + extent < center.width) {
|
||||
final double dx = center.width - (start + extent);
|
||||
final double sweep = math.acos(dx / center.trRadiusX);
|
||||
} else if (start + extent < scaledRRect.width) {
|
||||
final double dx = scaledRRect.width - (start + extent);
|
||||
final double sweep = math.acos(dx / scaledRRect.trRadiusX);
|
||||
path.addArc(trCorner, trCornerArcStart + sweep, trCornerArcSweep - sweep);
|
||||
}
|
||||
|
||||
return path
|
||||
..moveTo(center.right, center.top + center.trRadiusY)
|
||||
..lineTo(center.right, center.bottom - center.brRadiusY)
|
||||
..moveTo(scaledRRect.right, scaledRRect.top + scaledRRect.trRadiusY)
|
||||
..lineTo(scaledRRect.right, scaledRRect.bottom - scaledRRect.brRadiusY)
|
||||
..addArc(brCorner, 0.0, cornerArcSweep)
|
||||
..lineTo(center.left + center.blRadiusX, center.bottom)
|
||||
..lineTo(scaledRRect.left + scaledRRect.blRadiusX, scaledRRect.bottom)
|
||||
..addArc(blCorner, math.pi / 2.0, cornerArcSweep)
|
||||
..lineTo(center.left, center.top + center.trRadiusY);
|
||||
..lineTo(scaledRRect.left, scaledRRect.top + scaledRRect.tlRadiusY);
|
||||
}
|
||||
|
||||
/// Draw a rounded rectangle around [rect] using [borderRadius].
|
||||
|
||||
@@ -3170,6 +3170,110 @@ void main() {
|
||||
expect(getBorder(tester), disabledBorder);
|
||||
});
|
||||
|
||||
testWidgets('OutlineInputBorder borders scale down to fit when large values are passed in', (WidgetTester tester) async {
|
||||
// This is a regression test for https://github.com/flutter/flutter/issues/34327
|
||||
const double largerBorderRadius = 200.0;
|
||||
const double smallerBorderRadius = 100.0;
|
||||
|
||||
// Overall height for this InputDecorator is 56dps:
|
||||
// 12 - top padding
|
||||
// 12 - floating label (ahem font size 16dps * 0.75 = 12)
|
||||
// 4 - floating label / input text gap
|
||||
// 16 - input text (ahem font size 16dps)
|
||||
// 12 - bottom padding
|
||||
const double inputDecoratorHeight = 56.0;
|
||||
const double inputDecoratorWidth = 800.0;
|
||||
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
decoration: const InputDecoration(
|
||||
filled: true,
|
||||
fillColor: Color(0xFF00FF00),
|
||||
labelText: 'label text',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
// Intentionally large values that are larger than the InputDecorator
|
||||
topLeft: Radius.circular(smallerBorderRadius),
|
||||
bottomLeft: Radius.circular(smallerBorderRadius),
|
||||
topRight: Radius.circular(largerBorderRadius),
|
||||
bottomRight: Radius.circular(largerBorderRadius),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Skia determines the scale based on the ratios of radii to the total
|
||||
// height or width allowed. In this case, it is the right side of the
|
||||
// border, which have two corners with largerBorderRadius that add up
|
||||
// to be 400.0.
|
||||
const double denominator = largerBorderRadius * 2.0;
|
||||
|
||||
const double largerBorderRadiusScaled = largerBorderRadius / denominator * inputDecoratorHeight;
|
||||
const double smallerBorderRadiusScaled = smallerBorderRadius / denominator * inputDecoratorHeight;
|
||||
|
||||
expect(findBorderPainter(), paints
|
||||
..save()
|
||||
..path(
|
||||
style: PaintingStyle.fill,
|
||||
color: const Color(0xFF00FF00),
|
||||
includes: const <Offset>[
|
||||
// The border should draw along the four edges of the
|
||||
// InputDecorator.
|
||||
|
||||
// Top center
|
||||
Offset(inputDecoratorWidth / 2.0, 0.0),
|
||||
// Bottom center
|
||||
Offset(inputDecoratorWidth / 2.0, inputDecoratorHeight),
|
||||
// Left center
|
||||
Offset(0.0, inputDecoratorHeight / 2.0),
|
||||
// Right center
|
||||
Offset(inputDecoratorWidth, inputDecoratorHeight / 2.0),
|
||||
|
||||
// The border path should contain points where each rounded corner
|
||||
// ends.
|
||||
|
||||
// Bottom-right arc
|
||||
Offset(inputDecoratorWidth, inputDecoratorHeight - largerBorderRadiusScaled),
|
||||
Offset(inputDecoratorWidth - largerBorderRadiusScaled, inputDecoratorHeight),
|
||||
// Top-right arc
|
||||
Offset(inputDecoratorWidth,0.0 + largerBorderRadiusScaled),
|
||||
Offset(inputDecoratorWidth - largerBorderRadiusScaled, 0.0),
|
||||
// Bottom-left arc
|
||||
Offset(0.0, inputDecoratorHeight - smallerBorderRadiusScaled),
|
||||
Offset(0.0 + smallerBorderRadiusScaled, inputDecoratorHeight),
|
||||
// Top-left arc
|
||||
Offset(0.0,0.0 + smallerBorderRadiusScaled),
|
||||
Offset(0.0 + smallerBorderRadiusScaled, 0.0),
|
||||
],
|
||||
excludes: const <Offset>[
|
||||
// The border should not contain the corner points, since the border
|
||||
// is rounded.
|
||||
|
||||
// Top-left
|
||||
Offset(0.0, 0.0),
|
||||
// Top-right
|
||||
Offset(inputDecoratorWidth, 0.0),
|
||||
// Bottom-left
|
||||
Offset(0.0, inputDecoratorHeight),
|
||||
// Bottom-right
|
||||
Offset(inputDecoratorWidth, inputDecoratorHeight),
|
||||
|
||||
// Corners with larger border ratio should not contain points outside
|
||||
// of the larger radius.
|
||||
|
||||
// Bottom-right arc
|
||||
Offset(inputDecoratorWidth, inputDecoratorHeight - smallerBorderRadiusScaled),
|
||||
Offset(inputDecoratorWidth - smallerBorderRadiusScaled, inputDecoratorWidth),
|
||||
// Top-left arc
|
||||
Offset(inputDecoratorWidth, 0.0 + smallerBorderRadiusScaled),
|
||||
Offset(inputDecoratorWidth - smallerBorderRadiusScaled, 0.0),
|
||||
],
|
||||
)
|
||||
..restore()
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('OutlineInputBorder radius carries over when lerping', (WidgetTester tester) async {
|
||||
// This is a regression test for https://github.com/flutter/flutter/issues/23982
|
||||
const Key key = Key('textField');
|
||||
|
||||
Reference in New Issue
Block a user