Playing with Paths in Flutter
Everything in Flutter is a Widget, and Flutter provides a lot of great widgets for usto use, but the one we love most about Flutter is CustomPaint.
The CustomPaint component provides a canvas on which we can draw the content we want to paint during the Paint phase of Flutter.
There are many different ways to draw on a Canvas, but the most efficient and common is using Path. In this article, I’ll show you how to draw a Path and apply animation to it. If you’re not familiar with Path, check out this article.
A line drawing
Drawing lines through Path is very easy in Flutter. First, the start of the drawing is moved to the specified position using the moveTo method, and then the drawing is performed using the lineTo method.
class LinePainter extends CustomPainter { final double progress; LinePainter({this.progress}); Paint _paint = Paint() .. color = Colors.black .. StrokeWidth = 4.0.. style = PaintingStyle.stroke .. strokeJoin = StrokeJoin.round; @override void paint(Canvas canvas, Size size) { var path = Path(); path.moveTo(0, size.height / 2); path.lineTo(size.width * progress, size.height / 2); canvas.drawPath(path, _paint); } @override bool shouldRepaint(LinePainter oldDelegate) {return oldDelegate.progress != progress;
}
}
Copy the code
Effect:
Second, draw a dotted line
Drawing dashed lines is a bit more complicated than drawing straight lines. There is no direct method for drawing dashed lines in Flutter, but we can do this with PathMetric.
PathMetric is a tool that measures paths and extracts subpaths.
First, we draw a line, just like the line above, and then we get the PathMetrics object through path.putemetrics (). By traversing the PathMetric, we can extract a subpath whose starting point has the current distance specified and whose length is our own dashWidth.
dashPath.addPath(
pathMetric.extractPath(distance, distance + dashWidth),
Offset.zero,
);
Copy the code
Complete code:
class DashLinePainter extends CustomPainter { final double progress; DashLinePainter({this.progress}); Paint _paint = Paint() .. color = Colors.black .. StrokeWidth = 4.0.. style = PaintingStyle.stroke .. strokeJoin = StrokeJoin.round; @override void paint(Canvas canvas, Size size) { var path = Path() .. moveTo(0, size.height / 2) .. lineTo(size.width * progress, size.height / 2); Path dashPath = Path(); Double dashWidth = 10.0; Double dashSpace = 5.0; Double short = 0.0;for (PathMetric pathMetric in path.computeMetrics()) {
while (distance < pathMetric.length) {
dashPath.addPath(
pathMetric.extractPath(distance, distance + dashWidth),
Offset.zero,
);
distance += dashWidth;
distance += dashSpace;
}
}
canvas.drawPath(dashPath, _paint);
}
@override
bool shouldRepaint(DashLinePainter oldDelegate) {
return oldDelegate.progress != progress;
}
}
Copy the code
Effect:
Third, a circle
A circle is essentially a special ellipse. We can draw an ellipse using the addOval method, which takes a Rect parameter. If we want to draw a circle, we can do it using rect.fromCircle.
@override
void paint(Canvas canvas, Size size) {
var path = Path();
path.addOval(Rect.fromCircle(
center: Offset(0, 0),
radius: 80.0,
));
canvas.drawPath(path, myPaint);
}
Copy the code
The above code looks like this:
It’s easy to draw a circle, but try a more complex one like this:
A simple analysis of the above drawing shows that all circles are of the same size and tangent to the same point (0,0), and then the intersection of all circles can form a circle with the same radians between adjacent points.
Let’s first try to analyze the relationship between these points of intersection.
First assume that there are n circles, so there will be n points of intersection, and then assume that one of the points (also the center of a circle) has the coordinates (x,y).
Since the radian of a circle is 2π, the radian of a circle is graded as follows:
And now we have trigonometric functions that we learned in high school. Through the above analysis, we can get the following values:
Then the values of x and y are calculated:
So here we have the calculation method for x and y of the center of each circle, and then the radius r of the circle is specified by ourselves, so we know all the information we need to draw the circle, expressed in code as follows:
@override
void paint(Canvas canvas, Size size) {
var path = createPath();
canvas.drawPath(path, myPaint);
}
Path createPath() {
var path = Path();
int n = circles.toInt();
var range = List<int>.generate(n, (i) => i + 1);
double angle = 2 * math.pi / n;
for (int i in range) {
double x = radius * math.cos(i * angle);
double y = radius * math.sin(i * angle);
path.addOval(Rect.fromCircle(center: Offset(x, y), radius: radius));
}
return path;
}
Copy the code
Since we know the number, radius and center of the circle, we can draw the circle dynamically.
- The number of circles changes dynamically
- Dynamic drawing of circle
Animation requires the AnimationController
class _CirclesState extends State<Circles> with SingleTickerProviderStateMixin {
AnimationController _controller;
@override
void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: Duration(seconds: 3), ); _controller. Value = 1.0; }Copy the code
Dynamic rendering of circles requires the use of pathMetrics, an auxiliary class that can be used to measure and extract subpaths.
@override
void paint(Canvas canvas, Size size) {
var path = createPath();
PathMetrics pathMetrics = path.computeMetrics();
for (PathMetric pathMetric inPathMetrics) {Path extractPath = pathmetric.extractPath (0.0, pathmetric.length * progress,); canvas.drawPath(extractPath, myPaint); }}Copy the code
The detailed code reference is as follows:
Github.com/divyanshub0…
Effect:
Four, polygon drawing
Another important part of path drawing is polygon drawing, where each side of a polygon is a straight line.
The coordinates of each vertex in a polygon are calculated in a manner similar to that of the center of a circle mentioned above.
Knowing the coordinates of the vertices, it’s easy to draw each edge.
class PolygonPainter extends CustomPainter { PolygonPainter({ this.sides, this.progress, this.showPath, this.showDots, }); final double sides; final double progress; bool showDots, showPath; final Paint _paint = Paint() .. color = Colors.purple .. StrokeWidth = 4.0.. style = PaintingStyle.stroke .. strokeCap = StrokeCap.round; @override void paint(Canvas canvas, Size size) { var path = createPath(sides.toInt(), 100); PathMetric pathMetric = path.computeMetrics().first; Path extractPath = pathMetric. ExtractPath (0.0, pathMetric. Length * progress);if (showPath) {
canvas.drawPath(extractPath, _paint);
}
if(showDots) { try { var metric = extractPath.computeMetrics().first; final offset = metric.getTangentForOffset(metric.length).position; Canvas. Methods like drawCircle (offset, 8.0, Paint ()); } catch (e) {} } } @override bool shouldRepaint(CustomPainter oldDelegate) {return true; } Path createPath(int sides, double radius) { var path = Path(); var angle = (math.pi * 2) / sides; Path.moveto (radius * math.cos(0.0), radius * math.sin(0.0));for (int i = 1; i <= sides; i++) {
double x = radius * math.cos(angle * i);
double y = radius * math.sin(angle * i);
path.lineTo(x, y);
}
path.close();
returnpath; }}Copy the code
Effect:
Five, spiral curve
A curve can be thought of as a movement of points, and it’s actually a little difficult to draw a spiral curve.
To achieve the curve effect, we can first move the center to the (x,y) coordinates, and then, for the next point, we increase the radius by 0.75 and the radian by 2 PI /50. For each additional point, the radius and Angle increase are small, so that what we see visually is a curve, not a straight line.
Path createSpiralPath(Size size) {
double radius = 0, angle = 0;
Path path = Path();
for(int n = 0; n < 200; N++) {radius += 0.75; angle += (math.pi * 2) / 50; var x = size.width / 2 + radius * math.cos(angle); var y = size.height / 2 + radius * math.sin(angle); path.lineTo(x, y); }return path;
}
Copy the code
The same animation needs to use pathMetric. The full code can be found here:
Github.com/divyanshub0…
Effect:
Six, super advanced
Finally show a planet rotation animation effect, complete code address:
Github.com/divyanshub0…
Effect:
Complete code sample
The last
Welcome to follow the wechat public account “Flutter Programming and Development”.