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.

4.4 (10) · 4 Reviews

Download materials
Save for later

Drawing the UI is one of the core features of every visual app framework. In this tutorial, you’ll build a drawing app in Flutter to explore Flutter’s capability to render and control a custom user interface. You’ll learn to control every pixel on the screen with the help of CustomPaint widgets.

Here’s a preview of the app you’ll build:

Drawing App Final Preview

To build this drawing app, you’ll harness the rendering powers and the pixel-level control that Flutter offers. It allows you to control each pixel on the screen — you can render anything you want, any where you want.

While building this app, you’ll learn about:

  • Using the CustomPaint widget to draw UI pixel-by-pixel
  • The drawing basics and using a Canvas
  • Detecting user input using GestureDetector and drawing a path
  • Drawing multiple paths on the screen
  • Changing painter colors and stroke widths
  • Clearing and saving your drawings
Note: This article assumes that you know the basics of the Flutter framework, including common widgets and their properties. If you want to learn how to draw basic shapes in Flutter, have a look at Drawing Custom Shapes with CustomPaint in Flutter tutorial. To learn more about Flutter from scratch, check out the Flutter Apprentice book.

Getting Started

Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial.

Unzip the downloaded file and open it with Android Studio 4.1 or later. You can use Visual Studio Code instead, but if you do, you’ll need to tweak some instructions to follow along.

Click Open an existing Android Studio project and choose the starter folder from your unzipped download.

Run the flutter create . command in the starter folder to generate the android and ios folders. Next, download your dependencies by double-clicking pubspec.yaml on the left panel, then clicking pub get at the top of your screen. To avoid problems, delete the test folder, which Flutter generated when you executed the flutter create . command.

Finally, build and run to see this:

Starter project on iOS Simulator

Here are a few files you’ll see in the starter project’s lib folder.

  • main.dart: This is the main file that acts as the entry point for the app. It contains a MyApp that contains MaterialApp. This widget uses DrawingPage as the child.
  • drawing_page.dart: This file contains most of the code. It renders the widgets that you saw in the app preview. The drawing area, the save and clear buttons, the color toolbar and the stroke toolbar are all rendered inside DrawingPage.
  • drawn_line.dart: This file is a simple model class DrawnLine for a typical path drawn on the screen. Each path that is drawn contains a List of points on the screen (List<Offset>), a color (Color) and a stroke width (double). You’ll use this class when drawing paths. “Path” might be a more relevent name for this class, but since it’s already a Flutter class, you use the name DrawnLine. Feel free to rename it, but be sure to change the rest of the code accordingly.
  • sketcher.dart: Sketcher is a class that extends CustomPainter and is used in combination with the CustomPaint to draw on the screen. This class contains the logic to draw DrawnLines on the screen.
  • main_learning.dart: You’ll use this file to show the basics of drawing simple shapes on the Canvas.

Here’s what your app’s user interface (UI) will look like:

Final app UI description

  • Drawing Space: This is the light yellow space where the user will draw with their finger.
  • Color Toolbar: This allows the user to change the currently selected color to one of seven colors.
  • New/Clear Button: This button allows the user to clear the canvas and start afresh.
  • Save Button: This lets the user save whatever is currently drawn on the screen as an image on the device. The saved image appears in the phone’s Gallery (on Android) and Photos (on iOS).
  • Stroke Toolbar: This contains buttons that allow the user to change the current stroke width to a specific size. There are three stroke sizes — small, medium and large.
Note: Even though the component is called ColorToolbar, it also contains the New/Clear buttons.

Introducing Flutter Canvas and CustomPaint

Flutter’s Canvas is an interface for recording graphical operations. It can be used to draw shapes, images, texts and nearly everything else on the screen with pixel precision. To create and access the Canvas, you’ll use a widget called CustomPaint. You’ll also use a painter parameter that contains all the painting logic and extends CustomPainter.

Using the CustomPaint Widget

Here’s a code snippet that includes a Container and has a CustomPaint as its child:

class MyWidget extends StatelessWidget {
  Widget build(BuildContext context) {
    return Container(
      color: Colors.yellow[100],
      child: CustomPaint(
        painter: MyCustomPainter(),

In the code snippet above, you create a widget called MyWidget that renders a Container. The Container contains a CustomPaint as its child. Have a quick look at the most simple implementation of MyCustomPainter:

// 1
class MyCustomPainter extends CustomPainter {
  // 2
  void paint(Canvas canvas, Size size) {

  // 4
  bool shouldRepaint(MyCustomPainter delegate) {
    return true;

Here’s what’s going on in the code snippet above:

  1. You create the MyCustomPainter, which extends CustomPainter. Once you create the class with this signature, your IDE will detect errors and suggest you implement the two missing methods – paint() and shouldRepaint().
  2. paint() contains all the drawing and painting logic. It receives two arguments: Canvas and Size. You’ll use both of these while writing the drawing code. All the drawing code goes within the body of this method, where you’ll gain access to various methods like drawLine(), drawArc(), drawPath(), drawPoints(), drawRect() and many more. To find all available methods, see the official Flutter documentation. The Size parameter allows you to draw considering the size of the canvas.
  3. shouldRepaint() is an optimization method that’s called whenever you create a new CustomPaint. If the new instance represents different information than the old one, the method returns true. Otherwise, it returns false.

The Sketcher (in sketcher.dart) available in the starter project is a similar class that extends CustomPainter.