From c6d4a6ef5c8aadba3e3c6da836dbe73725b2a328 Mon Sep 17 00:00:00 2001 From: Hans Muller Date: Thu, 12 Nov 2020 10:36:39 -0800 Subject: [PATCH] Update OutlinedButton default outline geometry to be backwards compatible (#70393) --- .../lib/src/material/outlined_button.dart | 68 ++++++++++++++++++- .../test/material/outlined_button_test.dart | 60 ++++++++-------- .../material/outlined_button_theme_test.dart | 10 +-- 3 files changed, 101 insertions(+), 37 deletions(-) diff --git a/packages/flutter/lib/src/material/outlined_button.dart b/packages/flutter/lib/src/material/outlined_button.dart index 98212289bc..cbfb16f3c3 100644 --- a/packages/flutter/lib/src/material/outlined_button.dart +++ b/packages/flutter/lib/src/material/outlined_button.dart @@ -242,7 +242,7 @@ class OutlinedButton extends ButtonStyleButton { color: Theme.of(context).colorScheme.onSurface.withOpacity(0.12), width: 1, ), - shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))), + shape: const _Outline(borderRadius: BorderRadius.all(Radius.circular(4))), enabledMouseCursor: SystemMouseCursors.click, disabledMouseCursor: SystemMouseCursors.forbidden, visualDensity: theme.visualDensity, @@ -349,3 +349,69 @@ class _OutlinedButtonWithIconChild extends StatelessWidget { ); } } + + +// This subclass only exists for the sake of backwards compatibility with the +// outline drawn by the original OutlineButton widget. The paint override +// draws a Path that's stroked with BorderSide, rather than filling a rounded +// rect whose inner path is the outer path inset by the BorderSide's width. +class _Outline extends RoundedRectangleBorder { + const _Outline({ + BorderSide side = BorderSide.none, + required BorderRadiusGeometry borderRadius, + }) : super(side: side, borderRadius: borderRadius); + + @override + ShapeBorder scale(double t) { + return _Outline( + side: side.scale(t), + borderRadius: borderRadius * t, + ); + } + + @override + ShapeBorder? lerpFrom(ShapeBorder? a, double t) { + assert(t != null); + if (a is _Outline) { + return _Outline( + side: BorderSide.lerp(a.side, side, t), + borderRadius: BorderRadiusGeometry.lerp(a.borderRadius, borderRadius, t)!, + ); + } + return super.lerpFrom(a, t); + } + + @override + ShapeBorder? lerpTo(ShapeBorder? b, double t) { + assert(t != null); + if (b is _Outline) { + return _Outline( + side: BorderSide.lerp(side, b.side, t), + borderRadius: BorderRadiusGeometry.lerp(borderRadius, b.borderRadius, t)!, + ); + } + return super.lerpTo(b, t); + } + + @override + _Outline copyWith({ BorderSide? side, BorderRadius? borderRadius }) { + return _Outline( + side: side ?? this.side, + borderRadius: borderRadius ?? this.borderRadius, + ); + } + + @override + void paint(Canvas canvas, Rect rect, { TextDirection? textDirection }) { + switch (side.style) { + case BorderStyle.none: + break; + case BorderStyle.solid: + if (side.width == 0.0) { + super.paint(canvas, rect, textDirection: textDirection); + } else { + canvas.drawPath(getOuterPath(rect, textDirection: textDirection), side.toPaint()); + } + } + } +} diff --git a/packages/flutter/test/material/outlined_button_test.dart b/packages/flutter/test/material/outlined_button_test.dart index f27e3180b6..8ecafd530a 100644 --- a/packages/flutter/test/material/outlined_button_test.dart +++ b/packages/flutter/test/material/outlined_button_test.dart @@ -40,13 +40,12 @@ void main() { expect(material.color, Colors.transparent); expect(material.elevation, 0.0); expect(material.shadowColor, const Color(0xff000000)); - expect(material.shape, RoundedRectangleBorder( - side: BorderSide( - width: 1, - color: colorScheme.onSurface.withOpacity(0.12), - ), - borderRadius: BorderRadius.circular(4.0), - )); + + expect(material.shape, isInstanceOf()); + RoundedRectangleBorder materialShape = material.shape! as RoundedRectangleBorder; + expect(materialShape.side, BorderSide(width: 1, color: colorScheme.onSurface.withOpacity(0.12))); + expect(materialShape.borderRadius, BorderRadius.circular(4.0)); + expect(material.textStyle!.color, colorScheme.primary); expect(material.textStyle!.fontFamily, 'Roboto'); expect(material.textStyle!.fontSize, 14); @@ -71,13 +70,12 @@ void main() { expect(material.color, Colors.transparent); expect(material.elevation, 0.0); expect(material.shadowColor, const Color(0xff000000)); - expect(material.shape, RoundedRectangleBorder( - side: BorderSide( - width: 1, - color: colorScheme.onSurface.withOpacity(0.12), - ), - borderRadius: BorderRadius.circular(4.0), - )); + + expect(material.shape, isInstanceOf()); + materialShape = material.shape! as RoundedRectangleBorder; + expect(materialShape.side, BorderSide(width: 1, color: colorScheme.onSurface.withOpacity(0.12))); + expect(materialShape.borderRadius, BorderRadius.circular(4.0)); + expect(material.textStyle!.color, colorScheme.primary); expect(material.textStyle!.fontFamily, 'Roboto'); expect(material.textStyle!.fontSize, 14); @@ -113,13 +111,12 @@ void main() { expect(material.color, Colors.transparent); expect(material.elevation, 0.0); expect(material.shadowColor, const Color(0xff000000)); - expect(material.shape, RoundedRectangleBorder( - side: BorderSide( - width: 1, - color: colorScheme.onSurface.withOpacity(0.12), - ), - borderRadius: BorderRadius.circular(4.0), - )); + + expect(material.shape, isInstanceOf()); + materialShape = material.shape! as RoundedRectangleBorder; + expect(materialShape.side, BorderSide(width: 1, color: colorScheme.onSurface.withOpacity(0.12))); + expect(materialShape.borderRadius, BorderRadius.circular(4.0)); + expect(material.textStyle!.color, colorScheme.primary); expect(material.textStyle!.fontFamily, 'Roboto'); expect(material.textStyle!.fontSize, 14); @@ -147,13 +144,12 @@ void main() { expect(material.color, Colors.transparent); expect(material.elevation, 0.0); expect(material.shadowColor, const Color(0xff000000)); - expect(material.shape, RoundedRectangleBorder( - side: BorderSide( - width: 1, - color: colorScheme.onSurface.withOpacity(0.12), - ), - borderRadius: BorderRadius.circular(4.0), - )); + + expect(material.shape, isInstanceOf()); + materialShape = material.shape! as RoundedRectangleBorder; + expect(materialShape.side, BorderSide(width: 1, color: colorScheme.onSurface.withOpacity(0.12))); + expect(materialShape.borderRadius, BorderRadius.circular(4.0)); + expect(material.textStyle!.color, colorScheme.onSurface.withOpacity(0.38)); expect(material.textStyle!.fontFamily, 'Roboto'); expect(material.textStyle!.fontSize, 14); @@ -546,12 +542,12 @@ void main() { final Finder outlinedButton = find.byType(OutlinedButton); // Default, not disabled. - expect(outlinedButton, paints..drrect(color: defaultColor)); + expect(outlinedButton, paints..path(color: defaultColor)); // Focused. focusNode.requestFocus(); await tester.pumpAndSettle(); - expect(outlinedButton, paints..drrect(color: focusedColor)); + expect(outlinedButton, paints..path(color: focusedColor)); // Hovered. final Offset center = tester.getCenter(find.byType(OutlinedButton)); @@ -562,12 +558,12 @@ void main() { addTearDown(gesture.removePointer); await gesture.moveTo(center); await tester.pumpAndSettle(); - expect(outlinedButton, paints..drrect(color: hoverColor)); + expect(outlinedButton, paints..path(color: hoverColor)); // Highlighted (pressed). await gesture.down(center); await tester.pumpAndSettle(); - expect(outlinedButton, paints..drrect(color: pressedColor)); + expect(outlinedButton, paints..path(color: pressedColor)); }); testWidgets('OutlinedButton onPressed and onLongPress callbacks are correctly called when non-null', (WidgetTester tester) async { diff --git a/packages/flutter/test/material/outlined_button_theme_test.dart b/packages/flutter/test/material/outlined_button_theme_test.dart index fb1ff3b71d..2ee60bf5d9 100644 --- a/packages/flutter/test/material/outlined_button_theme_test.dart +++ b/packages/flutter/test/material/outlined_button_theme_test.dart @@ -34,10 +34,12 @@ void main() { expect(material.color, Colors.transparent); expect(material.elevation, 0.0); expect(material.shadowColor, Colors.black); - expect(material.shape, RoundedRectangleBorder( - side: BorderSide(width: 1, color: colorScheme.onSurface.withOpacity(0.12)), - borderRadius: BorderRadius.circular(4.0), - )); + + expect(material.shape, isInstanceOf()); + final RoundedRectangleBorder materialShape = material.shape! as RoundedRectangleBorder; + expect(materialShape.side, BorderSide(width: 1, color: colorScheme.onSurface.withOpacity(0.12))); + expect(materialShape.borderRadius, BorderRadius.circular(4.0)); + expect(material.textStyle!.color, colorScheme.primary); expect(material.textStyle!.fontFamily, 'Roboto'); expect(material.textStyle!.fontSize, 14);