Skip to main content

Implementing Custom Painting

1. Using CustomPaint Widget

Example: Basic CustomPaint Usage

import 'package:flutter/material.dart';

class CustomPaintExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("CustomPaint Example")),
      body: Center(
        child: CustomPaint(
          size: Size(200, 200),
          painter: MyPainter(),
        ),
      ),
    );
  }
}

class MyPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..color = Colors.blue
      ..style = PaintingStyle.fill;
    canvas.drawRect(
      Rect.fromLTWH(0, 0, size.width, size.height),
      paint,
    );
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}
Property Description Values
size Defines the size of the painting area Size
painter Instance of CustomPainter to perform the drawing CustomPainter
isComplex Indicates if the painting is complex and requires additional optimizations bool
willChange Indicates if the painter will change in the next frame bool
Note: The CustomPaint widget is a powerful tool for creating custom graphics in Flutter. It allows you to draw directly on the canvas using the Canvas API.
Warning: Ensure that shouldRepaint returns false if the painter does not change to avoid unnecessary repaints.

2. Creating CustomPainter Class

Property Description Values
paint Method to implement custom drawing logic @override void paint(Canvas canvas, Size size)
shouldRepaint Determines if the painter should repaint when the widget rebuilds @override bool shouldRepaint(CustomPainter oldDelegate)
shouldRebuild Used for asynchronous painting operations (Flutter 3.0+) @override bool shouldRebuild(SemanticsHandle oldDelegate)

Example: Basic CustomPainter Implementation

class MyCustomPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..color = Colors.blue
      ..style = PaintingStyle.fill;

    canvas.drawRect(
      Rect.fromLTWH(0, 0, size.width, size.height),
      paint,
    );
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}
Note: The paint method is called every time the widget needs to be redrawn. The shouldRepaint method controls whether the painter should repaint when the widget rebuilds.

3. Implementing paint() Method

Parameter Type Description
canvas Canvas The canvas to draw on.
paint Paint The paint to use for drawing.

Example: Basic Custom Painting

class CustomPainterExample extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()
      ..color = Colors.blue
      ..style = PaintingStyle.fill;

    canvas.drawRect(
      Rect.fromLTWH(0, 0, size.width, size.height),
      paint,
    );
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}
Note: The paint() method is called whenever the widget needs to be redrawn.
Warning: Avoid heavy computations inside paint() to maintain performance.

4. Using Canvas Drawing Methods

Method Description Parameters Example
drawRect Draws a rectangle with specified bounds and optional paint. Rect rect, Paint paint
canvas.drawRect(
  Rect.fromLTWH(10, 10, 100, 50),
  Paint()..color = Colors.blue,
);
drawCircle Draws a circle with center and radius using specified paint. Offset center, double radius, Paint paint
canvas.drawCircle(
  Offset(50, 50),
  30,
  Paint()..color = Colors.red,
);
drawOval Draws an oval (ellipse) within a rectangle using specified paint. Rect rect, Paint paint
canvas.drawOval(
  Rect.fromLTWH(20, 20, 100, 60),
  Paint()..color = Colors.green,
);
drawLine Draws a line between two points with specified paint. Offset start, Offset end, Paint paint
canvas.drawLine(
  Offset(0, 0),
  Offset(100, 100),
  Paint()..color = Colors.black..strokeWidth = 2,
);
drawPath Draws a path using specified paint. Path path, Paint paint
Path path = Path()
  ..moveTo(0, 0)
  ..lineTo(50, 50)
  ..lineTo(100, 0);
canvas.drawPath(path, Paint()..color = Colors.purple);
drawPoints Draws multiple points using specified paint and point style. PointMode mode, List<Offset> points, Paint paint
canvas.drawPoints(
  PointMode.points,
  [Offset(10, 10), Offset(30, 30), Offset(50, 50)],
  Paint()..color = Colors.orange,
);
drawImage Draws an image at a specified offset using specified paint. ui.Image image, Offset offset, Paint paint
canvas.drawImage(
  image,
  Offset(0, 0),
  Paint(),
);
drawText Draws text at a specified offset using a TextPainter. TextPainter painter, Offset offset
TextPainter painter = TextPainter(
  text: TextSpan(text: 'Hello', style: TextStyle(color: Colors.black)),
  textDirection: TextDirection.ltr,
);
painter.layout();
canvas.drawText(painter, Offset(0, 0));

5. Using Paint Class

Property Values Description
color Color(0xFF000000) Defines the color used for drawing operations
strokeWidth 1.0 Defines the width of the stroke for outlines
style PaintingStyle.fill Determines whether to fill or stroke the shape
strokeCap StrokeCap.butt Defines the shape of the stroke ends
shader null Applies a gradient or texture to the paint

Example: Basic Paint Usage

class CustomPainterExample extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()
      ..color = Colors.blue
      ..strokeWidth = 2.0
      ..style = PaintingStyle.stroke;

    canvas.drawCircle(
      Offset(size.width / 2, size.height / 2),
      50,
      paint,
    );
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

Example: Using Shader

class GradientPaintExample extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()
      ..shader = LinearGradient(
        begin: Alignment.topLeft,
        end: Alignment.bottomRight,
        colors: [Colors.red, Colors.blue],
      ).createShader(Rect.fromLTWH(0, 0, size.width, size.height));

    canvas.drawRect(
      Rect.fromLTWH(0, 0, size.width, size.height),
      paint,
    );
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

6. Drawing Paths

Flutter provides the CustomPainter class to enable custom drawing. The Canvas class offers methods for drawing paths, which are collections of lines and curves.

Method Description Example
drawPath Draws a Path on the canvas. canvas.drawPath(path, paint);
drawPoints Draws a list of points using the specified style. canvas.drawPoints(PointMode.points, points, paint);
drawVertices Draws a set of vertices defined by a Vertices object. canvas.drawVertices(vertices, BlendMode.src);

Example: Drawing a Simple Path

class SimplePathPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final Path path = Path();
    path.moveTo(50, 50);
    path.lineTo(150, 50);
    path.lineTo(100, 150);
    path.close();

    final Paint paint = Paint()
      ..color = Colors.blue
      ..style = PaintingStyle.fill;

    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

Example: Drawing a Curved Path

class CurvedPathPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final Path path = Path();
    path.moveTo(50, 100);
    path.quadraticBezierTo(100, 50, 150, 100);
    path.quadraticBezierTo(200, 150, 250, 100);

    final Paint paint = Paint()
      ..color = Colors.red
      ..strokeWidth = 3
      ..style = PaintingStyle.stroke;

    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

7. Implementing shouldRepaint() Method

The shouldRepaint() method is a crucial part of custom painting in Flutter, allowing you to control when a CustomPainter should redraw itself. It's particularly important for performance optimization.

Method Description Return Type Notes
shouldRepaint() Override this method to control whether the painter should repaint when a new instance is provided to the same CustomPaint. bool Return true to repaint, false to skip.

Example: Basic shouldRepaint() Implementation

class CustomPainterExample extends CustomPainter {
  final Color color;

  CustomPainterExample({required this.color});

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()..color = color;
    canvas.drawRect(
      Rect.fromLTWH(0, 0, size.width, size.height),
      paint,
    );
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    // Only repaint if the color has changed
    return oldDelegate is CustomPainterExample &&
        oldDelegate.color != color;
  }
}
Note: If shouldRepaint() returns false, the painter will not be repainted even if the widget rebuilds, which can significantly improve performance.

8. Drawing Text on Canvas

Property Description Example
text The text to be drawn on the canvas "Hello Flutter"
offset Offset from the origin to draw the text Offset(10.0, 20.0)
style Style to apply to the text (font, size, color) TextStyle(fontSize: 20.0, color: Colors.blue)

Example: Drawing Text on Canvas

class TextPainterWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: TextPainterExample(),
      size: Size(300, 200),
    );
  }
}

class TextPainterExample extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final text = 'Hello Flutter';
    final offset = Offset(50, 100);
    final style = TextStyle(
      fontSize: 24,
      color: Colors.black,
      fontFamily: 'Arial',
    );
    final textPainter = TextPainter(
      text: TextSpan(text: text, style: style),
      textDirection: TextDirection.ltr,
    );
    textPainter.layout();
    textPainter.paint(canvas, offset);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}
Note: The TextPainter is used to measure and lay out text before painting it on the canvas.

9. Creating Custom Shapes and Graphics

Example: Drawing a Custom Shape

import &#x27;package:flutter/material.dart&#x27;;

class CustomShapeWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      size: Size(200, 200),
      painter: MyCustomPainter(),
    );
  }
}

class MyCustomPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()
      ..color = Colors.blue
      ..style = PaintingStyle.fill;

    canvas.drawCircle(
      Offset(size.width / 2, size.height / 2),
      50,
      paint,
    );
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}
Property Description Values
size Defines the size of the painting area Size
painter Instance of CustomPainter to use for drawing CustomPainter
Note: The CustomPaint widget is used to draw custom graphics on a canvas.

Example: Drawing Multiple Shapes

class MultiShapePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()
      ..color = Colors.red
      ..style = PaintingStyle.fill;

    // Draw a rectangle
    canvas.drawRect(
      Rect.fromLTWH(10, 10, 100, 50),
      paint,
    );

    // Draw a circle
    canvas.drawCircle(
      Offset(150, 50),
      30,
      paint..color = Colors.green,
    );

    // Draw a line
    canvas.drawLine(
      Offset(10, 100),
      Offset(150, 100),
      paint..color = Colors.blue,
    );
  }

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

Key Concepts in Custom Painting

  • Canvas is the drawing surface provided by Flutter.
  • CustomPainter is the base class for custom drawing logic.
  • Paint controls how shapes are drawn (color, style, etc.).

10. Using Clipping

Clipping in Flutter allows you to restrict the rendering of a widget to a specific area, effectively cutting off any content that extends beyond the defined bounds.

Property Description Values
clipBehavior Determines how the widget's content is clipped. Clip.none, Clip.hardEdge, Clip.antiAlias, Clip.antiAliasWithSaveLayer
clipRect Defines a rectangular clipping area. Rect object specifying the bounds.
clipRRect Defines a rounded rectangular clipping area. RRect object specifying the bounds and radii.

Example: Using ClipRect

Container(
  width: 200,
  height: 200,
  child: ClipRect(
    clipRect: Rect.fromLTWH(0, 0, 100, 100),
    child: Container(
      color: Colors.blue,
      child: Text('Clipped Content'),
    ),
  ),
)

Example: Using ClipRRect

Container(
  width: 200,
  height: 200,
  child: ClipRRect(
    borderRadius: BorderRadius.circular(20),
    child: Container(
      color: Colors.green,
      child: Text('Rounded Clip'),
    ),
  ),
)
Note: Clip.antiAlias and Clip.antiAliasWithSaveLayer provide smoother edges but may impact performance slightly.
Warning: Using Clip.antiAliasWithSaveLayer can cause performance issues in complex UIs due to the creation of a save layer.

Was this article helpful?