The following is a reference. How can I create a border-only bubble with CustomPainter?
But what I want to achieve is a balloon for the circle. The image will be as follows.
If implemented as follows, they will be separated and drawn as shown in the example.
final path = Path();
// create circle
final center = Offset(size.width / 2, size.height / 2 - 10);
final radius = size.width / 2;
path.addOval(Rect.fromCircle(center: center, radius: radius));
// create tip
path.moveTo(center.dx - 10, center.dy + radius);
path.lineTo(center.dx, center.dy + radius + 12);
path.lineTo(center.dx + 10, center.dy + radius);
path.close();
// draw path
canvas.drawPath(path, paint);
canvas.drawPath(path, borderPaint);
This may be a rudimentary question, but please answer.
I was able to implement it in my own way and share it with you.Better.I'm sure there is a better way.If you have a better way, please let me know.
class BorderBubblePainter extends CustomPainter {
BorderBubblePainter({
this.color = Colors.red,
});
final Color color;
@override
void paint(Canvas canvas, Size size) {
final width = size.width;
// Equivalent to width since it is circular.
// Define a variable with a different name for easier understanding.
final height = width;
const strokeWidth = 1.0;
final paint = Paint()
..isAntiAlias = true
..color = color
..strokeWidth = strokeWidth
..style = PaintingStyle.stroke;
final triangleH = height / 10;
final triangleW = width / 8;
// NOTE: Set up a good beginning and end.
const startAngle = 7;
// NOTE: The height is shifted slightly upward to cover the circle.
final heightPadding = triangleH / 10;
final center = Offset(width / 2, height / 2);
final radius = (size.width - strokeWidth) / 2;
final trianglePath = Path()
..moveTo(width / 2 - triangleW / 2, height - heightPadding)
..lineTo(width / 2, triangleH + height)
..lineTo(width / 2 + triangleW / 2, height - heightPadding)
..addArc(
Rect.fromCircle(center: center, radius: radius),
// θ*π/180=rad
(90 + startAngle) * pi / 180,
(360 - (2 * startAngle)) * pi / 180,
);
canvas.drawPath(trianglePath, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
usage
class BubbleWidget extends StatelessWidget {
const BubbleWidget({
super.key,
});
static const double _width = 100.0;
static const double _height = 108.0;
@override
Widget build(BuildContext context) {
return Stack(
clipBehavior: Clip.none,
alignment: Alignment.center,
children: [
SizedBox(
width: _width,
height: _height,
child: CustomPaint(
painter: BorderBubblePainter(),
),
),
Transform.translate(
offset: const Offset(
0,
-(_height - _width) / 2,
),
child: Icon(
Icons.check,
color: Theme.of(context).colorScheme.primary,
size: 16,
),
),
],
);
}
}