Building a Drawing App in Flutter
Learn how to create a drawing app in Flutter and explore Flutter’s capability to render and control a custom UI with the help of CustomPaint widgets. By Samarth Agarwal.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Building a Drawing App in Flutter
35 mins
- Getting Started
- Introducing Flutter Canvas and CustomPaint
- Using the CustomPaint Widget
- Understanding Canvas Basics
- Drawing Paths
- Changing the Stroke, Color and Width
- Diving Into Code
- Using GestureDetector
- Drawing a Single Path
- Drawing Multiple Paths
- Adding Stroke Color and Width
- Changing Stroke Color
- Changing Stroke Width
- Optimizing Your App
- Drawing Multiple Lines
- Using StreamBuilders and Two CustomPaint widgets
- Saving the Drawing
- Creating New and Save Buttons
- Using the Plugin
- Where to Go From Here
Understanding Canvas Basics
The Canvas
is available in paint()
of the CustomPainter
‘s subclass. It’s used to draw on the canvas, which has certain dimensions. Those are also available to you inside paint()
as the second argument, size
. The size of the parent widget limits the size of the canvas.
In the starter project, the whole screen will be occupied by CustomPaint
, so the size of the canvas is the same as the size of the screen of the device the app is running on.
Within a canvas, you specify each position on the screen — or rather, each point
— with the help of coordinates. Here’s what the coordinates look like for an iPhone X that has a width of 375.0 px and a height of 812.0 px.
Here’s an explaination of the image above:
- The top-left corner has the coordinates (0,0), which means the coordinates along the x-axis and the y-axis are both 0.
- The top-right corner has the coordinates (375,0). This means the coordinate along the x-axis is 375 but along the along the y-axis is still 0.
- The bottom-left corner has the coordinates (0,812), which means the coordinate along the x-axis is 0 but along the y-axis is 812.
- The bottom-right corner has the coordinates (375,812). This means the coordinate along the x-axis is 375 and along the y-axis is 812.
- The center of the screen has the coordinates (187.5,406).
By using coordinates, you can draw various shapes on the screen. Consider a simple implementation of paint()
that draws a line on the canvas. If you want to try it by yourself, replace the functionality of paint()
located in lib/main_learning.dart with the code below:
@override
void paint(Canvas canvas, Size size) {
// 1
Offset startPoint = Offset(0, 0);
// 2
Offset endPoint = Offset(size.width, size.height);
// 3
Paint paint = Paint();
// 4
canvas.drawLine(startPoint, endPoint, paint);
}
Here’s what’s happening in the code snippet above:
- You create an
Offset startPoint
and use the top-left coordinates of the screen, which will always be (0,0). - Next, you create another
Offset endPoint
and use the bottom-right coordinates of the screen. Since you do not know the size of the screen, you usesize
‘swidth
andheight
properties. - As you have the
startPoint
and theendPoint
ready, you need aPaint
, which specifies cosmetic properties of a drawn line. For now, you do nothing special here. - Finally, you use
drawLine
to draw a line fromstartPoint
toendPoint
using the specifiedPaint
.
If you replaced the code in the paint
, save the changes and run by writing command flutter run -t lib/main_learning.dart in the terminal. You’ll see a thin line drawn from the top-left to the bottom-right of the screen.
Drawing Paths
Paths in Flutter are a way to draw arbitrary shapes on the screen. It’s as simple as creating a path and then using methods like lineTo()
, moveTo()
, addOval()
, addArc()
, addPolygon()
etc., to get the desired shape on the canvas. Have a look at another fun implementation of paint()
— but this time, draw a Path
. Replace the functionality of paint()
, located in lib/main_learning.dart, with the code below:
void paint(Canvas canvas, Size size) {
// 1
Paint paint = Paint()..style = PaintingStyle.stroke;
// 2
Path path = Path();
// 3
path.moveTo(0, 250);
path.lineTo(100, 200);
path.lineTo(150, 150);
path.lineTo(200, 50);
path.lineTo(250, 150);
path.lineTo(300, 200);
path.lineTo(size.width, 250);
path.lineTo(0, 250);
// 4
path.moveTo(100, 100);
path.addOval(Rect.fromCircle(center: Offset(100, 100), radius: 25));
// 5
canvas.drawPath(path, paint);
}
Here’s what’s happening in the code snippet above:
Here’s an explanation of all the points used in drawing the path:
- You create a
Paint
and set thePaintingStyle
toPaintingStyle.stroke
. This is because you only want to draw the paths and not fill the enclosed spaces with color. - Next, you create a new
Path
using the default constructor. - Finally, you use
moveTo()
andlineTo()
to draw the path. It’s like moving a pen on the canvas from point to point — you create lines from one point to another. These lines will move in a zig-zag manner across the screen horizontally giving an impression of mountains.Here’s an explanation of all the points used in drawing the path:
- Then you move the current drawing position to (100,100). This is where you want to draw a circle — the sun behind the mountains. Use
moveTo()
instead oflineTo()
because while moving to (100,100), you do not want to draw a line. To create a circle with a radius of 25, useaddOval()
. - Finally, call
drawPath()
and pass in thepath
and thepaint
to draw the path.
moveTo()
only changes the current position, but lineTo()
changes the current position and draws a line.
Save the changes and hot reload. Here’s what you’ll see on the screen:
Changing the Stroke, Color and Width
So far, everything you drew on the canvas uses a very thin black stroke. You’ll change that by configuring Paint
. Building on the previous example of mountains and sun, you’ll create two Paint
s: one for the sun, and the other for the mountains.
Change the paint()
code to the following:
@override
void paint(Canvas canvas, Size size) {
// 1
Paint paintMountains = Paint()
..style = PaintingStyle.fill
..color = Colors.brown;
// 2
Paint paintSun = Paint()
..style = PaintingStyle.fill
..color = Colors.deepOrangeAccent;
// 3
Path path = Path();
path.moveTo(0, 250);
path.lineTo(100, 200);
path.lineTo(150, 150);
path.lineTo(200, 50);
path.lineTo(250, 150);
path.lineTo(300, 200);
path.lineTo(size.width, 250);
path.lineTo(0, 250);
canvas.drawPath(path, paintMountains);
// 4
path = Path();
path.moveTo(100, 100);
path.addOval(Rect.fromCircle(center: Offset(100, 100), radius: 25));
canvas.drawPath(path, paintSun);
}
Take a look at what this code does:
- By creating
paintMountains
, you set its color to brown and style toPaintingStyle.fill
. -
sunMountains
‘s color is set to deepOrangeAccent and style toPaintingStyle.fill
. - When drawing the path of the mountains, you use the
paintMountains
paint, and so the mountain shape is filled with the brown color. - After resetting the path, you draw the path of the sun using the
paintSun
paint, which is why the sun shape is filled with the orange color.
Save the changes and hot restart. Here’s what the final output will look like:
Isn’t it beautiful? And so simple to implement! :]