CustomPaint for Easy Drawing
Background
The RenderObjectWidget is powerful but complex. Even when you simply want to draw something, you need to inherit from RenderObject, implement createRenderObject, and override the paint method.
CustomPaint solves this complexity. Just pass the paint behavior as a first-class function and you're done!
Advantages of CustomPaint
- Simple API: Just pass the painter function
- Fast Prototyping: No complex class structure needed
- Reusability: Easy to replace painter functions
- Performance: Uses internally optimized RenderObject
Using CustomPaint
Basic Usage
// For SVG renderer
CustomPaint({
size: new Size(300, 200),
painter: {
svg: {
createDefaultSvgEl(context) {
return {
circle: context.createSvgEl('circle')
};
},
paint(els, size) {
els.circle.setAttribute('cx', `${size.width / 2}`);
els.circle.setAttribute('cy', `${size.height / 2}`);
els.circle.setAttribute('r', '50');
els.circle.setAttribute('fill', '#3B82F6');
}
}
}
})
// For Canvas renderer
CustomPaint({
size: new Size(300, 200),
painter: {
canvas: {
paint(context, size) {
const ctx = context.canvas;
ctx.fillStyle = '#3B82F6';
ctx.beginPath();
ctx.arc(size.width / 2, size.height / 2, 50, 0, Math.PI * 2);
ctx.fill();
}
}
}
})shouldRepaint Optimization
You can control when to repaint for performance:
CustomPaint({
painter: {
dependencies: myData, // Dependency data
shouldRepaint: (oldPainter) => {
// Return true to repaint
// Return false to keep previous painting
return oldPainter.dependencies !== myData;
},
svg: {
// SVG implementation
},
canvas: {
// Canvas implementation
}
}
})SVG vs Canvas Rendering
Flitter's CustomPaint supports two rendering methods:
SVG Rendering
- Maintains quality when scaling
- Suitable for vector graphics
- Create SVG elements with createDefaultSvgEl()
- Set attributes with setAttribute in paint()
svg: {
createDefaultSvgEl(context) {
return {
line: context.createSvgEl('path'),
point: context.createSvgEl('circle')
};
},
paint(els, size) {
els.line.setAttribute('d', pathData);
els.line.setAttribute('stroke', '#3B82F6');
els.point.setAttribute('cx', '50');
els.point.setAttribute('cy', '50');
}
}Canvas Rendering
- Suitable for complex pixel manipulation
- Advantageous for high-performance animations
- Same approach as web Canvas API
canvas: {
paint(context, size) {
const ctx = context.canvas;
ctx.fillStyle = '#3B82F6';
ctx.fillRect(0, 0, size.width, size.height);
ctx.strokeStyle = '#FF0000';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(100, 100);
ctx.stroke();
}
}Source Code Location
To examine the implementation of CustomPaint:
packages/flitter/src/component/CustomPaint.ts: CustomPaint widgetpackages/flitter/src/engine/canvas/: Canvas implementationpackages/flitter/src/engine/paint.ts: Paint class
Key Summary
- CustomPaint hides the complexity of RenderObject
- Provides separate svg/canvas implementations in the painter object
- Performance can be optimized with dependencies and shouldRepaint
- Supports both SVG and Canvas rendering methods
- You only need to provide implementation for the renderer you use
- Can be easily combined with animations