I'm trying to draw an image on a canvas while making a custom shape of a dvd disk on it.
I'm still new to drawing in general and trying to learn it so I managed to draw the custom shape I wanted by combining quadraticBezierTo, lineTo. I tried searching for a way to apply the image to the custom shape I drew but the result I get is as it follows
my code is as it follows
late ui.Image sBackground;
which will be initialized before calling the paint class
code For the paint class is:
class Painter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..style = PaintingStyle.fill;
canvas.drawImage(sBackground, Offset.zero, paint); // drawing image to canvas in here
Path path = Path()..moveTo(0, 0);
path.quadraticBezierTo(0, 0, size.width * 0.5, 0);
path.quadraticBezierTo(size.width, 0, size.width, size.height * 0.5);
path.lineTo(size.width * 0.55, size.height * 0.5);
path.quadraticBezierTo(size.width * 0.55, size.height * 0.45,
size.width * 0.5, size.height * 0.45);
path.quadraticBezierTo(size.width * 0.45, size.height * 0.45,
size.width * 0.45, size.height * 0.5);
path.quadraticBezierTo(size.width * 0.45, size.height * 0.55,
size.width * 0.5, size.height * 0.55);
path.quadraticBezierTo(size.width * 0.55, size.height * 0.55,
size.width * 0.55, size.height * 0.5);
path.lineTo(size.width, size.height * 0.5);
path.quadraticBezierTo(
size.width, size.height, size.width * 0.5, size.height);
path.quadraticBezierTo(0, size.height, 0, size.height * 0.5);
path.quadraticBezierTo(0, 0, size.width * 0.5, 0);
path.close();
canvas.drawShadow(path, Colors.white30, 2.0, true);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
reference: Clip objects drawn outside of canvas which I read and tried but didn't get a result
NOTE: I'm still searching for a way to solve my problem as I post this question in here. any help or docs will really be helpful and thanks in advance
EDIT : for anyone else having same problem please take a look at the comments below for the solution
CodePudding user response:
instead of CustomPainter
i would use a custom ShapeBorder
, note that the "elevation" shadow is hardcoded in BoxShadow
constructor - you can change that by adding some extra parameters to Cover
widget, also you can add some custom painting by overriding CoverShape.paint
method:
class Cover extends StatelessWidget {
const Cover({
required this.image,
Key? key,
}) : super(key: key);
Cover.asset(
String name,
Key? key,
) : this(key: key, image: AssetImage(name));
Cover.file(
File file,
Key? key,
) : this(key: key, image: FileImage(file));
Cover.network(
String src,
Key? key,
) : this(key: key, image: NetworkImage(src));
final ImageProvider image;
@override
Widget build(BuildContext context) {
return Center(
child: AspectRatio(
aspectRatio: 1,
child: Container(
clipBehavior: Clip.antiAlias,
decoration: ShapeDecoration(
shape: CoverShape(),
shadows: const [BoxShadow(color: Colors.black, blurRadius: 5, spreadRadius: 1, offset: Offset(3, 3))],
),
child: Image(image: image, fit: BoxFit.cover),
),
),
);
}
}
class CoverShape extends ShapeBorder {
@override
EdgeInsetsGeometry get dimensions => EdgeInsets.zero;
@override
ui.Path getInnerPath(ui.Rect rect, {ui.TextDirection? textDirection}) => getOuterPath(rect);
@override
ui.Path getOuterPath(ui.Rect rect, {ui.TextDirection? textDirection}) {
final center = rect.center;
final radius = rect.shortestSide / 2;
return Path()
..fillType = PathFillType.evenOdd
..addOval(Rect.fromCircle(center: center, radius: radius))
..addOval(Rect.fromCircle(center: center, radius: 0.1 * radius));
}
@override
void paint(ui.Canvas canvas, ui.Rect rect, {ui.TextDirection? textDirection}) {
}
@override
ShapeBorder scale(double t) => this;
}
alternatively you can use ClipPath
widget but with that solution the shadows cannot be used:
class Cover extends StatelessWidget {
const Cover({
required this.image,
Key? key,
}) : super(key: key);
Cover.asset(
String name,
Key? key,
) : this(key: key, image: AssetImage(name));
Cover.file(
File file,
Key? key,
) : this(key: key, image: FileImage(file));
Cover.network(
String src,
Key? key,
) : this(key: key, image: NetworkImage(src));
final ImageProvider image;
@override
Widget build(BuildContext context) {
return Center(
child: AspectRatio(
aspectRatio: 1,
child: ClipPath(
clipper: CoverClipper(),
child: Image(image: image, fit: BoxFit.cover),
),
),
);
}
}
class CoverClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
final center = size.center(Offset.zero);
final radius = size.shortestSide / 2;
return Path()
..fillType = PathFillType.evenOdd
..addOval(Rect.fromCircle(center: center, radius: radius))
..addOval(Rect.fromCircle(center: center, radius: 0.1 * radius));
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}
CodePudding user response:
for anyone else having same problem here is my final code
class PainterV2 extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = const Color.fromARGB(255, 0, 0, 0)
..style = PaintingStyle.fill;
Path path = Path()
..fillType = PathFillType.evenOdd
..moveTo(0, 0);
Path path2 = Path()
..fillType = PathFillType.evenOdd
..moveTo(0, 0);
var rect = Rect.fromLTRB(0, 0, size.width, size.height);
var rect2 = Rect.fromLTRB(size.width * 0.47, size.height * 0.47,
size.width * 0.53, size.height * 0.53);
path.addOval(rect);
path.addOval(rect2);
path2.addOval(rect.deflate(69));
path2.addOval(rect2);
// canvas.drawPath(path, paint);
path.close();
path2.close();
canvas.clipPath(path);
canvas.drawImage(paintimage, Offset.zero, paint);
canvas.drawPath(path2, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
instead of using quadraticBezier i switched to addOval , also if i use canvas.drawimage alongside canvas.drawPath it wont work, and keep in mind that order wich you place it matters in my case it neded to be ,
path.close();
canvas.clipPath(path);
canvas.drawImage(paintimage, Offset.zero, paint);
long story short the script above will make a circle in shape of cd with a hole in the middle alongside another shape in black to cover the hole in the middle