diff --git a/packages/flutter/lib/src/painting/border_radius.dart b/packages/flutter/lib/src/painting/border_radius.dart index 8e7396ea03..ced35c0107 100644 --- a/packages/flutter/lib/src/painting/border_radius.dart +++ b/packages/flutter/lib/src/painting/border_radius.dart @@ -392,13 +392,19 @@ class BorderRadius extends BorderRadiusGeometry { Radius get _bottomEnd => Radius.zero; /// Creates an [RRect] from the current border radius and a [Rect]. + /// + /// If any of the radii have negative values in x or y, those values will be + /// clamped to zero in order to produce a valid [RRect]. RRect toRRect(Rect rect) { + // Because the current radii could be negative, we must clamp them before + // converting them to an RRect to be rendered, since negative radii on + // RRects don't make sense. return RRect.fromRectAndCorners( rect, - topLeft: topLeft, - topRight: topRight, - bottomLeft: bottomLeft, - bottomRight: bottomRight, + topLeft: topLeft.clamp(minimum: Radius.zero), // ignore_clamp_double_lint + topRight: topRight.clamp(minimum: Radius.zero), // ignore_clamp_double_lint + bottomLeft: bottomLeft.clamp(minimum: Radius.zero), // ignore_clamp_double_lint + bottomRight: bottomRight.clamp(minimum: Radius.zero), // ignore_clamp_double_lint ); } diff --git a/packages/flutter/lib/src/painting/box_border.dart b/packages/flutter/lib/src/painting/box_border.dart index 650bd5d5d8..ed6d815019 100644 --- a/packages/flutter/lib/src/painting/box_border.dart +++ b/packages/flutter/lib/src/painting/box_border.dart @@ -232,7 +232,18 @@ abstract class BoxBorder extends ShapeBorder { canvas.drawRRect(borderRadius.toRRect(rect), paint); } else { final RRect borderRect = borderRadius.toRRect(rect); - final RRect inner = borderRect.deflate(side.strokeInset); + RRect inner = borderRect.deflate(side.strokeInset); + // Clamp the inner border's radii to zero, until deflate does this + // automatically, so that we can start asserting non-negative values + // in the engine without breaking the framework. + // TODO(gspencergoog): Remove this once https://github.com/flutter/engine/pull/36062 rolls into the framework. + inner = RRect.fromLTRBAndCorners( + inner.left, inner.top, inner.right, inner.bottom, + topLeft: inner.tlRadius.clamp(minimum: Radius.zero), // ignore_clamp_double_lint + topRight: inner.trRadius.clamp(minimum: Radius.zero), // ignore_clamp_double_lint + bottomLeft: inner.blRadius.clamp(minimum: Radius.zero), // ignore_clamp_double_lint + bottomRight: inner.brRadius.clamp(minimum: Radius.zero), // ignore_clamp_double_lint + ); final RRect outer = borderRect.inflate(side.strokeOutset); canvas.drawDRRect(outer, inner, paint); } diff --git a/packages/flutter/lib/src/painting/rounded_rectangle_border.dart b/packages/flutter/lib/src/painting/rounded_rectangle_border.dart index 2e2faf8daa..c2ad93e2e0 100644 --- a/packages/flutter/lib/src/painting/rounded_rectangle_border.dart +++ b/packages/flutter/lib/src/painting/rounded_rectangle_border.dart @@ -132,7 +132,18 @@ class RoundedRectangleBorder extends OutlinedBorder { final Paint paint = Paint() ..color = side.color; final RRect borderRect = borderRadius.resolve(textDirection).toRRect(rect); - final RRect inner = borderRect.deflate(side.strokeInset); + RRect inner = borderRect.deflate(side.strokeInset); + // Clamp the inner border's radii to zero, until deflate does this + // automatically, so that we can start asserting non-negative values + // in the engine without breaking the framework. + // TODO(gspencergoog): Remove this once https://github.com/flutter/engine/pull/36062 rolls into the framework. + inner = RRect.fromLTRBAndCorners( + inner.left, inner.top, inner.right, inner.bottom, + topLeft: inner.tlRadius.clamp(minimum: Radius.zero), // ignore_clamp_double_lint + topRight: inner.trRadius.clamp(minimum: Radius.zero), // ignore_clamp_double_lint + bottomLeft: inner.blRadius.clamp(minimum: Radius.zero), // ignore_clamp_double_lint + bottomRight: inner.brRadius.clamp(minimum: Radius.zero), // ignore_clamp_double_lint + ); final RRect outer = borderRect.inflate(side.strokeOutset); canvas.drawDRRect(outer, inner, paint); } diff --git a/packages/flutter/lib/src/rendering/table_border.dart b/packages/flutter/lib/src/rendering/table_border.dart index d504bf4a35..5b22555317 100644 --- a/packages/flutter/lib/src/rendering/table_border.dart +++ b/packages/flutter/lib/src/rendering/table_border.dart @@ -271,7 +271,18 @@ class TableBorder { paintBorder(canvas, rect, top: top, right: right, bottom: bottom, left: left); } else { final RRect outer = borderRadius.toRRect(rect); - final RRect inner = outer.deflate(top.width); + RRect inner = outer.deflate(top.width); + // Clamp the inner border's radii to zero, until deflate does this + // automatically, so that we can start asserting non-negative values + // in the engine without breaking the framework. + // TODO(gspencergoog): Remove this once https://github.com/flutter/engine/pull/36062 rolls into the framework. + inner = RRect.fromLTRBAndCorners( + inner.left, inner.top, inner.right, inner.bottom, + topLeft: inner.tlRadius.clamp(minimum: Radius.zero), // ignore_clamp_double_lint + topRight: inner.trRadius.clamp(minimum: Radius.zero), // ignore_clamp_double_lint + bottomLeft: inner.blRadius.clamp(minimum: Radius.zero), // ignore_clamp_double_lint + bottomRight: inner.brRadius.clamp(minimum: Radius.zero), // ignore_clamp_double_lint + ); final Paint paint = Paint()..color = top.color; canvas.drawDRRect(outer, inner, paint); } diff --git a/packages/flutter/lib/src/widgets/widget_inspector.dart b/packages/flutter/lib/src/widgets/widget_inspector.dart index 7734bd273c..b439dcd987 100644 --- a/packages/flutter/lib/src/widgets/widget_inspector.dart +++ b/packages/flutter/lib/src/widgets/widget_inspector.dart @@ -129,6 +129,17 @@ class _MulticastCanvas implements Canvas { @override void drawDRRect(RRect outer, RRect inner, Paint paint) { + // Clamp the inner border's radii to zero, until deflate does this + // automatically, so that we can start asserting non-negative values + // in the engine without breaking the framework. + // TODO(gspencergoog): Remove this once https://github.com/flutter/engine/pull/36062 rolls into the framework. + inner = RRect.fromLTRBAndCorners( + inner.left, inner.top, inner.right, inner.bottom, + topLeft: inner.tlRadius.clamp(minimum: Radius.zero), // ignore_clamp_double_lint + topRight: inner.trRadius.clamp(minimum: Radius.zero), // ignore_clamp_double_lint + bottomLeft: inner.blRadius.clamp(minimum: Radius.zero), // ignore_clamp_double_lint + bottomRight: inner.brRadius.clamp(minimum: Radius.zero), // ignore_clamp_double_lint + ); _main.drawDRRect(outer, inner, paint); _screenshot.drawDRRect(outer, inner, paint); } diff --git a/packages/flutter/test/material/checkbox_test.dart b/packages/flutter/test/material/checkbox_test.dart index d0f2f1bbf5..3967ab8a91 100644 --- a/packages/flutter/test/material/checkbox_test.dart +++ b/packages/flutter/test/material/checkbox_test.dart @@ -553,7 +553,7 @@ void main() { ..drrect( color: material3 ? theme.colorScheme.onSurface : const Color(0x8a000000), outer: RRect.fromLTRBR(15.0, 15.0, 33.0, 33.0, const Radius.circular(1.0)), - inner: RRect.fromLTRBR(17.0, 17.0, 31.0, 31.0, const Radius.circular(-1.0)), + inner: RRect.fromLTRBR(17.0, 17.0, 31.0, 31.0, Radius.zero), ), ); @@ -568,7 +568,7 @@ void main() { ..drrect( color: material3 ? theme.colorScheme.onSurface.withOpacity(0.38) : const Color(0x61000000), outer: RRect.fromLTRBR(15.0, 15.0, 33.0, 33.0, const Radius.circular(1.0)), - inner: RRect.fromLTRBR(17.0, 17.0, 31.0, 31.0, const Radius.circular(-1.0)), + inner: RRect.fromLTRBR(17.0, 17.0, 31.0, 31.0, Radius.zero), ), ); }); @@ -1420,7 +1420,7 @@ void main() { ..drrect( color: borderColor, outer: RRect.fromLTRBR(15, 15, 33, 33, const Radius.circular(1)), - inner: RRect.fromLTRBR(19, 19, 29, 29, const Radius.circular(-3)), + inner: RRect.fromLTRBR(19, 19, 29, 29, Radius.zero), ), ); } @@ -1479,7 +1479,7 @@ void main() { ..drrect( color: borderColor, outer: RRect.fromLTRBR(15, 15, 33, 33, const Radius.circular(1)), - inner: RRect.fromLTRBR(19, 19, 29, 29, const Radius.circular(-3)), + inner: RRect.fromLTRBR(19, 19, 29, 29, Radius.zero), ), ); } diff --git a/packages/flutter/test/widgets/box_decoration_test.dart b/packages/flutter/test/widgets/box_decoration_test.dart index a8f46cc599..0d9d4e1e95 100644 --- a/packages/flutter/test/widgets/box_decoration_test.dart +++ b/packages/flutter/test/widgets/box_decoration_test.dart @@ -249,12 +249,7 @@ Future main() async { 350.0, 200.0, 450.0, 300.0, topRight: const Radius.circular(10.0), ), - inner: RRect.fromLTRBAndCorners( - 360.0, 210.0, 440.0, 290.0, - topLeft: const Radius.circular(-10.0), - bottomRight: const Radius.circular(-10.0), - bottomLeft: const Radius.circular(-10.0), - ), + inner: RRect.fromLTRBAndCorners(360.0, 210.0, 440.0, 290.0), ) ..circle(x: 400.0, y: 350.0, radius: 45.0), );