Skip to content

Commit

Permalink
feat: add triangle badge shape (#107)
Browse files Browse the repository at this point in the history
* feat: add triangle badge shape

* Add widget tests for triangle badge shape
  • Loading branch information
sawel24 authored Apr 6, 2023
1 parent 7182cf4 commit 3273429
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 9 deletions.
2 changes: 1 addition & 1 deletion example/.flutter-plugins-dependencies
Original file line number Diff line number Diff line change
@@ -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"}
{"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"}
29 changes: 22 additions & 7 deletions example/lib/alarm_app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,33 @@ class _AlarmAppState extends State<AlarmApp> {
@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: () {
Expand Down
3 changes: 2 additions & 1 deletion lib/src/badge.dart
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ class BadgeState extends State<Badge> 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(
Expand Down
5 changes: 5 additions & 0 deletions lib/src/badge_shape.dart
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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,
Expand Down
72 changes: 72 additions & 0 deletions lib/src/painters/triangle_badge_shape_painter.dart
Original file line number Diff line number Diff line change
@@ -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;
}
}
8 changes: 8 additions & 0 deletions lib/src/utils/drawing_utils.dart
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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;
Expand Down
71 changes: 71 additions & 0 deletions test/badges_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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<CustomPaint>(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<CustomPaint>(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<CustomPaint>(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<CustomPaint>(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<CustomPaint>(find.byType(CustomPaint)).last;
final painter = customPaint.painter as TriangleBadgeShapePainter;
expect(painter.shouldRepaint(painter), true);
});
});
});

group('Badge tests', () {
Expand Down

0 comments on commit 3273429

Please sign in to comment.