Pinkesh Darji I love to solve problems using technology that improves users' lives on a major scale. Over the last seven-plus years, I've been developing and leading various mobile apps in different areas.

Drawing shapes in Flutter with CustomPaint and Shape Maker

5 min read 1437

Flutter Custompaint Shape Maker

As a mobile developer, you may find yourself working on an app that demands highly customized user interactions. Your design team may come up with some wacky, never-before-seen UI/UX, and it’s your job to figure out how to execute it. Or let’s say you’re working on a pet project and you want to produce eye-catching visuals to engage your users. Imagine how frustrating it would be if you couldn’t access the assets required to do this, perhaps because the files are not in the proper format, they’re too expensive to add to your project, etc.

Don’t panic! Flutter has you covered.

Flutter’s CustomPaint widget enables you to pour your imagination onto the digital canvas. You can draw almost anything on the screen with this low-level painting API. It’s similar to drawing something on paper, but instead of a pencil and paper, you have an API and a canvas on which to draw.

In this tutorial, we’ll introduce you to CustomPaint, how to use it, what problems you may face while using it, and eventually the need of using the Flutter Shape Maker tool.

Here’s what we’ll cover:

How to use CustomPaint

CustomPaint is a widget in Flutter that generates a canvas on which to draw during the paint phase. The canvas has a coordinate system that matches the coordinate system of the CustomPaint object.

First, CustomPaint asks its painter to paint on the canvas. After it paints its child, the widget asks the foregroundPainter property to paint. The painters are restricted to a rectangular area that starts at the origin and encompasses a region of a specified size. If they venture outside this allotted space, there may not be enough memory to rasterize the painting commands.

The following video does a great job of breaking down how Flutter’s CustomPaint widget works:

To see Flutter CustomPaint in action, let’s try to draw a simple line:

Flutter CustomPaint Trial Simple Line Drawing

First, introduce the CustomPaint widget in your code. The basic code to draw a simple line looks like this:

// Step 1
class MyBookings extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Custom paint Demo'),
      ),
      body: Container(
        child: Center(
          child: CustomPaint(
            size: Size(300, 200),
            painter: LinePainter(),
          ),
        ),
      ),
    );
  }
}

CustomPaint needs at least two properties:

  1. painter, which paints before the children
  2. size, which specifies the canvas over which to draw the line

If you want the line to be drawn over any widget, such as container, stack, row, column, etc., replace size with child and painter with foregroundPainter:

CustomPaint(
  child: Container(
    width: 300,
    height: 200,
    color: Colors.amberAccent,
  ),
  foregroundPainter: LinePainter(),
)

Whatever child is given, the line will be drawn over it. foregroundPainter draws a line above the child.

The result looks like this:

CustomPaint Foregroundpainter Line Over Child Result

Now let’s create a class that extends CustomPainter. This class is responsible for drawing actual paintings. It has a paint and canvas class, which allows you to draw different shapes, such as a line, circle, rectangle, custom path, etc.

class LinePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    var paint = Paint()
      ..color = Colors.teal
      ..strokeWidth = 15;

    Offset start = Offset(0, size.height / 2);
    Offset end = Offset(size.width, size.height / 2);

    canvas.drawLine(start, end, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}

The Paint class is used to configure the painting properties, such the color and size of the pen. Apart from this, it offers a lot of other fun stuff you can play around with. The paint class is usually configured before painting, just like you would decide which color pen to draw on paper with.

The canvas class offers a wide variety of methods to actually start painting.

Drawing a circle in Flutter is also easy with CustomPaint. Canvas offers a drawCircle method, which, as the name suggests, draws a circle. The circle is centered at the point given by the first argument; the radius is given in the second argument.

Replace the LinePainter class in the above example with the below code to see a circle on the screen:

class CirclePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    var paint = Paint()
      ..color = Colors.teal
      ..strokeWidth = 15;

    Offset center = Offset(size.width / 2, size.height / 2);

    canvas.drawCircle(center, 100, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}

Custompaint Flutter CirclePainter Display

Drawing challenging shapes in Flutter

Drawing simples shapes, such as lines and circles, is easy. But let’s say you want to draw a curved shape, like this:

Flutter Curved Shape Drawing Display

Canvas provides a way to draw a path using the drawPath method. Here, the Path class is very useful in moving the pointer. The curve is drawn using the path.quadraticBezierTo(x1,y1,x2,y2) method, which draws a curve to x2,y2 using x1,y2 as control points:

class MyBookings extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Custom paint Demo'),
      ),
      body: Container(
        child: Center(
          child: CustomPaint(
            size: Size(400,400), 
            painter: CurvedPainter(),
          ),
        ),
      ),
    );
  }
}
class CurvedPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    var paint = Paint()
      ..color = Colors.teal
      ..strokeWidth = 15;

    var path = Path();

    path.moveTo(0, size.height * 0.7);
    path.quadraticBezierTo(size.width * 0.25, size.height * 0.7,
        size.width * 0.5, size.height * 0.8);
    path.quadraticBezierTo(size.width * 0.75, size.height * 0.9,
        size.width * 1.0, size.height * 0.8);
    path.lineTo(size.width, size.height);
    path.lineTo(0, size.height);

    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}

The real challenge here is identifying the control points while developing. If you’re developing a UI that has a lot of curves, determining the best place for a control point can be tricky and time-consuming.

Although Flutter provides a hot reload feature that allows you to iterate fast on design while developing, sometimes you need more to put the control points in the best place. Put simply, you need something that allows you to manage the curves with UI controls instead of code.

Flutter Shape Maker

Flutter Shape Maker by Paras Jain is the only toolset available today that enables you to manage curves with UI controls.

To configure Flutter Shape Maker to draw shapes, as in the real world, first select your canvas and then start drawing on it.



Shape size

Here we have an option to select our canvas size. Make sure you configure the size that best matches the aspect ratio of the final shape. For example, if your desired shape is a rectangle, then you must set the size as something like 800(W) * 400(H).

Select Canvas Size Flutter Shape Maker Tutorial

The responsive code option will be on by default. It’s always good practice to have responsive code to make your shape ratio consistent across all the devices.

Pen tool

The pen tool allows you to plot points and create the desired shape on the canvas. Snap points to the grid help you create accurate drawings.

Pen Tool Plot Points Flutter Shapemaker

Layers

A layer is simply a stack of various shapes. This is super helpful for creating complex scenes composed of multiple shapes. You can add, delete, hide, and show any layer to manage one shape at a time:

Layer Stacking Example Flutter Shape Maker

Move

This enables you to freely move the whole shape and do some final adjustment in the position of the shape.

Move Tool Adjustment Example Flutter Shape Maker

Get code

Get code is the unique selling point of Flutter Shape Maker. The custom paint code is now just one click away. Click on it and you’ll get the responsive version of the code that is ready to be included directly in your code:

Get Code Custom Paint Code Output Example

Creating a curve

With Flutter Shape Maker, creating a curve is a piece of cake. Just select any point and click H to enable control handles to create the curve:

Create Curve Flutter Shape Maker Example

As you can see, the ease of creating and modifying a curve makes all the difference here.

Building a complex UI in Flutter

To see Flutter Shape Maker in action, let’s quickly whip up a fairly complex ticket UI. The finished result will look like this:

Complex Ticket UI Finished Result

First, creating a bottom layer:

Flutter Shape Maker Ticket Bottom Layer Visual

Then, create an upper layer:

Flutter Shape Maker Upper Layer Visual

By repeating the same process for the right-hand side using layers and carefully managing the curves with control handles, you can achieve a complicated-looking UI in a very short amount of time.

You can view the complete source code for this ticket UI.

Conclusion

In this tutorial, we introduced you to Flutter’s CustomPaint widget and demonstrated how to create and manage complex shapes using Flutter Shape Maker. We then walked you through how to create a relatively complex UI using these tools in an example Flutter app.

With these skills in your toolbox, the shapes you can create with Flutter are limited only by your imagination.

Get set up with LogRocket's modern error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID
  2. Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, not server-side
  3. $ npm i --save logrocket 

    // Code:

    import LogRocket from 'logrocket';
    LogRocket.init('app/id');
    Add to your HTML:

    <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
    <script>window.LogRocket && window.LogRocket.init('app/id');</script>
  4. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • NgRx middleware
    • Vuex plugin
Get started now
Pinkesh Darji I love to solve problems using technology that improves users' lives on a major scale. Over the last seven-plus years, I've been developing and leading various mobile apps in different areas.

Leave a Reply