You can see my arc starts from top but there is some area which is painted pink, it should be white. I think it is because I am using StrokeCap.round, but how do I remove that part?
Painter class:
class MyPainter extends CustomPainter {
void paint(Canvas canvas, Size size) {
double centerPoint = size.height / 2;
Paint paint = Paint()
..color = Colors.white
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke
..strokeWidth = 20;
paint.shader = SweepGradient(
colors: [Colors.white, Colors.pink],
tileMode: TileMode.repeated,
startAngle: _degreeToRad(270),
endAngle: _degreeToRad(270 + 360.0),
).createShader(Rect.fromCircle(center: Offset(centerPoint, centerPoint), radius: 0));
double startAngle = _degreeToRad(270);
double sweepAngle = _degreeToRad(95 / 100 * 360);
Rect rect = Rect.fromCircle(center: Offset(centerPoint, centerPoint), radius: centerPoint);
canvas.drawArc(rect, startAngle, sweepAngle, false, paint);
}
double _degreeToRad(double degree) => degree * math.pi / 180;
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
Usage:
CustomPaint(
size: Size.fromRadius(100),
painter: MyPainter(),
)
when you are using strokeCap = StrokeCap.round it adds half of the stroke width to start of your line and half of it to the end for creating the round edges of the line so in your calculation you should remove half of the stroke Width from the start and a half of it from end
I changed your code and it works as should here is the code:
class MyPainter extends CustomPainter {
void paint(Canvas canvas, Size size) {
double centerPoint = size.height / 2;
double strokeWidth = 30;
double percentValue=100/100;
double radius=centerPoint;
Paint paint = Paint()
..color = Colors.white
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke
..strokeWidth = strokeWidth;
paint.shader = SweepGradient(
colors: [Colors.black, Colors.pink],
tileMode: TileMode.repeated,
startAngle: _degreeToRad(270),
endAngle: _degreeToRad(270 + 360.0),
).createShader(Rect.fromCircle(center: Offset(centerPoint, centerPoint), radius: 0));
Rect rect = Rect.fromCircle(center: Offset(centerPoint, centerPoint), radius: radius);
var scapSize = strokeWidth / 2;
double scapToDegree = scapSize / radius;
double startAngle = _degreeToRad(270)+scapToDegree;
double sweepAngle = _degreeToRad( 360)-(2*scapToDegree);
canvas.drawArc(rect, startAngle, percentValue*sweepAngle, false, paint);
}
double _degreeToRad(double degree) => degree * pi / 180;
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
And a performance tip for you :
don't create your shader and gradient each time , the Paint method
will be executed numers of time so you should check if you already
created the shader for the inComing size use that and don't create it
again
Related
I try to make custom progress indicator with Custom painter but painter is not updating the value
help:
can you help me to use custom painter?
also can we change circle pointer to square box with arrow pointing to progress
class MyCsPaint extends CustomPainter {
MyCsPaint({required this.progress});
final double progress;
#override
void paint(Canvas canvas, Size size) {
Paint paintBg = Paint()
..color = Colors.teal.shade100
..strokeWidth = 1
..strokeCap = StrokeCap.round;
Paint paint = Paint()
..color = Colors.teal
..strokeWidth = 5
..strokeCap = StrokeCap.round;
Offset p1bg = Offset(size.width, size.height);
Offset p2bg = Offset(0, size.height);
Offset p2 = Offset(0, size.height);
Offset p1 = Offset(size.width * progress, size.height);
canvas.drawLine(p1bg, p2bg, paintBg);
canvas.drawLine(p1, p2, paint);
Offset pc = Offset(size.width * progress, size.height * .7);
canvas.drawCircle(pc, 10, paint);
final textStyle = ui.TextStyle(
color: Colors.black,
fontSize: 16,
);
final paragraphStyle = ui.ParagraphStyle(
textDirection: TextDirection.ltr,
);
final paragraphBuilder = ui.ParagraphBuilder(paragraphStyle)
..pushStyle(textStyle)
..addText('${progress * 100} %');
final constraints = ui.ParagraphConstraints(width: size.width);
final paragraph = paragraphBuilder.build();
paragraph.layout(constraints);
canvas.drawParagraph(paragraph, pc);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
I want to draw/insert image and text inside canvas circle but I am unable to do that. I am posting screenshot for better understanding of my requirements
Image
Code
void paint(Canvas canvas, Size size) {
Size size = MediaQuery.of(buildContext).size;
double strokeWidth = 10;
Rect myRect = Offset(-size.width * 0.21, -40) &
Size(size.width * 0.195, size.width * 0.195);
var paint1 = Paint()
..color = Color(0xFF23BBFF)
..strokeWidth = strokeWidth
..style = PaintingStyle.stroke;
var paint2 = Paint()
..color = Color(0xFFFF8623)
..strokeWidth = strokeWidth
..style = PaintingStyle.stroke;
double firstLineRadianStart = 0;
double _unAnswered = 0.30;
double firstLineRadianEnd = (360 * _unAnswered) * pi / 180;
canvas.drawArc(
myRect, firstLineRadianStart, firstLineRadianEnd, false, paint1);
double _learned = 1 - _unAnswered;
double secondLineRadianEnd = getRadians(_learned);
canvas.drawArc(
myRect, firstLineRadianEnd, secondLineRadianEnd, false, paint2);
}
double getRadians(double value) {
return (360 * value) * pi / 180;
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
Please tell me how can I create this Arc with Radius CustomPainter UI Component like this Mockup Design?
There must be a gap between Start & End angle.
Code:
class ArcPainter extends CustomPainter {
final double angle = 310.0;
double toAngle(double angle) => angle * pi / 180.0;
#override
void paint(Canvas canvas, Size size) {
final Offset center = Offset(size.width / 2.0, size.height / 2.0);
final double radius = size.width / 3.0;
Paint paint = Paint()
..strokeCap = StrokeCap.round
..strokeWidth = 8.0
..style = PaintingStyle.stroke
..color = Colors.pink;
canvas.drawArc(
Rect.fromCircle(center: center, radius: radius),
toAngle(110.0),
toAngle(angle),
true,
paint,
);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
Problem:
Here is the current output for my Arc with Radius. The problem is that borders are showing in the arc gap. I want to remove the border in the gap.
you can create drawPath instead of drawArc
#override
void paint(Canvas canvas, Size size) {
final center = size.center(Offset.zero);
final oval = Rect.fromCenter(
center: center,
width: size.width,
height: size.height,
);
var progressPath = _path(oval, 200);
var progressPaint = _paint(Color(0xFF8987F7));
var backgroundPath = _path(oval, 280);
var backgroundPaint = _paint(Color(0xFF3a329e));
canvas
..drawPath(backgroundPath, backgroundPaint)
..drawPath(progressPath, progressPaint);
}
Paint _paint(Color color) {
return Paint()
..strokeCap = StrokeCap.round
..strokeWidth = 15.0
..style = PaintingStyle.stroke
..color = color;
}
Path _path(Rect oval, double angle) {
return Path()
..addArc(
oval,
toAngle(130),
toAngle(angle),
);
}
You told it to close the gap using the center. You can tell it to not do so:
canvas.drawArc(
Rect.fromCircle(center: center, radius: radius),
toAngle(110.0),
toAngle(angle),
false, // this is called "useCenter" for a reason...
paint,
);
I've just started getting to grips with Custom painter and I've created a basic cross but not sure how to make the corners rounded and make the colour a gradient?
It seems a gradient requires createShader which takes a rect but I don't have one since I used paths.
Also I'd like to round the corners of the cross a little but not sure how that is done. I thought about creating rectangles but it doesn't seem to be possible to create slanted rectangles either.
class CrossPainter extends CustomPainter {
CrossPainter({this.bodyColour, this.strokeColour, this.stroke});
final Color bodyColour;
final Color strokeColour;
final double stroke;
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint();
paint
..color = strokeColour
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..strokeWidth = stroke;
Paint paint1 = Paint();
paint1
..color = bodyColour
..style = PaintingStyle.fill
..strokeWidth = 0;
double width = size.width;
double height = size.height;
Path path = Path();
path.addPolygon([Offset.zero, Offset(width / 4, 0), Offset(width, height), Offset(width - (width / 4), height)], true);
Path path2 = Path();
path2.addPolygon(
[Offset(width, 0), Offset(width - (width / 4), 0), Offset(0, height), Offset(0 + (width / 4), height)], true);
canvas.drawPath(path, paint1);
canvas.drawPath(path2, paint1);
// canvas.drawPath(path, paint); //border
// canvas.drawPath(path2, paint); //border
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
I also have paint1 which adds a border but it doesn't look so good since each polygon is a separate object.
You can freely use Rect on the Shader to fill gradient on rounded polygon drawn on the canvas. On my approach, I used the size defined on the canvas as a base for the Rect.
Paint()
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..shader = gradient
.createShader(Rect.fromLTWH(0.0, 0.0, size.width, size.height))
..strokeWidth = 20,
You can then add this paint object to the canvas that you're rendering.
As for the gradient, you can define it however you like. But here's an example.
const Gradient gradient = SweepGradient(
startAngle: 1.25 * math.pi / 2,
endAngle: 5.5 * math.pi / 2,
tileMode: TileMode.repeated,
colors: <Color>[
Colors.blueAccent,
Colors.lightBlueAccent,
],
);
I'm currently blocked trying to create a widget (like a tooltip box) that is represented by the image below. I should probably be able to create it by relying on a Painter class, but i'm not familiar doing so...
https://pasteboard.co/IgccNxD.png
(it's small, yes, max height = ~35px)
The code ended being something like:
void paint(Canvas canvas, Size size) {
Paint p = Paint()
..color = Colors.red
..isAntiAlias = true
..style = PaintingStyle.fill;
Offset nw = Offset(0, 0);
Offset se = Offset(size.width, size.height * 0.8);
final Rect rect = Rect.fromPoints(nw, se);
final RRect r = RRect.fromRectAndRadius(rect, Radius.circular(10));
canvas.drawRRect(r, p);
Offset bottomPoint = Offset((size.width / 2), size.height);
Offset rightPoint = Offset((size.width / 2) * 0.80, size.height * 0.80);
Offset leftPoint = Offset((size.width / 2) * 1.20, size.height * 0.80);
var path1 = Path()
..moveTo(rightPoint.dx, rightPoint.dy)
..lineTo(bottomPoint.dx, bottomPoint.dy)
..lineTo(leftPoint.dx, leftPoint.dy)
..lineTo(rightPoint.dx, rightPoint.dy);
canvas.drawPath(path1, p);
}
For the text inside, I've stacked it above this painting.