Created
May 20, 2025 14:48
-
-
Save xsahil03x/7300e17e3616f12c47e52a239e9973ff to your computer and use it in GitHub Desktop.
ReactionBubblePainter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
enum TailAlignment { start, center, end } | |
class ReactionBubblePainter extends CustomPainter { | |
ReactionBubblePainter({ | |
required Color fillColor, | |
required Color maskColor, | |
required Color borderColor, | |
this.flipTail = false, | |
this.tailAlignment = TailAlignment.center, | |
double borderWidth = 1.0, | |
this.maskWidth = 2.0, | |
this.bigTailCircleRadius = 4.0, | |
this.smallTailCircleRadius = 2.0, | |
}) : _fillPaint = Paint() | |
..color = fillColor | |
..style = PaintingStyle.fill, | |
_maskPaint = Paint() | |
..color = maskColor | |
..style = PaintingStyle.fill, | |
_borderPaint = Paint() | |
..color = borderColor | |
..style = PaintingStyle.stroke | |
..strokeWidth = borderWidth; | |
final double maskWidth; | |
final double bigTailCircleRadius; | |
final double smallTailCircleRadius; | |
final bool flipTail; | |
final TailAlignment tailAlignment; | |
final Paint _fillPaint; | |
final Paint _borderPaint; | |
final Paint _maskPaint; | |
@override | |
void paint(Canvas canvas, Size size) { | |
final tailHeight = bigTailCircleRadius * 2 + smallTailCircleRadius * 2; | |
final fullHeight = size.height + tailHeight; | |
final bubbleHeight = fullHeight - tailHeight; | |
final bubbleWidth = size.width; | |
final bubbleRect = RRect.fromRectAndRadius( | |
Rect.fromLTRB(0, 0, bubbleWidth, bubbleHeight), | |
Radius.circular(bubbleHeight / 2), | |
); | |
final bigTailCircleLeft = switch (tailAlignment) { | |
TailAlignment.start => bubbleRect.left + 8, | |
TailAlignment.center => bubbleRect.center.dx, | |
TailAlignment.end => bubbleRect.right - 8, | |
}; | |
final bigTailCircleRect = Rect.fromCircle( | |
center: Offset(bigTailCircleLeft, bubbleRect.bottom), | |
radius: bigTailCircleRadius, | |
); | |
final smallTailCircleOffset = Offset( | |
flipTail ? bigTailCircleRect.right : bigTailCircleRect.left, | |
bigTailCircleRect.bottom + smallTailCircleRadius, | |
); | |
final smallTailCircleRect = Rect.fromCircle( | |
center: smallTailCircleOffset, | |
radius: smallTailCircleRadius, | |
); | |
final reactionBubbleMaskPath = _buildCombinedPath( | |
bubbleRect.inflate(maskWidth), | |
bigTailCircleRect.inflate(maskWidth), | |
smallTailCircleRect.inflate(maskWidth), | |
); | |
canvas.drawPath(reactionBubbleMaskPath, _maskPaint); | |
final reactionBubblePath = _buildCombinedPath( | |
bubbleRect, | |
bigTailCircleRect, | |
smallTailCircleRect, | |
); | |
canvas.drawPath(reactionBubblePath, _borderPaint); | |
canvas.drawPath(reactionBubblePath, _fillPaint); | |
} | |
Path _buildCombinedPath( | |
RRect bubble, | |
Rect bigCircle, | |
Rect smallCircle, | |
) { | |
final bubblePath = Path()..addRRect(bubble); | |
final bigTailPath = Path()..addOval(bigCircle); | |
final smallTailPath = Path()..addOval(smallCircle); | |
return Path.combine( | |
PathOperation.union, | |
Path.combine(PathOperation.union, bubblePath, bigTailPath), | |
smallTailPath, | |
); | |
} | |
@override | |
bool shouldRepaint(covariant ReactionBubblePainter oldDelegate) => false; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment