Center an Item on Canvas using CustomPaint - flutter

I've tried to center items in a Canvas using the CustomPainter in Flutter, ranging from TextPaints to even plain circles.
Normally, when centering a custom paint, I'd set the Offset to Offset(size.width/2, size.height/2). But it's not working and my item is drawn towards the bottom right of the canvas.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: Container(
child: LayoutBuilder(builder: (context, constraints) {
return Container(
width: constraints.widthConstraints().maxWidth,
height: constraints.widthConstraints().maxHeight,
child: CustomPaint(
painter: FollowPainter(),
),
);
}),
));
}
}
class FollowPainter extends CustomPainter {
static final fill = Paint()..color = Colors.black;
#override
void paint(Canvas canvas, Size size) {
TextSpan span = new TextSpan(
style: new TextStyle(
color: Colors.black, fontSize: 50, fontWeight: FontWeight.w700),
text: 'Hello World');
TextPainter tp = new TextPainter(
text: span,
textAlign: TextAlign.left,
textDirection: TextDirection.ltr);
tp.layout();
tp.paint(canvas, new Offset(size.width / 2, size.height / 2));
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
// TODO: implement shouldRepaint
throw UnimplementedError();
}
I suspect that the problem could be the fact that I'm starting the drawing from that Offset, instead of drawing from the center.
So how can I center a drawn widget in the CustomPainter?

I suspect that the problem could be the fact that I'm starting the drawing from that Offset, instead of drawing from the center.
You're right, just subtract half of the width of the text from width and half of its height from height.
tp.paint(canvas, new Offset(size.width / 2 - tp.width / 2, size.height / 2 - tp.height / 2));

Related

How to draw Text as a child of Custom Painter shape Flutter

I am trying to center a Text widget in a custom shape made using Custom Painter i tried laying a Stack widget but it failed i googled and found about TextPainter and tried to use it I dont know if its the right solution but i could not acheive what i wanted
I want to achieve the button with green arrow i currently have the button with yellow arrow
Any Idea?
class CustomShape1 extends CustomPainter{
#override
void paint(Canvas canvas, Size size) {
TextPainter textPainter = TextPainter(
text: TextSpan(
text: 'Foo\nBar',
style: TextStyle(
color: Colors.white,
fontSize: 30,
),
),
textDirection: TextDirection.ltr,
textAlign: TextAlign.center
);
Paint fillPaint = Paint();
fillPaint.color = const Color(0xFF830B2C);
fillPaint.style = PaintingStyle.fill;
Path path = Path();
path.moveTo(size.width*0.5000125,size.height*0.2996000);
path.cubicTo(size.width*0.6875031,size.height*0.2990750,
size.width*0.6871875,size.height*0.2979000,size.width*0.7500000,size.height*0.2989000);
path.quadraticBezierTo(size.width*0.7645125,size.height*0.5243200,
size.width*0.9373000,size.height*0.6009200);
path.lineTo(size.width*0.9373000,size.height*0.6395600);
path.lineTo(size.width*0.5000500,size.height*0.6401600);
path.quadraticBezierTo(size.width*0.5000406,size.height*0.5550200,
size.width*0.5000125,size.height*0.2996000);
path.close();
canvas.drawPath(path, fillPaint);
textPainter.layout(
minWidth: 0,
maxWidth: size.width,
);
final offset = Offset(50 - (textPainter.width / 2), 100 - (textPainter.height / 2));
textPainter.paint(canvas,offset);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
After inspecting your canvas, you can see that it is not drawn correctly, so you do not see the text on the shape.
final offset = Offset(size.width / 2 + 50, size.height / 2); //text offset
either remake the shape, or try to adjust to this offset, this will give you the visibility of the text on the shape for a start

Flutter Circular Progress bar with gap at intervals

I need to create a Circular progress bar with intervals on the outer circle. The progress bar fills the empty spaces when it is tapped.
Circle with gaps at intervals
Completed circle
You can use CustomPainter or ClipPath with Stack
Stack(
alignment: Alignment.center,
children: [
CustomPaint(
size: const Size(100, 100),
painter: LoaderPaint(percentage: _sliderValue),
),
/// image widget with size, or may wrap stack with sizedBox
],
)
Pain Class
class LoaderPaint extends CustomPainter {
final double percentage;
LoaderPaint({
required this.percentage,
});
deg2Rand(double deg) => deg * pi / 180;
#override
void paint(Canvas canvas, Size size) {
final midOffset = Offset(size.width / 2, size.height / 2);
Paint paint = Paint()..color = Colors.black;
canvas.drawArc(
Rect.fromCenter(
center: midOffset,
width: size.width * .9,
height: size.height * .9,
),
deg2Rand(-90),
deg2Rand(360 * percentage),
true,
paint,
);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}

Align Text in custom created Shape : Flutter

I have created one Custom shape in Flutter. The issue I am facing is, I have used ShapeBorder and assigned it Card. I want to align my text inside the drawn widget but currently it's also considering oursider view as a part of a widget.
Here is the code I have used:
Align(
child: Container(
height: 250,
width: 300,
child: Card(
clipBehavior: Clip.hardEdge,
shape: Shape1(factor: 0.8),
child: Container(
padding: EdgeInsets.only(left: 16.0),
child: Align(
alignment: Alignment.bottomLeft,
child: Text(
'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book',
// style: textStyleNotoSansRegular(),
),
),
),
),
),
widthFactor: 0.8,
),
Shape Class:
class Shape1 extends ShapeBorder {
final double factor;
Shape1({this.factor});
#override
EdgeInsetsGeometry get dimensions => EdgeInsets.zero;
#override
Path getInnerPath(Rect rect, {TextDirection textDirection}) => null;
#override
Path getOuterPath(Rect rect, {TextDirection textDirection}) {
return Path()
..moveTo(rect.bottomLeft.dx, rect.bottomLeft.dy)
..relativeLineTo(rect.width * factor, 0)
..lineTo(rect.topRight.dx, rect.topRight.dy)
..relativeLineTo(rect.width * -factor, 0)
..close();
}
#override
void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {
var path = Path()
..moveTo(rect.bottomLeft.dx, rect.bottomLeft.dy)
..relativeLineTo(rect.width * factor, 0)
..lineTo(rect.topRight.dx, rect.topRight.dy)
..relativeLineTo(rect.width * -factor, 0)
..close();
var boxPainter = Paint();
boxPainter.strokeWidth = 5;
boxPainter.color = Colors.yellow;
boxPainter.style = PaintingStyle.stroke;
canvas.drawPath(path, boxPainter);
}
#override
ShapeBorder scale(double t) => null;
}
Current Result:
Expected Result:

GestureDetector on painted triangles in circle

I'm trying to handle taps for three painted "quartercirlces" using the CustomPaint widget. I've tried adding GestureDetectors around the QuarterCirclePainter class. Even tried using using a GestureRecognizer for the TextSpan without any success. Tried wrapping it in containers wrapped in gestureDetectors. Looked at similar posts about adding GestureDetectors around the CustomPaint, but none of them seems to work in this case.
How can this be achieved?
class CategoryCircle extends StatelessWidget {
final Color color;
final double startAngle;
final String category;
final Offset textOffset;
CategoryCircle({this.color, this.startAngle, this.category, this.textOffset});
Widget build(BuildContext context) {
FocusScope.of(context).nextFocus();
return FractionallySizedBox(
widthFactor: 1,
heightFactor: 1,
child: Center(
child: CustomPaint(
painter: QuarterCirclePainter(
context: context,
color: color,
startAngle: startAngle,
sweepAngle: math.pi * 2 / 3,
text: category,
textOffset: textOffset)),
),
);
}
}
class QuarterCirclePainter extends CustomPainter {
final BuildContext context;
final Color color;
final double startAngle;
final double sweepAngle;
final String text;
final Offset textOffset;
const QuarterCirclePainter(
{this.context,
this.color,
this.startAngle,
this.sweepAngle,
this.text,
this.textOffset});
#override
void paint(Canvas canvas, Size size) {
Offset center = Offset(size.width / 2, size.height / 2);
Rect rect = Rect.fromCircle(center: center, radius: 130);
Path path = Path()
// set the "current point"
..moveTo(center.dx, center.dy)
..arcTo(rect, startAngle, (math.pi * 2) / 3, false);
canvas.drawPath(path, Paint()..color = color);
TextSpan span = new TextSpan(
style: GoogleFonts.overpass(
textStyle: TextStyle(fontSize: 22, fontWeight: FontWeight.bold)),
text: text);
TextPainter tp = new TextPainter(
text: span,
textAlign: TextAlign.center,
textDirection: TextDirection.ltr);
tp.layout();
tp.paint(canvas, textOffset);
}
#override
bool shouldRepaint(QuarterCirclePainter oldDelegate) {
return false;
}
}
I made a CustomClipper with what you did in the QuarterCirclePainter, it looks like this:
class QuaterCircleClipper extends CustomClipper<Path>{
double startAngle, sweepAngle;
QuaterCircleClipper({#required this.startAngle, #required this.sweepAngle});
#override
Path getClip(Size size){
Offset center = Offset(size.width / 2, size.height / 2);
Rect rect = Rect.fromCircle(center: center, radius: 130);
Path path = Path()
// set the "current point"
..moveTo(center.dx, center.dy)
..arcTo(rect, startAngle, (math.pi * 2) / 3, false);
return path;
}
#override
bool shouldReclip(oldCliper) => false;
}
And used a ClipPath with the above CustomClipper as clipper to wrap a GestureDetector which has a blue Container as its child
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: AspectRatio(
aspectRatio: 1,
child: ClipPath(
clipper: QuaterCircleClipper(startAngle: 0, sweepAngle: 120),
child: GestureDetector(
onTap: (){showDialog(context: context, builder: (_) => AlertDialog(content: Text("Tap Detected"),));},
child: Container(
color: Colors.blueAccent,
)
),
),
)
),

How BeveledRectangleBorder for TextField in flutter

I have TextField i need to change the border Corners Cut, I have try to do with wrap TextField on Container and apply BeveledRectangleBorder but is not proper.
Please suggest me how can do this.
Update
After searching a little i've found the source code of that design you shared with me. They have build a custom input border (CutCornersBorder) for that particular style. You can use that file and add that to your theme to get that look of your TextField without adding any extra code.
CutCornersBorder
return MaterialApp(
home: HomePage(),
theme: ThemeData(
inputDecorationTheme: InputDecorationTheme(border: CutCornersBorder()),
),
);
Full Preview
OLD
You can do it by creating your own custom painter implementation.
FlatCorneredBackgroundPainter
class FlatCorneredBackgroundPainter extends CustomPainter {
double radius, strokeWidth;
Color strokeColor;
FlatCorneredBackgroundPainter(
{this.radius = 10, this.strokeWidth = 4, this.strokeColor = Colors.blue});
#override
void paint(Canvas canvas, Size size) {
double w = size.width;
double h = size.height;
Paint paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = strokeWidth
..color = strokeColor;
Path path = Path()
..addPolygon([
Offset(radius, 0),
Offset(w - radius, 0),
Offset(w, radius),
Offset(w, h - radius),
Offset(w - radius, h),
Offset(radius, h),
Offset(0, h - radius),
Offset(0, radius),
], true);
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
Usage
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: CustomPaint(
painter: FlatCorneredBackgroundPainter(radius: 10.0, strokeColor: Colors.red, strokeWidth: 2),
child: Container(
width: 300,
height: 70,
padding: const EdgeInsets.all(8.0),
child: TextField(),
),
),
),
);
}
}