Skip to content

Commit 25e88cb

Browse files
authored
Fix OutlineInputBorder with BorderRadius.zero is distorted (#106849)
1 parent 7714a8d commit 25e88cb

File tree

2 files changed

+100
-18
lines changed

2 files changed

+100
-18
lines changed

packages/flutter/lib/src/material/input_border.dart

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -439,44 +439,63 @@ class OutlineInputBorder extends InputBorder {
439439
scaledRRect.left,
440440
scaledRRect.bottom - scaledRRect.blRadiusY * 2.0,
441441
scaledRRect.blRadiusX * 2.0,
442-
scaledRRect.blRadiusX * 2.0,
442+
scaledRRect.blRadiusY * 2.0,
443443
);
444444

445445
// This assumes that the radius is circular (x and y radius are equal).
446446
// Currently, BorderRadius only supports circular radii.
447447
const double cornerArcSweep = math.pi / 2.0;
448-
final double tlCornerArcSweep = math.acos(
449-
clampDouble(1 - start / scaledRRect.tlRadiusX, 0.0, 1.0),
450-
);
448+
final Path path = Path();
451449

452-
final Path path = Path()
453-
..addArc(tlCorner, math.pi, tlCornerArcSweep);
450+
// Top left corner
451+
if (scaledRRect.tlRadius != Radius.zero) {
452+
final double tlCornerArcSweep = math.acos(clampDouble(1 - start / scaledRRect.tlRadiusX, 0.0, 1.0));
453+
path.addArc(tlCorner, math.pi, tlCornerArcSweep);
454+
} else {
455+
// Because the path is painted with Paint.strokeCap = StrokeCap.butt, horizontal coordinate is moved
456+
// to the left using borderSide.width / 2.
457+
path.moveTo(scaledRRect.left - borderSide.width / 2, scaledRRect.top);
458+
}
454459

460+
// Draw top border from top left corner to gap start.
455461
if (start > scaledRRect.tlRadiusX) {
456462
path.lineTo(scaledRRect.left + start, scaledRRect.top);
457463
}
458464

465+
// Draw top border from gap end to top right corner and draw top right corner.
459466
const double trCornerArcStart = (3 * math.pi) / 2.0;
460467
const double trCornerArcSweep = cornerArcSweep;
461468
if (start + extent < scaledRRect.width - scaledRRect.trRadiusX) {
462469
path.moveTo(scaledRRect.left + start + extent, scaledRRect.top);
463470
path.lineTo(scaledRRect.right - scaledRRect.trRadiusX, scaledRRect.top);
464-
path.addArc(trCorner, trCornerArcStart, trCornerArcSweep);
471+
if (scaledRRect.trRadius != Radius.zero) {
472+
path.addArc(trCorner, trCornerArcStart, trCornerArcSweep);
473+
}
465474
} else if (start + extent < scaledRRect.width) {
466475
final double dx = scaledRRect.width - (start + extent);
467-
final double sweep = math.asin(
468-
clampDouble(1 - dx / scaledRRect.trRadiusX, 0.0, 1.0),
469-
);
476+
final double sweep = math.asin(clampDouble(1 - dx / scaledRRect.trRadiusX, 0.0, 1.0));
470477
path.addArc(trCorner, trCornerArcStart + sweep, trCornerArcSweep - sweep);
471478
}
472479

473-
return path
474-
..moveTo(scaledRRect.right, scaledRRect.top + scaledRRect.trRadiusY)
475-
..lineTo(scaledRRect.right, scaledRRect.bottom - scaledRRect.brRadiusY)
476-
..addArc(brCorner, 0.0, cornerArcSweep)
477-
..lineTo(scaledRRect.left + scaledRRect.blRadiusX, scaledRRect.bottom)
478-
..addArc(blCorner, math.pi / 2.0, cornerArcSweep)
479-
..lineTo(scaledRRect.left, scaledRRect.top + scaledRRect.tlRadiusY);
480+
// Draw right border and bottom right corner.
481+
if (scaledRRect.brRadius != Radius.zero) {
482+
path.moveTo(scaledRRect.right, scaledRRect.top + scaledRRect.trRadiusY);
483+
}
484+
path.lineTo(scaledRRect.right, scaledRRect.bottom - scaledRRect.brRadiusY);
485+
if (scaledRRect.brRadius != Radius.zero) {
486+
path.addArc(brCorner, 0.0, cornerArcSweep);
487+
}
488+
489+
// Draw bottom border and bottom left corner.
490+
path.lineTo(scaledRRect.left + scaledRRect.blRadiusX, scaledRRect.bottom);
491+
if (scaledRRect.blRadius != Radius.zero) {
492+
path.addArc(blCorner, math.pi / 2.0, cornerArcSweep);
493+
}
494+
495+
// Draw left border
496+
path.lineTo(scaledRRect.left, scaledRRect.top + scaledRRect.tlRadiusY);
497+
498+
return path;
480499
}
481500

482501
/// Draw a rounded rectangle around [rect] using [borderRadius].

packages/flutter/test/material/input_decorator_test.dart

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4992,6 +4992,70 @@ void main() {
49924992
);
49934993
}, skip: isBrowser); // https://github.com/flutter/flutter/issues/55317
49944994

4995+
testWidgets('OutlineInputBorder with BorderRadius.zero should draw a rectangular border', (WidgetTester tester) async {
4996+
// Regression test for https://github.com/flutter/flutter/issues/78855
4997+
const String labelText = 'Flutter';
4998+
4999+
// Overall height for this InputDecorator is 56dps:
5000+
// 12 - top padding
5001+
// 12 - floating label (ahem font size 16dps * 0.75 = 12)
5002+
// 4 - floating label / input text gap
5003+
// 16 - input text (ahem font size 16dps)
5004+
// 12 - bottom padding
5005+
const double inputDecoratorHeight = 56.0;
5006+
const double inputDecoratorWidth = 800.0;
5007+
const double borderWidth = 4.0;
5008+
5009+
await tester.pumpWidget(
5010+
buildInputDecorator(
5011+
isFocused: true,
5012+
decoration: const InputDecoration(
5013+
filled: false,
5014+
labelText: labelText,
5015+
focusedBorder: OutlineInputBorder(
5016+
borderRadius: BorderRadius.zero,
5017+
borderSide: BorderSide(width: borderWidth, color: Colors.red),
5018+
),
5019+
),
5020+
),
5021+
);
5022+
5023+
expect(find.text(labelText), findsOneWidget);
5024+
expect(findBorderPainter(), paints
5025+
..save()
5026+
..path(
5027+
includes: const <Offset>[
5028+
// Corner points in the middle of the border line should be in the path.
5029+
// The path is not filled and borderWidth is 4.0 so Offset(2.0, 2.0) is in the path and Offset(1.0, 1.0) is not.
5030+
// See Skia SkPath::contains method.
5031+
5032+
// Top-left
5033+
Offset(borderWidth / 2, borderWidth / 2),
5034+
// Top-right
5035+
Offset(inputDecoratorWidth - 1 - borderWidth / 2, borderWidth / 2),
5036+
// Bottom-left
5037+
Offset(borderWidth / 2, inputDecoratorHeight - 1 - borderWidth / 2),
5038+
// Bottom-right
5039+
Offset(inputDecoratorWidth - 1 - borderWidth / 2, inputDecoratorHeight - 1 - borderWidth / 2),
5040+
],
5041+
excludes: const <Offset>[
5042+
// The path is not filled and borderWidth is 4.0 so the path should not contains the corner points.
5043+
// See Skia SkPath::contains method.
5044+
5045+
// Top-left
5046+
Offset.zero,
5047+
// // Top-right
5048+
Offset(inputDecoratorWidth - 1, 0),
5049+
// // Bottom-left
5050+
Offset(0, inputDecoratorHeight - 1),
5051+
// // Bottom-right
5052+
Offset(inputDecoratorWidth - 1, inputDecoratorHeight - 1),
5053+
],
5054+
)
5055+
..restore(),
5056+
);
5057+
}, skip: isBrowser); // https://github.com/flutter/flutter/issues/55317
5058+
49955059
testWidgets('OutlineInputBorder radius carries over when lerping', (WidgetTester tester) async {
49965060
// This is a regression test for https://github.com/flutter/flutter/issues/23982
49975061
const Key key = Key('textField');
@@ -5085,7 +5149,6 @@ void main() {
50855149
expect(underlineInputBorder, isNot(const UnderlineInputBorder()));
50865150
});
50875151

5088-
50895152
test('InputBorder hashCodes', () {
50905153
// OutlineInputBorder's hashCode is defined by the borderRadius, borderSide, & gapPadding
50915154
const OutlineInputBorder outlineInputBorder = OutlineInputBorder(

0 commit comments

Comments
 (0)