Flutter offers a huge amount of widgets for interface building, but you’re still not satisfied. You want to create a unique, unique widget that you have to come to CustomPaint. It will help you do what you want.
CustomPaint gives us access to low-level – graphics when Flutter draws a widget. It is like custom views on Android or iOS but will be simpler and more intuitive when combined with Flutter’s HotReload.
In this article I will introduce the use of CustomPaint to draw simple shapes.
CustomPaint
Declare to use 1 CustomPaint
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <span class="token keyword">class</span> <span class="token class-name">MyPainter</span> <span class="token keyword">extends</span> <span class="token class-name">StatelessWidget</span> <span class="token punctuation">{</span> <span class="token metadata symbol">@override</span> Widget <span class="token function">build</span> <span class="token punctuation">(</span> BuildContext context <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">Scaffold</span> <span class="token punctuation">(</span> appBar <span class="token punctuation">:</span> <span class="token function">AppBar</span> <span class="token punctuation">(</span> title <span class="token punctuation">:</span> <span class="token function">Text</span> <span class="token punctuation">(</span> <span class="token string">'Lines'</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> body <span class="token punctuation">:</span> <span class="token function">CustomPaint</span> <span class="token punctuation">(</span> painter <span class="token punctuation">:</span> <span class="token function">ShapePainter</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> child <span class="token punctuation">:</span> <span class="token function">Container</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Some important properties of CustomPaint:
- child : By default, the canvas takes the size of the child, if it is specified.
- painter : A painter was drawing before the child was drawn.
- foregroundPainter : A painter was drawn after the child was drawn (over them).
- size : If the child is not declared, the size of the canvas must be specified.
In the scope of this article, we will only need two properties, paint and child . In the above code, I have declared child the Container , as you know if there is no child container it will take up all the space it is allowed to have.
Now we need to define the ShapePainter widget that will extend the CustomPainter class
CustomPainter
CustomPainter is an abtract class so ShapePainter has to override two methods:
- paint : This method is called whenever the object needs to be redrawn.
- shouldRepaint : This method is called when a new instance of the class is provided.
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">class</span> <span class="token class-name">ShapePainter</span> <span class="token keyword">extends</span> <span class="token class-name">CustomPainter</span> <span class="token punctuation">{</span> <span class="token metadata symbol">@override</span> <span class="token keyword">void</span> <span class="token function">paint</span> <span class="token punctuation">(</span> Canvas canvas <span class="token punctuation">,</span> Size size <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// TODO: implement paint</span> <span class="token punctuation">}</span> <span class="token metadata symbol">@override</span> bool <span class="token function">shouldRepaint</span> <span class="token punctuation">(</span> CustomPainter oldDelegate <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// TODO: implement shouldRepaint</span> <span class="token keyword">return</span> <span class="token keyword">null</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
The paint method has two parameters: canvas, size. If CustomPaint has a child, the canvas will be the same size as the child. In this case the cavas will be the size of the entire screen – the space portion of the AppBar.
Canvas Area
Before we draw anything on the canvas, we need to understand its coordinate system. Canvas has the coordinate system as shown below:
You can see that the origin – origin (0, 0) is in the upper left corner of the canvas. Every stroke will be linked to the origin – origin, where the painter started.
Draw Line
Now, I will draw a horizontal line in the middle of the screen (as if it were dividing the screen into two halves vertically).
To draw a line he needs two points, so that when connecting those two points, we will have the desired line.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <span class="token comment">// FOR PAINTING LINES</span> <span class="token keyword">class</span> <span class="token class-name">ShapePainter</span> <span class="token keyword">extends</span> <span class="token class-name">CustomPainter</span> <span class="token punctuation">{</span> <span class="token metadata symbol">@override</span> <span class="token keyword">void</span> <span class="token function">paint</span> <span class="token punctuation">(</span> Canvas canvas <span class="token punctuation">,</span> Size size <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> paint <span class="token operator">=</span> <span class="token function">Paint</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> color <span class="token operator">=</span> Colors <span class="token punctuation">.</span> teal <span class="token punctuation">.</span> <span class="token punctuation">.</span> strokeWidth <span class="token operator">=</span> <span class="token number">5</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> strokeCap <span class="token operator">=</span> StrokeCap <span class="token punctuation">.</span> round <span class="token punctuation">;</span> Offset startingPoint <span class="token operator">=</span> <span class="token function">Offset</span> <span class="token punctuation">(</span> <span class="token number">0</span> <span class="token punctuation">,</span> size <span class="token punctuation">.</span> height <span class="token operator">/</span> <span class="token number">2</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> Offset endingPoint <span class="token operator">=</span> <span class="token function">Offset</span> <span class="token punctuation">(</span> size <span class="token punctuation">.</span> width <span class="token punctuation">,</span> size <span class="token punctuation">.</span> height <span class="token operator">/</span> <span class="token number">2</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> canvas <span class="token punctuation">.</span> <span class="token function">drawLine</span> <span class="token punctuation">(</span> startingPoint <span class="token punctuation">,</span> endingPoint <span class="token punctuation">,</span> paint <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token metadata symbol">@override</span> bool <span class="token function">shouldRepaint</span> <span class="token punctuation">(</span> CustomPainter oldDelegate <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token boolean">false</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Variable paint – an instance of Paint, it looks like a brush and helps to specify the color, stroke, stickiness of the stroke, etc.
Two Offset variables specify the starting and ending position coordinates.
The drawLine method is called on the canvas to draw a line between the two Offset positions and the paint variable is also passed for this method.
The shouldRepaint return false method because there is no need to redraw the line.
- Another way to draw a line is to use paths
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <span class="token comment">// FOR PAINTING LINES</span> <span class="token keyword">class</span> <span class="token class-name">ShapePainter</span> <span class="token keyword">extends</span> <span class="token class-name">CustomPainter</span> <span class="token punctuation">{</span> <span class="token metadata symbol">@override</span> <span class="token keyword">void</span> <span class="token function">paint</span> <span class="token punctuation">(</span> Canvas canvas <span class="token punctuation">,</span> Size size <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> paint <span class="token operator">=</span> <span class="token function">Paint</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> color <span class="token operator">=</span> Colors <span class="token punctuation">.</span> teal <span class="token punctuation">.</span> <span class="token punctuation">.</span> strokeWidth <span class="token operator">=</span> <span class="token number">5</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> style <span class="token operator">=</span> PaintingStyle <span class="token punctuation">.</span> stroke <span class="token punctuation">.</span> <span class="token punctuation">.</span> strokeCap <span class="token operator">=</span> StrokeCap <span class="token punctuation">.</span> round <span class="token punctuation">;</span> <span class="token keyword">var</span> path <span class="token operator">=</span> <span class="token function">Path</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> path <span class="token punctuation">.</span> <span class="token function">moveTo</span> <span class="token punctuation">(</span> <span class="token number">0</span> <span class="token punctuation">,</span> size <span class="token punctuation">.</span> height <span class="token operator">/</span> <span class="token number">2</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> path <span class="token punctuation">.</span> <span class="token function">lineTo</span> <span class="token punctuation">(</span> size <span class="token punctuation">.</span> width <span class="token punctuation">,</span> size <span class="token punctuation">.</span> height <span class="token operator">/</span> <span class="token number">2</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> canvas <span class="token punctuation">.</span> <span class="token function">drawPath</span> <span class="token punctuation">(</span> path <span class="token punctuation">,</span> paint <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token metadata symbol">@override</span> bool <span class="token function">shouldRepaint</span> <span class="token punctuation">(</span> CustomPainter oldDelegate <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token boolean">false</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
When using paths, you will need to specify another property for the paint variable, style (here is PaintingStyle.stroke). If you do not specify this property, the line will not be displayed.
- moveTo: is used to change the current position of the point to the specified coordinate.
- lineTo: used to draw a line from current point to specified point on canvas.
- drawPath: called on the canvas to draw the path on the screen
Code: https://dartpad.dev/0f1e8a36c2a716f2cb46ca853df54749 ?
Draw Circle
You can draw a simple circle centered (size.width / 2, size.height / 2), i.e. at the center of the Container, by calling the canvas’s drawCircle method or by using Path.
- drawCircle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <span class="token comment">// FOR PAINTING CIRCLES</span> <span class="token keyword">class</span> <span class="token class-name">ShapePainter</span> <span class="token keyword">extends</span> <span class="token class-name">CustomPainter</span> <span class="token punctuation">{</span> <span class="token metadata symbol">@override</span> <span class="token keyword">void</span> <span class="token function">paint</span> <span class="token punctuation">(</span> Canvas canvas <span class="token punctuation">,</span> Size size <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> paint <span class="token operator">=</span> <span class="token function">Paint</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> color <span class="token operator">=</span> Colors <span class="token punctuation">.</span> teal <span class="token punctuation">.</span> <span class="token punctuation">.</span> strokeWidth <span class="token operator">=</span> <span class="token number">5</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> style <span class="token operator">=</span> PaintingStyle <span class="token punctuation">.</span> stroke <span class="token punctuation">.</span> <span class="token punctuation">.</span> strokeCap <span class="token operator">=</span> StrokeCap <span class="token punctuation">.</span> round <span class="token punctuation">;</span> Offset center <span class="token operator">=</span> <span class="token function">Offset</span> <span class="token punctuation">(</span> size <span class="token punctuation">.</span> width <span class="token operator">/</span> <span class="token number">2</span> <span class="token punctuation">,</span> size <span class="token punctuation">.</span> height <span class="token operator">/</span> <span class="token number">2</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> canvas <span class="token punctuation">.</span> <span class="token function">drawCircle</span> <span class="token punctuation">(</span> center <span class="token punctuation">,</span> <span class="token number">100</span> <span class="token punctuation">,</span> paint <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token metadata symbol">@override</span> bool <span class="token function">shouldRepaint</span> <span class="token punctuation">(</span> CustomPainter oldDelegate <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token boolean">false</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
drawCircle needs an Offset which is the center coordinates of the circle, the radius of the circle and paint
- addOval () of Path
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <span class="token comment">// FOR PAINTING CIRCLES</span> <span class="token keyword">class</span> <span class="token class-name">ShapePainter</span> <span class="token keyword">extends</span> <span class="token class-name">CustomPainter</span> <span class="token punctuation">{</span> <span class="token metadata symbol">@override</span> <span class="token keyword">void</span> <span class="token function">paint</span> <span class="token punctuation">(</span> Canvas canvas <span class="token punctuation">,</span> Size size <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> paint <span class="token operator">=</span> <span class="token function">Paint</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> color <span class="token operator">=</span> Colors <span class="token punctuation">.</span> teal <span class="token punctuation">.</span> <span class="token punctuation">.</span> strokeWidth <span class="token operator">=</span> <span class="token number">5</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> style <span class="token operator">=</span> PaintingStyle <span class="token punctuation">.</span> stroke <span class="token punctuation">.</span> <span class="token punctuation">.</span> strokeCap <span class="token operator">=</span> StrokeCap <span class="token punctuation">.</span> round <span class="token punctuation">;</span> <span class="token keyword">var</span> path <span class="token operator">=</span> <span class="token function">Path</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> path <span class="token punctuation">.</span> <span class="token function">addOval</span> <span class="token punctuation">(</span> Rect <span class="token punctuation">.</span> <span class="token function">fromCircle</span> <span class="token punctuation">(</span> center <span class="token punctuation">:</span> <span class="token function">Offset</span> <span class="token punctuation">(</span> size <span class="token punctuation">.</span> width <span class="token operator">/</span> <span class="token number">2</span> <span class="token punctuation">,</span> size <span class="token punctuation">.</span> height <span class="token operator">/</span> <span class="token number">2</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> radius <span class="token punctuation">:</span> <span class="token number">100</span> <span class="token punctuation">,</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> canvas <span class="token punctuation">.</span> <span class="token function">drawPath</span> <span class="token punctuation">(</span> path <span class="token punctuation">,</span> paint <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token metadata symbol">@override</span> bool <span class="token function">shouldRepaint</span> <span class="token punctuation">(</span> CustomPainter oldDelegate <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token boolean">false</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
path.addOval : This method is used here to draw a circle with the center center of the screen and with radius = 100 pixels.
Code: https://dartpad.dev/8e49a84dd72a64971a377478f99a54ec ?
To be continued …
The article is referenced here