flutter CustomPainter - how to cut out a hole in line path
Okay I found a solution for it.
I created a rect path with the size of the CustomPainter
area and built the difference with the cutout hole path.
That created a cutout template:
final rectWithCutout = Path.combine(
PathOperation.difference,
Path()
..addRect(
Rect.fromCenter(
center: Offset.zero,
width: size.width,
height: size.height,
),
),
holePath);
Which I could clip the canvas with: canvas.clipPath(rectWithCutout);
The final result:
This is the working full code example again:
import 'dart:math';
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: const Scaffold(
body: Center(
child: OvalCustomPaint(),
),
),
);
}
}
class OvalCustomPaint extends StatelessWidget {
const OvalCustomPaint({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: LayoutBuilder(
builder: (context, constraints) {
return Center(
child: CustomPaint(
painter: _Painter(),
child: SizedBox(
width: constraints.maxWidth,
height: constraints.maxHeight,
),
),
);
},
),
);
}
}
class _Painter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
canvas.translate(size.width / 2, size.height / 2);
const curveRadius = 40.0;
const legLength = 130.0;
canvas.rotate(pi / 2);
final ovalPaint = Paint()
..color = Colors.blue
..strokeWidth = 2.5
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;
const fixPoint = Offset.zero;
//* OVAL LINE
final ovalPath = Path()..moveTo(fixPoint.dx, fixPoint.dy);
ovalPath.relativeArcToPoint(
const Offset(curveRadius * 2, 0),
radius: const Radius.circular(curveRadius),
);
ovalPath.relativeLineTo(0, legLength);
ovalPath.relativeArcToPoint(
const Offset(-curveRadius * 2, 0),
radius: const Radius.circular(curveRadius),
);
ovalPath.relativeLineTo(0, -legLength);
//* CLIP HOLE
final holePath = Path();
holePath.addArc(Rect.fromCircle(center: fixPoint, radius: 13), 0, 2 * pi);
final rectWithCutout = Path.combine(
PathOperation.difference,
Path()
..addRect(
Rect.fromCenter(
center: Offset.zero,
width: size.width,
height: size.height,
),
),
holePath);
canvas.clipPath(rectWithCutout);
canvas.drawPath(
ovalPath,
ovalPaint,
);
}
@override
bool shouldRepaint(_Painter oldDelegate) => false;
}
tmaihoff
Updated on December 19, 2022Comments
-
tmaihoff over 1 year
I have a
CustomPaint
which paints an oval.I want to cut out a hole at a specific position which I couldn't figure out yet how that works.
I tried:
canvas.drawPath( Path.combine(PathOperation.difference, ovalPath, holePath), ovalPaint, );
but that doesn't cut the hole, but gives me the following result:
But this is what I want to achieve:
This oval is just an example, the "real" custom paint is gonna get more complex and I need more than just one cutout. So just painting several lines is not an alternative. I want to first define the path and then apply a cutout (or even inverted clipping) to get the hole.
Is that possible?
Here is a full working example of what I have:
import 'package:flutter/material.dart'; import 'dart:math'; const Color darkBlue = Color.fromARGB(255, 18, 32, 47); void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData.dark().copyWith( scaffoldBackgroundColor: darkBlue, ), debugShowCheckedModeBanner: false, home: const Scaffold( body: Center( child: OvalCustomPaint(), ), ), ); } } class OvalCustomPaint extends StatelessWidget { const OvalCustomPaint({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Center( child: LayoutBuilder( builder: (context, constraints) { return Center( child: CustomPaint( painter: _Painter(), child: SizedBox( width: constraints.maxWidth, height: constraints.maxHeight, ), ), ); }, ), ); } } class _Painter extends CustomPainter { @override void paint(Canvas canvas, Size size) { canvas.translate(size.width / 2, size.height / 2); const curveRadius = 50.0; const legLength = 150.0; canvas.rotate(pi/2); final ovalPaint = Paint() ..color = Colors.blue ..strokeWidth = 2.5 ..style = PaintingStyle.stroke ..strokeCap = StrokeCap.round; const fixPoint = Offset.zero; //* OVAL LINE final ovalPath = Path()..moveTo(fixPoint.dx, fixPoint.dy); ovalPath.relativeArcToPoint( const Offset(curveRadius * 2, 0), radius: const Radius.circular(curveRadius), ); ovalPath.relativeLineTo(0, legLength); ovalPath.relativeArcToPoint( const Offset(-curveRadius * 2, 0), radius: const Radius.circular(curveRadius), ); ovalPath.relativeLineTo(0, -legLength); //* CLP HOLE final holePath = Path(); holePath.addArc(Rect.fromCircle(center: fixPoint, radius: 13), 0, 2 * pi); canvas.drawPath( Path.combine(PathOperation.difference, ovalPath, holePath), ovalPaint, ); } @override bool shouldRepaint(_Painter oldDelegate) => false; }
-
pskink about 2 yearsdo you want to create some kind of stadium shaped progress indicator?
-
tmaihoff about 2 yearsno, nothing like that. This example only points out the problem. The real shape is gonna be more complex
-
pskink about 2 yearsyou could try
PathMetric.extractPath
method -
tmaihoff about 2 yearscould you provide a little more information? What should I do with extracted paths then?
-
pskink about 2 years
PathMetric.extractPath
returns aPath
- you draw it withCanvas.drawPath
method -canvas.drawPath(extracedPath, ovalPaint);
-
-
tmaihoff about 2 yearsThanks @Yeasin Sheikh, but I need to cut out portions of after I draw it. Just altering the length does not work because my "real" is more complex and I might also cut out portions of the arc for example
-
Yeasin Sheikh about 2 yearsHow about using Clipper