I am working on a small Flutter application for children, the concept is that there is an image in the background on which users have to draw. I am using Custom Painter for that.
There is a feature to erase what they draw and that is where the problem begins, the easy way to erase is to draw using the white color but when I use that it will also erase the background image which is very important. What is the proper way to erase without having to draw white on the image?
Here is the demo:
Here is the source code:
The main page
Listener(
onPointerDown: (details) {
RenderBox? renderBox = context.findRenderObject() as RenderBox?;
if (renderBox != null) {
setState(() {
points.add(
DrawingPoints(
points: renderBox.globalToLocal(details.position),
paint: Paint()
..strokeCap = strokeCap
..isAntiAlias = true
..color = widget.selectedColor.withOpacity(1.0)
..strokeWidth = strokeWidth
)
);
});
}
},
onPointerMove: (details) {
RenderBox? renderBox = context.findRenderObject() as RenderBox?;
if (renderBox != null) {
setState(() {
points.add(DrawingPoints(
points: renderBox.globalToLocal(details.position),
paint: Paint()
..strokeCap = strokeCap
..isAntiAlias = true
..color = widget.selectedColor.withOpacity(1.0)
..strokeWidth = strokeWidth));
});
}
},
onPointerUp: (details) {
setState(() {
points.add(null);
});
},
child: CustomPaint(
size: Size.infinite,
painter: DrawingPainter(
pointsList: points,
),
),
)
DrawingPainter
class DrawingPainter extends CustomPainter {
final List<DrawingPoints?> pointsList;
List<Offset> offsetPoints = [];
DrawingPainter({required this.pointsList});
@override
void paint(Canvas canvas, Size size) {
for (int i = 0; i < pointsList.length - 1; i++) {
if (pointsList[i] != null && pointsList[i + 1] != null) {
canvas.drawLine(pointsList[i]!.points, pointsList[i +1]!.points, pointsList[i]!.paint,);
}
else if (pointsList[i] != null && pointsList[i + 1] == null) {
offsetPoints.clear();
offsetPoints.add(pointsList[i]!.points);
offsetPoints.add(Offset(pointsList[i]!.points.dx + 0.1, pointsList[i]!.points.dy + 0.1));
canvas.drawPoints(PointMode.points, offsetPoints, pointsList[i]!.paint);
}
}
}
@override
bool shouldRepaint(DrawingPainter oldDelegate) => true;
}
DrawingPoints
class DrawingPoints {
final Offset points;
final Paint paint;
const DrawingPoints({required this.points, required this.paint});
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is DrawingPoints &&
other.points.dx == points.dy &&
other.paint.color == paint.color;
}
@override
int get hashCode => points.hashCode ^ paint.hashCode;
}
I was finally been able to solve the issue.
The solution looks very simple, what I did is to put the background image in a Stack as suggested by @iStornZ but I put the image above the custom painter, the image has to be transparent so that even when we paint in white the whole image will still be visible.
Source code
Demo