From 3273429eaabcb4bc5c47655be58465fb86d3bae8 Mon Sep 17 00:00:00 2001 From: Vitaliy Hnidenko <82770717+sawel24@users.noreply.github.com> Date: Thu, 6 Apr 2023 18:28:18 +0300 Subject: [PATCH 1/3] feat: add triangle badge shape (#107) * feat: add triangle badge shape * Add widget tests for triangle badge shape --- example/.flutter-plugins-dependencies | 2 +- example/lib/alarm_app.dart | 29 ++++++-- lib/src/badge.dart | 3 +- lib/src/badge_shape.dart | 5 ++ .../triangle_badge_shape_painter.dart | 72 +++++++++++++++++++ lib/src/utils/drawing_utils.dart | 8 +++ test/badges_test.dart | 71 ++++++++++++++++++ 7 files changed, 181 insertions(+), 9 deletions(-) create mode 100644 lib/src/painters/triangle_badge_shape_painter.dart diff --git a/example/.flutter-plugins-dependencies b/example/.flutter-plugins-dependencies index 1fbe401..4dadac9 100644 --- a/example/.flutter-plugins-dependencies +++ b/example/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"integration_test","path":"/Users/yadaniyil/flutter/packages/integration_test/","native_build":true,"dependencies":[]}],"android":[{"name":"integration_test","path":"/Users/yadaniyil/flutter/packages/integration_test/","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"integration_test","dependencies":[]}],"date_created":"2023-03-24 12:03:15.082040","version":"3.7.6"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"integration_test","path":"/Users/vitalii/flutter3/flutter/packages/integration_test/","native_build":true,"dependencies":[]}],"android":[{"name":"integration_test","path":"/Users/vitalii/flutter3/flutter/packages/integration_test/","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"integration_test","dependencies":[]}],"date_created":"2023-04-03 12:55:06.152967","version":"3.7.0"} \ No newline at end of file diff --git a/example/lib/alarm_app.dart b/example/lib/alarm_app.dart index 80e3322..5c6f913 100644 --- a/example/lib/alarm_app.dart +++ b/example/lib/alarm_app.dart @@ -17,18 +17,33 @@ class _AlarmAppState extends State { @override Widget build(BuildContext context) { return badges.Badge( - badgeStyle: badges.BadgeStyle(padding: EdgeInsets.all(7)), + badgeStyle: badges.BadgeStyle( + borderSide: BorderSide(color: Colors.white, width: 2), + shape: badges.BadgeShape.triangle, + badgeGradient: badges.BadgeGradient.linear( + colors: [ + Colors.red, + Colors.orange, + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + ), badgeAnimation: badges.BadgeAnimation.fade( animationDuration: Duration(seconds: 1), loopAnimation: _isLooped, ), - // onTap: () { - // setState(() => _isLooped = !_isLooped); - // }, ignorePointer: false, - // toAnimate: false, - badgeContent: - Text(counter.toString(), style: TextStyle(color: Colors.white)), + badgeContent: Container( + width: 20, + height: 20, + child: Padding( + padding: const EdgeInsets.only(left: 8), + child: Text( + '!', + style: TextStyle(color: Colors.white), + ), + )), position: badges.BadgePosition.topEnd(top: -12), child: GestureDetector( onTap: () { diff --git a/lib/src/badge.dart b/lib/src/badge.dart index 7a13e8b..70ee966 100644 --- a/lib/src/badge.dart +++ b/lib/src/badge.dart @@ -157,7 +157,8 @@ class BadgeState extends State with TickerProviderStateMixin { borderRadius: widget.badgeStyle.borderRadius, ); final isCustomShape = widget.badgeStyle.shape == BadgeShape.twitter || - widget.badgeStyle.shape == BadgeShape.instagram; + widget.badgeStyle.shape == BadgeShape.instagram || + widget.badgeStyle.shape == BadgeShape.triangle; final gradientBorder = widget.badgeStyle.borderGradient != null ? BadgeBorderGradient( diff --git a/lib/src/badge_shape.dart b/lib/src/badge_shape.dart index 4788701..3aacf91 100644 --- a/lib/src/badge_shape.dart +++ b/lib/src/badge_shape.dart @@ -1,4 +1,5 @@ import 'package:badges/badges.dart' as badges; +import 'package:badges/src/painters/triangle_badge_shape_painter.dart'; import 'package:flutter/material.dart'; /// Set of shapes that you can use for your [badges.Badge] widget. @@ -15,6 +16,10 @@ enum BadgeShape { /// * [RoundedRectangleBorder] square, + /// To make the triangle badge . + /// See [TriangleBadgeShapePainter] for more details. + triangle, + /// To make the twitter badge . /// See [TwitterBadgeShapePainter] for more details. twitter, diff --git a/lib/src/painters/triangle_badge_shape_painter.dart b/lib/src/painters/triangle_badge_shape_painter.dart new file mode 100644 index 0000000..a7e5454 --- /dev/null +++ b/lib/src/painters/triangle_badge_shape_painter.dart @@ -0,0 +1,72 @@ +import 'package:badges/badges.dart'; +import 'package:badges/src/utils/gradient_utils.dart'; +import 'package:flutter/material.dart'; + +class TriangleBadgeShapePainter extends CustomPainter { + Color? color; + BadgeGradient? badgeGradient; + BadgeGradient? borderGradient; + BorderSide? borderSide; + + TriangleBadgeShapePainter({ + Key? key, + this.color = Colors.blue, + this.badgeGradient, + this.borderGradient, + this.borderSide, + }); + + @override + void paint(Canvas canvas, Size size) { + final width = size.width; + final height = size.height; + + Path path = Path(); + Paint paint = Paint(); + Paint paintBorder = Paint(); + + if (badgeGradient != null) { + paint.shader = GradientUtils.getGradientShader( + badgeGradient: badgeGradient!, + width: width, + height: height, + ); + } + paintBorder + ..color = borderSide?.color ?? Colors.white + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round + ..strokeWidth = borderSide?.width ?? 0; + + if (borderGradient != null) { + paintBorder.shader = GradientUtils.getGradientShader( + badgeGradient: borderGradient!, + width: width, + height: height, + ); + } + path + ..moveTo(width * 0.132, height * 0.888) + ..arcToPoint(Offset(width * 0.075, height * 0.772), + radius: Radius.circular(height * 0.09)) + ..lineTo(width * 0.428, height * 0.156) + ..arcToPoint(Offset(width * 0.582, height * 0.156), + radius: Radius.circular(height * 0.09)) + ..lineTo(width * 0.928, height * 0.756) + ..arcToPoint(Offset(width * 0.868, height * 0.888), + radius: Radius.circular(height * 0.09)) + ..lineTo(width * 0.132, height * 0.888); + path.close(); + + paint.color = color!; + canvas.drawPath(path, paint); + if (borderSide != BorderSide.none) { + canvas.drawPath(path, paintBorder); + } + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return true; + } +} diff --git a/lib/src/utils/drawing_utils.dart b/lib/src/utils/drawing_utils.dart index a896794..fa051af 100644 --- a/lib/src/utils/drawing_utils.dart +++ b/lib/src/utils/drawing_utils.dart @@ -1,5 +1,6 @@ import 'package:badges/badges.dart'; import 'package:badges/src/painters/instagram_badge_shape_painter.dart'; +import 'package:badges/src/painters/triangle_badge_shape_painter.dart'; import 'package:badges/src/painters/twitter_badge_shape_painter.dart'; import 'package:flutter/material.dart'; @@ -26,6 +27,13 @@ class DrawingUtils { borderSide: borderSide, borderGradient: borderGradient, ); + case BadgeShape.triangle: + return TriangleBadgeShapePainter( + color: color, + badgeGradient: badgeGradient, + borderSide: borderSide, + borderGradient: borderGradient, + ); case BadgeShape.square: case BadgeShape.circle: break; diff --git a/test/badges_test.dart b/test/badges_test.dart index fbf6bbd..3bd661f 100644 --- a/test/badges_test.dart +++ b/test/badges_test.dart @@ -2,6 +2,7 @@ import 'package:badges/badges.dart' as badges; import 'package:badges/src/badge_border_gradient.dart'; import 'package:badges/src/badge_gradient_type.dart'; import 'package:badges/src/painters/instagram_badge_shape_painter.dart'; +import 'package:badges/src/painters/triangle_badge_shape_painter.dart'; import 'package:badges/src/painters/twitter_badge_shape_painter.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -334,6 +335,76 @@ void main() { expect(painter.shouldRepaint(painter), true); }); }); + + group('Triangle shape', () { + const triangleBadge = badges.Badge( + badgeStyle: badges.BadgeStyle( + shape: badges.BadgeShape.triangle, + badgeColor: Colors.green, + badgeGradient: + badges.BadgeGradient.radial(colors: [Colors.black, Colors.green]), + borderGradient: + badges.BadgeGradient.linear(colors: [Colors.red, Colors.yellow]), + borderSide: BorderSide(width: 2), + ), + ); + + testWidgets('Triangle badge should render correctly', (tester) async { + await tester.pumpWidget(_wrapWithMaterialApp(triangleBadge)); + expect(find.byType(badges.Badge), findsOneWidget); + expect(find.byType(CustomPaint), findsWidgets); + }); + + testWidgets('Triangle badge color should match', (tester) async { + await tester.pumpWidget(_wrapWithMaterialApp(triangleBadge)); + + final customPaint = + tester.widgetList(find.byType(CustomPaint)).last; + final painter = customPaint.painter as TriangleBadgeShapePainter; + expect(painter.color, Colors.green); + }); + + testWidgets('Triangle badge gradient should match', (tester) async { + await tester.pumpWidget(_wrapWithMaterialApp(triangleBadge)); + final customPaint = + tester.widgetList(find.byType(CustomPaint)).last; + final painter = customPaint.painter as TriangleBadgeShapePainter; + expect(painter.badgeGradient?.gradientType, BadgeGradientType.radial); + expect(painter.badgeGradient?.colors.first, Colors.black); + expect(painter.badgeGradient?.colors.last, Colors.green); + expect(painter.badgeGradient?.colors.length, 2); + }); + + testWidgets('Triangle badge border gradient should match', + (tester) async { + await tester.pumpWidget(_wrapWithMaterialApp(triangleBadge)); + final customPaint = + tester.widgetList(find.byType(CustomPaint)).last; + final painter = customPaint.painter as TriangleBadgeShapePainter; + expect(painter.borderGradient?.gradientType, BadgeGradientType.linear); + expect(painter.borderGradient?.colors.first, Colors.red); + expect(painter.borderGradient?.colors.last, Colors.yellow); + expect(painter.borderGradient?.colors.length, 2); + }); + + testWidgets('Triangle badge border should match', (tester) async { + await tester.pumpWidget(_wrapWithMaterialApp(triangleBadge)); + final customPaint = + tester.widgetList(find.byType(CustomPaint)).last; + final painter = customPaint.painter as TriangleBadgeShapePainter; + expect(painter.borderSide?.width, 2); + expect(painter.borderSide?.style, BorderStyle.solid); + expect(painter.borderSide?.strokeAlign, BorderSide.strokeAlignInside); + }); + + testWidgets('Triangle badge repaint should match', (tester) async { + await tester.pumpWidget(_wrapWithMaterialApp(triangleBadge)); + final customPaint = + tester.widgetList(find.byType(CustomPaint)).last; + final painter = customPaint.painter as TriangleBadgeShapePainter; + expect(painter.shouldRepaint(painter), true); + }); + }); }); group('Badge tests', () { From fec7f9fc43bc0f6a4ad9d7804c823135eaf8f9de Mon Sep 17 00:00:00 2001 From: Vitaliy Hnidenko <82770717+sawel24@users.noreply.github.com> Date: Tue, 11 Apr 2023 18:39:39 +0300 Subject: [PATCH 2/3] fix: set all custom shapes to fixed-size proportions (#108) * Update custom painter for all custom shapes to set fixed-size proportions * Return default value of child content for verified account widgets * Resize the shape depending on the child widget in the same proportions * Fix git check errors * Resize all types of shapes depending on the size child widget * Add method for calculate default badge content padding value * Change method for calculate default badge content padding value * Add unit tests for calculateBadgeContentPadding method --- example/lib/alarm_app.dart | 14 +- lib/src/badge.dart | 122 +++++++++++------- lib/src/badge_style.dart | 4 +- .../instagram_badge_shape_painter.dart | 64 ++++----- .../triangle_badge_shape_painter.dart | 34 +++-- .../painters/twitter_badge_shape_painter.dart | 48 ++++--- test/badges_test.dart | 65 +++++++++- 7 files changed, 229 insertions(+), 122 deletions(-) diff --git a/example/lib/alarm_app.dart b/example/lib/alarm_app.dart index 5c6f913..c5db1e2 100644 --- a/example/lib/alarm_app.dart +++ b/example/lib/alarm_app.dart @@ -34,16 +34,10 @@ class _AlarmAppState extends State { loopAnimation: _isLooped, ), ignorePointer: false, - badgeContent: Container( - width: 20, - height: 20, - child: Padding( - padding: const EdgeInsets.only(left: 8), - child: Text( - '!', - style: TextStyle(color: Colors.white), - ), - )), + badgeContent: Text( + '!', + style: TextStyle(color: Colors.white), + ), position: badges.BadgePosition.topEnd(top: -12), child: GestureDetector( onTap: () { diff --git a/lib/src/badge.dart b/lib/src/badge.dart index 70ee966..843db20 100644 --- a/lib/src/badge.dart +++ b/lib/src/badge.dart @@ -144,6 +144,21 @@ class BadgeState extends State with TickerProviderStateMixin { return _appearanceController.value; } + EdgeInsets calculateBadgeContentPadding( + Widget? badgeContent, + BadgeShape shape, + ) { + final isTextContent = badgeContent is Text; + final isTriangleShape = shape == BadgeShape.triangle; + if (isTriangleShape) { + return const EdgeInsets.symmetric(horizontal: 10.0); + } else if (isTextContent) { + return const EdgeInsets.symmetric(horizontal: 8.0); + } else { + return const EdgeInsets.symmetric(horizontal: 5.0); + } + } + Widget _getBadge() { final border = widget.badgeStyle.shape == BadgeShape.circle ? CircleBorder( @@ -174,52 +189,69 @@ class BadgeState extends State with TickerProviderStateMixin { builder: (context, child) { return Opacity( opacity: _getOpacity(), - child: isCustomShape - ? CustomPaint( - painter: DrawingUtils.drawBadgeShape( - shape: widget.badgeStyle.shape, - color: widget.badgeStyle.badgeColor, - badgeGradient: widget.badgeStyle.badgeGradient, - borderGradient: widget.badgeStyle.borderGradient, - borderSide: widget.badgeStyle.borderSide, - ), - child: Padding( - padding: widget.badgeStyle.padding, - child: widget.badgeContent, - ), - ) - : Material( - shape: border, - elevation: widget.badgeStyle.elevation, - // Without this Colors.transparent will be ignored - type: MaterialType.transparency, - child: AnimatedContainer( - curve: widget.badgeAnimation.colorChangeAnimationCurve, - duration: widget.badgeAnimation.toAnimate - ? widget.badgeAnimation.colorChangeAnimationDuration - : Duration.zero, - decoration: widget.badgeStyle.shape == BadgeShape.circle - ? BoxDecoration( - color: widget.badgeStyle.badgeColor, - border: gradientBorder, - gradient: - widget.badgeStyle.badgeGradient?.gradient(), - shape: BoxShape.circle, - ) - : BoxDecoration( - color: widget.badgeStyle.badgeColor, - gradient: - widget.badgeStyle.badgeGradient?.gradient(), - shape: BoxShape.rectangle, - borderRadius: widget.badgeStyle.borderRadius, - border: gradientBorder, + child: UnconstrainedBox( + child: IntrinsicWidth( + child: AspectRatio( + aspectRatio: + widget.badgeStyle.shape == BadgeShape.square ? 1.5 : 1.0, + child: isCustomShape + ? CustomPaint( + painter: DrawingUtils.drawBadgeShape( + shape: widget.badgeStyle.shape, + color: widget.badgeStyle.badgeColor, + badgeGradient: widget.badgeStyle.badgeGradient, + borderGradient: widget.badgeStyle.borderGradient, + borderSide: widget.badgeStyle.borderSide, + ), + child: Padding( + padding: widget.badgeStyle.padding ?? + calculateBadgeContentPadding( + widget.badgeContent, + widget.badgeStyle.shape, + ), + child: Center(child: widget.badgeContent), + ), + ) + : Material( + shape: border, + elevation: widget.badgeStyle.elevation, + // Without this Colors.transparent will be ignored + type: MaterialType.transparency, + child: AnimatedContainer( + curve: + widget.badgeAnimation.colorChangeAnimationCurve, + duration: widget.badgeAnimation.toAnimate + ? widget + .badgeAnimation.colorChangeAnimationDuration + : Duration.zero, + decoration: widget.badgeStyle.shape == + BadgeShape.circle + ? BoxDecoration( + color: widget.badgeStyle.badgeColor, + border: gradientBorder, + gradient: widget.badgeStyle.badgeGradient + ?.gradient(), + shape: BoxShape.circle, + ) + : BoxDecoration( + color: widget.badgeStyle.badgeColor, + gradient: widget.badgeStyle.badgeGradient + ?.gradient(), + shape: BoxShape.rectangle, + borderRadius: + widget.badgeStyle.borderRadius, + border: gradientBorder, + ), + child: Padding( + padding: widget.badgeStyle.padding ?? + const EdgeInsets.symmetric(horizontal: 5.0), + child: Center(child: widget.badgeContent), ), - child: Padding( - padding: widget.badgeStyle.padding, - child: widget.badgeContent, - ), - ), - ), + ), + ), + ), + ), + ), ); }, ); diff --git a/lib/src/badge_style.dart b/lib/src/badge_style.dart index 55486aa..1aad24c 100644 --- a/lib/src/badge_style.dart +++ b/lib/src/badge_style.dart @@ -31,7 +31,7 @@ class BadgeStyle { /// Specifies padding for [badgeContent]. /// The default value is EdgeInsets.all(5.0). - final EdgeInsetsGeometry padding; + final EdgeInsetsGeometry? padding; const BadgeStyle({ this.shape = BadgeShape.circle, @@ -41,6 +41,6 @@ class BadgeStyle { this.elevation = 2, this.badgeGradient, this.borderGradient, - this.padding = const EdgeInsets.all(5.0), + this.padding, }); } diff --git a/lib/src/painters/instagram_badge_shape_painter.dart b/lib/src/painters/instagram_badge_shape_painter.dart index 51560fa..65a16db 100644 --- a/lib/src/painters/instagram_badge_shape_painter.dart +++ b/lib/src/painters/instagram_badge_shape_painter.dart @@ -1,3 +1,5 @@ +import 'dart:math' as math; + import 'package:badges/badges.dart'; import 'package:badges/src/utils/gradient_utils.dart'; import 'package:flutter/material.dart'; @@ -21,6 +23,10 @@ class InstagramBadgeShapePainter extends CustomPainter { final width = size.width; final height = size.height; + final double maxSize = math.max(width, height); + + canvas.clipRect(Offset.zero & Size(maxSize, maxSize)); + Path path = Path(); Paint paint = Paint(); Paint paintBorder = Paint(); @@ -28,8 +34,8 @@ class InstagramBadgeShapePainter extends CustomPainter { if (badgeGradient != null) { paint.shader = GradientUtils.getGradientShader( badgeGradient: badgeGradient!, - width: width, - height: height, + width: maxSize, + height: maxSize, ); } paintBorder @@ -41,36 +47,36 @@ class InstagramBadgeShapePainter extends CustomPainter { if (borderGradient != null) { paintBorder.shader = GradientUtils.getGradientShader( badgeGradient: borderGradient!, - width: width, - height: height, + width: maxSize, + height: maxSize, ); } - path.moveTo(width * 0.14, height * 0.14); - path.lineTo(width * 0.3, height * 0.14); - path.lineTo(width * 0.385, 0); - path.lineTo(width * 0.515, height * 0.08); - path.lineTo(width * 0.627, height * 0.012); - path.lineTo(width * 0.7, height * 0.134); - path.lineTo(width * 0.867, height * 0.134); - path.lineTo(width * 0.867, height * 0.3); - path.lineTo(width, height * 0.38); - path.lineTo(width * 0.922, height * 0.505); - path.lineTo(width * 0.995, height * 0.629); - path.lineTo(width * 0.866, height * 0.706); - path.lineTo(width * 0.866, height * 0.868); - path.lineTo(width * 0.697, height * 0.868); - path.lineTo(width * 0.618, height * 0.996); - path.lineTo(width * 0.5, height * 0.924); - path.lineTo(width * 0.379, height * 0.996); - path.lineTo(width * 0.302, height * 0.868); - path.lineTo(width * 0.14, height * 0.868); - path.lineTo(width * 0.14, height * 0.702); - path.lineTo(width * 0.004, height * 0.618); - path.lineTo(width * 0.08, height * 0.494); - path.lineTo(width * 0.012, height * 0.379); - path.lineTo(width * 0.14, height * 0.306); - path.lineTo(width * 0.14, height * 0.14); + path.moveTo(maxSize * 0.14, maxSize * 0.14); + path.lineTo(maxSize * 0.3, maxSize * 0.14); + path.lineTo(maxSize * 0.385, 0); + path.lineTo(maxSize * 0.515, maxSize * 0.08); + path.lineTo(maxSize * 0.627, maxSize * 0.012); + path.lineTo(maxSize * 0.7, maxSize * 0.134); + path.lineTo(maxSize * 0.867, maxSize * 0.134); + path.lineTo(maxSize * 0.867, maxSize * 0.3); + path.lineTo(maxSize, maxSize * 0.38); + path.lineTo(maxSize * 0.922, maxSize * 0.505); + path.lineTo(maxSize * 0.995, maxSize * 0.629); + path.lineTo(maxSize * 0.866, maxSize * 0.706); + path.lineTo(maxSize * 0.866, maxSize * 0.868); + path.lineTo(maxSize * 0.697, maxSize * 0.868); + path.lineTo(maxSize * 0.618, maxSize * 0.996); + path.lineTo(maxSize * 0.5, maxSize * 0.924); + path.lineTo(maxSize * 0.379, maxSize * 0.996); + path.lineTo(maxSize * 0.302, maxSize * 0.868); + path.lineTo(maxSize * 0.14, maxSize * 0.868); + path.lineTo(maxSize * 0.14, maxSize * 0.702); + path.lineTo(maxSize * 0.004, maxSize * 0.618); + path.lineTo(maxSize * 0.08, maxSize * 0.494); + path.lineTo(maxSize * 0.012, maxSize * 0.379); + path.lineTo(maxSize * 0.14, maxSize * 0.306); + path.lineTo(maxSize * 0.14, maxSize * 0.14); paint.color = color!; canvas.drawPath(path, paint); diff --git a/lib/src/painters/triangle_badge_shape_painter.dart b/lib/src/painters/triangle_badge_shape_painter.dart index a7e5454..37ce8bb 100644 --- a/lib/src/painters/triangle_badge_shape_painter.dart +++ b/lib/src/painters/triangle_badge_shape_painter.dart @@ -1,3 +1,5 @@ +import 'dart:math' as math; + import 'package:badges/badges.dart'; import 'package:badges/src/utils/gradient_utils.dart'; import 'package:flutter/material.dart'; @@ -21,6 +23,10 @@ class TriangleBadgeShapePainter extends CustomPainter { final width = size.width; final height = size.height; + final double maxSize = math.max(width, height); + + canvas.clipRect(Offset.zero & Size(maxSize, maxSize)); + Path path = Path(); Paint paint = Paint(); Paint paintBorder = Paint(); @@ -28,8 +34,8 @@ class TriangleBadgeShapePainter extends CustomPainter { if (badgeGradient != null) { paint.shader = GradientUtils.getGradientShader( badgeGradient: badgeGradient!, - width: width, - height: height, + width: maxSize, + height: maxSize, ); } paintBorder @@ -41,21 +47,21 @@ class TriangleBadgeShapePainter extends CustomPainter { if (borderGradient != null) { paintBorder.shader = GradientUtils.getGradientShader( badgeGradient: borderGradient!, - width: width, - height: height, + width: maxSize, + height: maxSize, ); } path - ..moveTo(width * 0.132, height * 0.888) - ..arcToPoint(Offset(width * 0.075, height * 0.772), - radius: Radius.circular(height * 0.09)) - ..lineTo(width * 0.428, height * 0.156) - ..arcToPoint(Offset(width * 0.582, height * 0.156), - radius: Radius.circular(height * 0.09)) - ..lineTo(width * 0.928, height * 0.756) - ..arcToPoint(Offset(width * 0.868, height * 0.888), - radius: Radius.circular(height * 0.09)) - ..lineTo(width * 0.132, height * 0.888); + ..moveTo(maxSize * 0.132, maxSize * 0.888) + ..arcToPoint(Offset(maxSize * 0.075, maxSize * 0.772), + radius: Radius.circular(maxSize * 0.09)) + ..lineTo(maxSize * 0.428, maxSize * 0.156) + ..arcToPoint(Offset(maxSize * 0.582, maxSize * 0.156), + radius: Radius.circular(maxSize * 0.09)) + ..lineTo(maxSize * 0.928, maxSize * 0.756) + ..arcToPoint(Offset(maxSize * 0.868, maxSize * 0.888), + radius: Radius.circular(maxSize * 0.09)) + ..lineTo(maxSize * 0.132, maxSize * 0.888); path.close(); paint.color = color!; diff --git a/lib/src/painters/twitter_badge_shape_painter.dart b/lib/src/painters/twitter_badge_shape_painter.dart index 2b02642..1b05422 100644 --- a/lib/src/painters/twitter_badge_shape_painter.dart +++ b/lib/src/painters/twitter_badge_shape_painter.dart @@ -1,3 +1,5 @@ +import 'dart:math' as math; + import 'package:badges/src/badge_gradient.dart'; import 'package:badges/src/utils/gradient_utils.dart'; import 'package:flutter/material.dart'; @@ -21,6 +23,10 @@ class TwitterBadgeShapePainter extends CustomPainter { final width = size.width; final height = size.height; + final double maxSize = math.max(width, height); + + canvas.clipRect(Offset.zero & Size(maxSize, maxSize)); + Path path = Path(); Paint paint = Paint(); Paint paintBorder = Paint(); @@ -28,8 +34,8 @@ class TwitterBadgeShapePainter extends CustomPainter { if (badgeGradient != null) { paint.shader = GradientUtils.getGradientShader( badgeGradient: badgeGradient!, - width: width, - height: height, + width: maxSize, + height: maxSize, ); } paintBorder @@ -41,28 +47,28 @@ class TwitterBadgeShapePainter extends CustomPainter { if (borderGradient != null) { paintBorder.shader = GradientUtils.getGradientShader( badgeGradient: borderGradient!, - width: width, - height: height, + width: maxSize, + height: maxSize, ); } - path.moveTo(width * 0.357, height * 0.156); - path.arcToPoint(Offset(width * 0.643, height * 0.156), - radius: Radius.circular(height * 0.157)); - path.arcToPoint(Offset(width * 0.847, height * 0.396), - radius: Radius.circular(height * 0.165)); - path.arcToPoint(Offset(width * 0.857, height * 0.666), - radius: Radius.circular(height * 0.170)); - path.arcToPoint(Offset(width * 0.643, height * 0.844), - radius: Radius.circular(height * 0.163)); - path.arcToPoint(Offset(width * 0.357, height * 0.844), - radius: Radius.circular(height * 0.157)); - path.arcToPoint(Offset(width * 0.145, height * 0.665), - radius: Radius.circular(height * 0.163)); - path.arcToPoint(Offset(width * 0.154, height * 0.372), - radius: Radius.circular(height * 0.170)); - path.arcToPoint(Offset(width * 0.357, height * 0.156), - radius: Radius.circular(height * 0.163)); + path.moveTo(maxSize * 0.357, maxSize * 0.156); + path.arcToPoint(Offset(maxSize * 0.643, maxSize * 0.156), + radius: Radius.circular(maxSize * 0.157)); + path.arcToPoint(Offset(maxSize * 0.847, maxSize * 0.396), + radius: Radius.circular(maxSize * 0.165)); + path.arcToPoint(Offset(maxSize * 0.857, maxSize * 0.666), + radius: Radius.circular(maxSize * 0.170)); + path.arcToPoint(Offset(maxSize * 0.643, maxSize * 0.844), + radius: Radius.circular(maxSize * 0.163)); + path.arcToPoint(Offset(maxSize * 0.357, maxSize * 0.844), + radius: Radius.circular(maxSize * 0.157)); + path.arcToPoint(Offset(maxSize * 0.145, maxSize * 0.665), + radius: Radius.circular(maxSize * 0.163)); + path.arcToPoint(Offset(maxSize * 0.154, maxSize * 0.372), + radius: Radius.circular(maxSize * 0.170)); + path.arcToPoint(Offset(maxSize * 0.357, maxSize * 0.156), + radius: Radius.circular(maxSize * 0.163)); paint.color = color!; canvas.drawPath(path, paint); diff --git a/test/badges_test.dart b/test/badges_test.dart index 3bd661f..74c4f4d 100644 --- a/test/badges_test.dart +++ b/test/badges_test.dart @@ -1,6 +1,7 @@ import 'package:badges/badges.dart' as badges; import 'package:badges/src/badge_border_gradient.dart'; import 'package:badges/src/badge_gradient_type.dart'; +import 'package:badges/src/badge_shape.dart'; import 'package:badges/src/painters/instagram_badge_shape_painter.dart'; import 'package:badges/src/painters/triangle_badge_shape_painter.dart'; import 'package:badges/src/painters/twitter_badge_shape_painter.dart'; @@ -18,6 +19,68 @@ import 'test_widget_screen.dart'; import 'utils_tests.dart'; void main() { + group('Unit tests', () { + test('Calculate padding in triangle badge with text', () async { + const badge = badges.Badge( + badgeContent: Text('!'), + badgeStyle: badges.BadgeStyle(shape: BadgeShape.triangle), + ); + final badgeState = badge.createState(); + final edgeInsets = badgeState.calculateBadgeContentPadding( + badge.badgeContent, + badge.badgeStyle.shape, + ); + expect(edgeInsets.top, 0); + expect(edgeInsets.bottom, 0); + expect(edgeInsets.left, 10); + expect(edgeInsets.right, 10); + }); + test('Calculate padding in triangle badge with icon', () async { + const badge = badges.Badge( + badgeContent: Icon(Icons.check, size: 10), + badgeStyle: badges.BadgeStyle(shape: BadgeShape.triangle), + ); + final badgeState = badge.createState(); + final edgeInsets = badgeState.calculateBadgeContentPadding( + badge.badgeContent, + badge.badgeStyle.shape, + ); + expect(edgeInsets.top, 0); + expect(edgeInsets.bottom, 0); + expect(edgeInsets.left, 10); + expect(edgeInsets.right, 10); + }); + test('Calculate padding in instagram badge with icon', () async { + const badge = badges.Badge( + badgeContent: Icon(Icons.check, size: 10), + badgeStyle: badges.BadgeStyle(shape: BadgeShape.instagram), + ); + final badgeState = badge.createState(); + final edgeInsets = badgeState.calculateBadgeContentPadding( + badge.badgeContent, + badge.badgeStyle.shape, + ); + expect(edgeInsets.top, 0); + expect(edgeInsets.bottom, 0); + expect(edgeInsets.left, 5); + expect(edgeInsets.right, 5); + }); + test('Calculate padding in instagram badge with text', () async { + const badge = badges.Badge( + badgeContent: Text('test'), + badgeStyle: badges.BadgeStyle(shape: BadgeShape.instagram), + ); + final badgeState = badge.createState(); + final edgeInsets = badgeState.calculateBadgeContentPadding( + badge.badgeContent, + badge.badgeStyle.shape, + ); + expect(edgeInsets.top, 0); + expect(edgeInsets.bottom, 0); + expect(edgeInsets.left, 8); + expect(edgeInsets.right, 8); + }); + }); group('Badge Position tests', () { Widget getBadge(badges.BadgePosition position) { return badges.Badge( @@ -536,7 +599,7 @@ void main() { expect(badgeWidget.badgeStyle.elevation, 2); expect(badgeWidget.badgeStyle.badgeGradient, null); expect(badgeWidget.badgeStyle.borderGradient, null); - expect(badgeWidget.badgeStyle.padding, const EdgeInsets.all(5.0)); + expect(badgeWidget.badgeStyle.padding, null); // Animation expect(badgeWidget.badgeAnimation.toAnimate, true); From 0ec99ea8816749d320cf08b8c9598f27303c955b Mon Sep 17 00:00:00 2001 From: Daniyil Yakovlev Date: Tue, 11 Apr 2023 18:56:13 +0300 Subject: [PATCH 3/3] Version bump to 3.1.0 --- CHANGELOG.md | 4 ++++ README.md | 3 ++- example/.flutter-plugins-dependencies | 2 +- pubspec.yaml | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97e3e21..f3a8e53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [3.1.0] - [April 11, 2023] +* Add BadgeShape.triangle +* Fix custom shapes deformation depending on the badge content + ## [3.0.3] - [March 24, 2023] * Fix transparent color for the badge diff --git a/README.md b/README.md index 1135214..a0669e3 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ In your pubspec.yaml ```yaml dependencies: - badges: ^3.0.3 + badges: ^3.1.0 ``` Attention! In Flutter 3.7 the Badge widget was introduced in the Material library, so to escape the ambiguous imports you need to import the package like this: ```dart @@ -104,6 +104,7 @@ From left to right:
2) BadgeShape.square 3) BadgeShape.twitter 4) BadgeShape.instagram +5) BadgeShape.triangle

--- diff --git a/example/.flutter-plugins-dependencies b/example/.flutter-plugins-dependencies index 4dadac9..a844e10 100644 --- a/example/.flutter-plugins-dependencies +++ b/example/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"integration_test","path":"/Users/vitalii/flutter3/flutter/packages/integration_test/","native_build":true,"dependencies":[]}],"android":[{"name":"integration_test","path":"/Users/vitalii/flutter3/flutter/packages/integration_test/","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"integration_test","dependencies":[]}],"date_created":"2023-04-03 12:55:06.152967","version":"3.7.0"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"integration_test","path":"/Users/yadaniyil/flutter/packages/integration_test/","native_build":true,"dependencies":[]}],"android":[{"name":"integration_test","path":"/Users/yadaniyil/flutter/packages/integration_test/","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"integration_test","dependencies":[]}],"date_created":"2023-04-11 18:51:47.132592","version":"3.7.9"} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 3724bbb..8da0e40 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: badges description: A package for creating badges. Badges can be used for an additional marker for any widget, e.g. show a number of items in a shopping cart. -version: 3.0.3 +version: 3.1.0 repository: https://github.com/yako-dev/flutter_badges issue_tracker: https://github.com/yako-dev/flutter_badges/issues