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 |
|
| drawCircle | Draws a circle with center and radius using specified paint. | Offset center, double radius, Paint paint |
|
| drawOval | Draws an oval (ellipse) within a rectangle using specified paint. | Rect rect, Paint paint |
|
| drawLine | Draws a line between two points with specified paint. | Offset start, Offset end, Paint paint |
|
| drawPath | Draws a path using specified paint. | Path path, Paint paint |
|
| drawPoints | Draws multiple points using specified paint and point style. | PointMode mode, List<Offset> points, Paint paint |
|
| drawImage | Draws an image at a specified offset using specified paint. | ui.Image image, Offset offset, Paint paint |
|
| drawText | Draws text at a specified offset using a TextPainter. | TextPainter painter, Offset offset |
|
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 'package:flutter/material.dart';
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.