Using the latest flutter I would like to draw an arch from 0 to 10 using the Canvas.drawArc method. Here's what my code looks like:
#override
void paint(Canvas canvas, Size size) {
final double radius = 1000.0;
final Paint paint = new Paint()
..isAntiAlias = true
..strokeWidth = 1.0
..color = Colors.blue[500]
..style = PaintingStyle.stroke;
canvas.drawArc(new Rect.fromLTWH(0.0, 0.0, size.width/2, size.height/2),
10.0, 20.0, false, paint);
}
It's being drawn inside of a CustomPainter, and is called in the layout like this:
children: <Widget>[
new CustomPaint(
painter: new CircleGraphWidget(),
child: new Center(
child: new Text(
'Here\'s text',
style: const TextStyle(
fontSize: 40.0,
fontWeight: FontWeight.w900,
color: const Color(0xFFFFFFFFF),
),
),
),
)
],
I expect the drawArc call to draw an arc within the rect from 10 to 20 on the circle, however this is what I get:
I'm wondering what it takes to draw just a fraction of the oval instead of the whole thing. For instance, if I only wanted 1/4 of the circle drawn, how would I go about it?
How to draw arcs
To paint in Flutter you use the CustomPaint widget. The CustomPaint widget takes a CustomPainter object as a parameter. In that class you have to override the paint method, which gives you a canvas that you can paint on. Here is the code to draw the arc in the image above.
// Don't forget: import 'dart:math' as math;
#override
void paint(Canvas canvas, Size size) {
final rect = Rect.fromLTRB(50, 100, 250, 200);
final startAngle = -math.pi / 2;
final sweepAngle = math.pi;
final useCenter = false;
final paint = Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeWidth = 4;
canvas.drawArc(rect, startAngle, sweepAngle, useCenter, paint);
}
Notes:
The rect is what the full oval would be inscribed within.
The startAngle is the location on the oval that the line starts drawing from. An angle of 0 is at the right side. Angles are in radians, not degrees. The top is at 3π/2 (or -π/2), the left at π, and the bottom at π/2.
The sweepAngle is how much of the oval is included in the arc. Again, angles are in radians. A value of 2π would draw the entire oval.
If you set useCenter to true, then there will be a straight line from both sides of the arc to the center.
Context
Here is the main.dart code so that you can see it in context.
import 'dart:math' as math;
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: HomeWidget(),
),
);
}
}
class HomeWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: CustomPaint( // <-- CustomPaint widget
size: Size(300, 300),
painter: MyPainter(),
),
);
}
}
class MyPainter extends CustomPainter { // <-- CustomPainter class
#override
void paint(Canvas canvas, Size size) {
// <-- Insert your painting code here.
}
#override
bool shouldRepaint(CustomPainter old) {
return false;
}
}
See also
See this article for my fuller answer.
https://docs.flutter.io/flutter/dart-ui/Canvas/drawArc.html
It starts from startAngle radians around the oval up to startAngle + sweepAngle radians around the oval, with zero radians
It expects Radians instead of Degrees:
canvas.drawArc(new Rect.fromLTWH(0.0, 0.0, size.width/2, size.height/2),
0.175, 0.349, false, paint);
Related
I am trying to create a border with curved lines and rounded corners in Flutter. I was able to curve the lines using shapemaker.web.app, but I was not able to round the corners.
I have already tried it with a ClipPath + Clipper:
class NativeClipper extends CustomClipper<Path> {
#override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => true;
#override
Path getClip(Size size) {
Path path0 = Path();
path0.moveTo(size.width*0.0833333,size.height*0.0714286);
path0.lineTo(size.width*0.9166667,size.height*0.0714286);
path0.quadraticBezierTo(size.width*0.9583333,size.height*0.5000143,size.width*0.9166667,size.height*0.9285714);
path0.cubicTo(size.width*0.7083333,size.height*0.9285714,size.width*0.2916667,size.height*0.9285714,size.width*0.0833333,size.height*0.9285714);
path0.quadraticBezierTo(size.width*0.0416833,size.height*0.5001143,size.width*0.0833333,size.height*0.0714286);
return path0;
}
}
Current Result:
Expected Result: (do not pay attention to the Colors)
Is there some way to draw a dashed arc in Flutter?
At the moment I'm using canvas.drawArc but I don't know how to get the correct result.
canvas.drawArc(
rectangle,
startAngle,
fullArcRadius,
false,
Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke
..strokeWidth = 2.0,
);
dashed-arc
Unfortunately, flutter doesn't handle dashes all that well. There is a plugin that helps with it though: path_drawing
Using that, you can draw any path dashed simply by wrapping it in the dashPath function. That sounds simple enough, but it means that you can't use the canvas.drawArc method which complicates things a little. You have to use canvas.drawPath instead and figure out how to draw a path which is the same as that arc.
This is how I'd do it (and I've put in the code I use to draw the item fit to the canvas which you can use or ignore as you see fit):
import 'package:flutter/material.dart';
import 'package:path_drawing/path_drawing.dart';
class DashedArc extends CustomPainter {
final Color color;
DashedArc({Color color}) : color = color ?? Colors.white;
#override
void paint(Canvas canvas, Size size) {
// TODO: remove me. This makes it easier to tell
// where the canvas should be
canvas.drawRect(
Offset.zero & size,
Paint()
..color = Colors.black
..style = PaintingStyle.stroke);
var width = 520, height = 520, scale;
// this is a simple Boxfit calculation for the `cover` mode. You could
// use the applyBoxFit function instead, but as it doesn't return a
// centered rect it's almost as much work to use it as to just do it
// manually (unless someone has a better way in which case I'm all ears!)
double rw = size.width / width;
double rh = size.height / height;
double actualWidth, actualHeight, offsetLeft, offsetTop;
if (rw > rh) {
// height is constraining attribute so scale to it
actualWidth = rh * width;
actualHeight = size.height;
offsetTop = 0.0;
offsetLeft = (size.width - actualWidth) / 2.0;
scale = rh;
} else {
// width is constraining attribute so scale to it
actualHeight = rw * height;
actualWidth = size.width;
offsetLeft = 0.0;
offsetTop = (size.height - actualHeight) / 2.0;
scale = rw;
}
canvas.translate(offsetLeft, offsetTop);
canvas.scale(scale);
// parameters from the original drawing (guesstimated a bit using
// preview...)
const double startX = 60;
const double startY = 430; // flutter starts counting from top left
const double dashSize = 5;
const double gapSize = 16;
canvas.drawPath(
dashPath(
Path()
// tell the path where to start
..moveTo(startX, startY)
// the offset tells the arc where to end, the radius is the
// radius of the circle, and largeArc tells it to use the
// big part of the circle rather than the small one.
// The implied parameter `clockwise` means that it starts the arc
// and draw clockwise; setting this to false would result in a large
// arc below!
..arcToPoint(Offset(520 - startX, startY), radius: Radius.circular(260), largeArc: true),
// dash is `dashSize` long followed by a gap `gapSize` long
dashArray: CircularIntervalList<double>([dashSize, gapSize]),
dashOffset: DashOffset.percentage(0.005)),
Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeWidth = dashSize);
}
#override
bool shouldRepaint(DashedArc oldDelegate) {
return oldDelegate.color != this.color;
}
}
I want to create a gradient shader.
The Gradient.radial() constructor should return one.
However, VS Code claims that this method is not defined for Gradient. huh.
Now, from what I've seen online, you can create a RadialGradient object and then use .createShader(rect) to convert it into a shader (haven't got it work yet, but maybe I'm implementing it wrong), but that can't be the intended way, right?
Update:
As #pskink pointed out, I'm using a newer version of Flutter and have to import the Gradient shader class as part of the Dart ui library.
The resulting code is valid and looks like this:
paint.shader = ui.Gradient.radial(
Offset(-.5, -.5),
1, // The radius
List.from({
Colors.lightBlueAccent,
Colors.blue,
Colors.deepPurpleAccent},
growable: false
)
);
Unfortunately it doesn't render anything.
This is how the code with the .createShader() method on the other Gradient class looks.
RadialGradient _gradient = RadialGradient(
colors: List.from({
Colors.lightBlueAccent,
Colors.blue,
Colors.deepPurpleAccent},
growable: false),
);
// center is the center of the canvas
paint.shader = _gradient.createShader(
Rect.fromCircle(
center: center,
radius: size.shortestSide / 2
)
);
This is kinda ugly IMO (and less efficient?), but it does render the expected result.
2nd Update:
When having more than 2 color elements in the Gradient shader class you can't omit the stops. This info is just being presented as an errorlog, which is why I didn't see it. With that being fixed: Both versions return different results...
This code returns this gradient:
#override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final paint = Paint();
RadialGradient _gradient = RadialGradient(
radius: 1,
center: Alignment(-.5, -.5),
tileMode: TileMode.clamp,
colors: List.from({Colors.lightBlueAccent, Colors.blue, Colors.deepPurpleAccent}, growable: false),
stops: List.from({0.0, 0.5, 1.0}, growable: false),
);
paint.shader = _gradient.createShader(
Rect.fromCircle(
center: center,
radius: size.shortestSide / 2
)
);
canvas.drawCircle(center, size.shortestSide / 2, paint);
}
While this code renders this gradient:
#override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final paint = Paint();
paint.shader = ui.Gradient.radial(
Offset(-.5, -.5),
1, // this is the radius
List.from({Colors.lightBlueAccent, Colors.blue, Colors.deepPurpleAccent},
growable: false),
List.from({0.0, 0.5, 1.0}, growable: false),
TileMode.clamp,
);
canvas.drawCircle(center, size.shortestSide / 2, paint);
}
I want to draw the different pattern in the flutter like this
Although this code is javascript is given in this link
I used ImageShader and used shader property of Paint Function
device transform is used to get the device pixel so that it can render the effect
This is my paint method look like...
final double devicePixelRatio = ui.window.devicePixelRatio;
#override
void paint(Canvas canvas, Size size) {
final Float64List deviceTransform = new Float64List(16)
..[0] = devicePixelRatio
..[5] = devicePixelRatio
..[10] = 1.0
..[15] = 2.0;
Float64List matrix = new Float64List(16);
print('matrix is $matrix');
print('image is coming in paint $image');
Paint paint = new Paint()
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..shader = ImageShader(
image, TileMode.repeated, TileMode.repeated, deviceTransform)
..strokeWidth = 40.2;
paths.forEach((path) {
canvas.drawPath(path, paint);
});
repaint = false;
}
Here I am passing a list of the image to ImagePaint class to clip image in a circular, Its fine the images are clipping but only clipping some part of the image, If I could decrease this height and width of the image. Then it will fit the circle, Please comment if any have an idea.
void paint(Canvas canvas, Size size) {
int c = 0;
canvas.save();
canvas.translate(size.width / 2, size.height / 2);
canvas.rotate(-rotation);
for (var i = 0; i < 16; ++i) {
if (i % 2 == 0) {
canvas.drawLine(
new Offset(0.0, 0.0),
new Offset(0.0, size.width / 2 - 4.2),
tickPaint,
);
} else {
canvas.save();
canvas.translate(-0.0, -((size.width) / 2));
canvas.clipPath(path);
if (images[c] != null) {
ui.Image img = images[c];
canvas.drawImage(img, Offset(0.0, 0.0), new Paint());
}
canvas.rotate(2 * pi);
canvas.restore();
c++;
}
canvas.rotate(2 * pi / 16);
}
canvas.restore();}
This image is printed in a circular manner:
This is my app I am clipping image and printing in circular:
I just figured out how to reduce the image and send it to the painter for drawing.
The method that shrinks the image and what it returns is sent to the drawing class.
If it works for you, thank me for my reply with a thumbs up, thanks.
import 'dart:ui' as ui;
import 'package:image/image.dart' as IMG;
Future<ui.Image> loadImage( List<int> data ) async {
final IMG.Image image = IMG.decodeImage(data);
final IMG.Image resized = IMG.copyResize(image, width: 200);
final List<int> resizedBytes = IMG.encodePng(resized);
final Completer<ui.Image> completer = new Completer();
ui.decodeImageFromList(resizedBytes, (ui.Image img) => completer.complete(img));
return completer.future;
}
Drawing class.
class CustomDraw extends CustomPainter {
final ui.Image image;
const CustomDraw({this.image});
#override
void paint(Canvas canvas, Size size) {
Paint paint = new Paint();
canvas.drawImage(image, new Offset(40.0, 10.0), paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}