Skip to content
24 changes: 17 additions & 7 deletions example/lib/alarm_app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,28 @@ class _AlarmAppState extends State<AlarmApp> {
@override
Widget build(BuildContext context) {
return badges.Badge(
badgeStyle: badges.BadgeStyle(padding: EdgeInsets.all(7)),
badgeStyle: badges.BadgeStyle(
padding: EdgeInsets.zero,
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: Text(
'!',
style: TextStyle(color: Colors.white),
),
position: badges.BadgePosition.topEnd(top: -12),
child: GestureDetector(
onTap: () {
Expand Down
67 changes: 61 additions & 6 deletions lib/src/badge.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:badges/src/badge_border_gradient.dart';
import 'package:badges/src/utils/calculation_utils.dart';
import 'package:badges/src/utils/drawing_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';

class Badge extends StatefulWidget {
const Badge({
Expand Down Expand Up @@ -62,10 +63,15 @@ class BadgeState extends State<Badge> with TickerProviderStateMixin {
late AnimationController _appearanceController;
late Animation<double> _animation;
bool enableLoopAnimation = false;
double? textSize;
final GlobalKey _key = GlobalKey();
double? _widgetSize;

@override
void initState() {
super.initState();
SchedulerBinding.instance
.addPostFrameCallback((_) => scaleWidgetSize(_key, badge: widget));
enableLoopAnimation =
widget.badgeAnimation.animationDuration.inMilliseconds > 0;
_animationController = AnimationController(
Expand Down Expand Up @@ -97,6 +103,30 @@ class BadgeState extends State<Badge> with TickerProviderStateMixin {
}
}

void scaleWidgetSize(GlobalKey key, {required Badge badge, Badge? oldBadge}) {
double newSize = 0;

if (badge.badgeContent is Text) {
final newText = badge.badgeContent as Text;
final size =
CalculationUtils.calculateSizeOfText(newText.data!, newText.style);
newSize = size.width >= size.height
? size.width * 1.1764
: size.height * 1.1764;
} else if (badge.badgeContent is Icon) {
newSize = (badge.badgeContent as Icon).size ?? 0;
} else {
final RenderBox? childBox =
_key.currentContext?.findRenderObject() as RenderBox?;
if (childBox != null) {
Size size = childBox.size;
newSize = size.height >= size.width ? size.height : size.width;
}
}
newSize *= badge.badgeStyle.shape == BadgeShape.triangle ? 1.7 : 1;
setState(() => _widgetSize = newSize);
}

@override
Widget build(BuildContext context) {
if (widget.child == null) {
Expand All @@ -113,7 +143,8 @@ class BadgeState extends State<Badge> with TickerProviderStateMixin {
widget.onTap == null
? widget.child!
: Padding(
padding: CalculationUtils.calculatePadding(widget.position),
padding: CalculationUtils.calculatePaddingByPosition(
widget.position),
child: widget.child!,
),
BadgePositioned(
Expand Down Expand Up @@ -157,7 +188,9 @@ 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 isSquareShape = widget.badgeStyle.shape == BadgeShape.square;

final gradientBorder = widget.badgeStyle.borderGradient != null
? BadgeBorderGradient(
Expand All @@ -183,8 +216,18 @@ class BadgeState extends State<Badge> with TickerProviderStateMixin {
borderSide: widget.badgeStyle.borderSide,
),
child: Padding(
padding: widget.badgeStyle.padding,
child: widget.badgeContent,
padding: CalculationUtils.calculateBadgeContentPadding(
widget.badgeContent,
widget.badgeStyle,
),
child: SizedBox(
width: _widgetSize,
height: _widgetSize,
child: Center(
key: _key,
child: widget.badgeContent,
),
),
),
)
: Material(
Expand Down Expand Up @@ -214,8 +257,18 @@ class BadgeState extends State<Badge> with TickerProviderStateMixin {
border: gradientBorder,
),
child: Padding(
padding: widget.badgeStyle.padding,
child: widget.badgeContent,
padding: CalculationUtils.calculateBadgeContentPadding(
widget.badgeContent,
widget.badgeStyle,
),
child: SizedBox(
width: _widgetSize,
height: isSquareShape ? null : _widgetSize,
child: Center(
key: _key,
child: widget.badgeContent,
),
),
),
),
),
Expand Down Expand Up @@ -261,6 +314,8 @@ class BadgeState extends State<Badge> with TickerProviderStateMixin {
@override
void didUpdateWidget(Badge oldWidget) {
super.didUpdateWidget(oldWidget);
SchedulerBinding.instance.addPostFrameCallback(
(_) => scaleWidgetSize(_key, badge: widget, oldBadge: oldWidget));
if (widget.badgeAnimation.toAnimate) {
if (widget.badgeStyle.badgeColor != oldWidget.badgeStyle.badgeColor &&
widget.showBadge) {
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
4 changes: 2 additions & 2 deletions lib/src/badge_style.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -41,6 +41,6 @@ class BadgeStyle {
this.elevation = 2,
this.badgeGradient,
this.borderGradient,
this.padding = const EdgeInsets.all(5.0),
this.padding,
});
}
64 changes: 35 additions & 29 deletions lib/src/painters/instagram_badge_shape_painter.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -21,15 +23,19 @@ 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();

if (badgeGradient != null) {
paint.shader = GradientUtils.getGradientShader(
badgeGradient: badgeGradient!,
width: width,
height: height,
width: maxSize,
height: maxSize,
);
}
paintBorder
Expand All @@ -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);
Expand Down
78 changes: 78 additions & 0 deletions lib/src/painters/triangle_badge_shape_painter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import 'dart:math' as math;

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;

final double maxSize = math.max(width, height);

canvas.clipRect(Offset.zero & Size(maxSize, maxSize));

Path path = Path();
Paint paint = Paint();
Paint paintBorder = Paint();

if (badgeGradient != null) {
paint.shader = GradientUtils.getGradientShader(
badgeGradient: badgeGradient!,
width: maxSize,
height: maxSize,
);
}
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: maxSize,
height: maxSize,
);
}
path
..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!;
canvas.drawPath(path, paint);
if (borderSide != BorderSide.none) {
canvas.drawPath(path, paintBorder);
}
}

@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
Loading