Background of CustomPaint Widget
The RenderObjectWidget we learned earlier 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!
Why CustomPaint?
Avoid the complexity of RenderObjectWidget and focus only on drawing
❌ RenderObject 방식
✅ CustomPaint 방식
💡 핵심 아이디어
CustomPaint는 내부적으로 RenderObject를 생성하고 관리합니다. 개발자는 그리기 로직만 함수로 전달하면 되므로, 복잡한 클래스 구조를 이해할 필요가 없습니다.
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 CustomPaint Usage
Implement custom drawing by passing a function to the painter property
Canvas API 사용법
canvas: {
paint: (context, size) => {
const ctx = context.canvas;
// 색상 설정
ctx.fillStyle = '#3B82F6';
ctx.strokeStyle = '#FF0000';
// 선 설정
ctx.lineWidth = 2;
ctx.lineCap = 'round';
ctx.lineJoin = 'miter';
// 그리기
ctx.fillRect(x, y, w, h);
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI * 2);
ctx.fill();
}
}
최적화 팁
// shouldRepaint 사용
CustomPaint({
painter: {
dependencies: myData,
shouldRepaint: (oldPainter) => {
// 데이터가 변경되었을 때만 다시 그림
return oldPainter.dependencies !== myData;
},
canvas: {
paint: (context, size) => {
// 그리기 로직
}
}
}
})
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 위젯packages/flitter/src/engine/canvas/
: Canvas 구현packages/flitter/src/engine/paint.ts
: Paint 클래스
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
In the next chapter, we will examine practical examples that utilize everything we have learned so far.