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;
}
}
Related
I made a custom shape of a crooked square for music artwork images, now I'm trying to make the image fill it completely whilst not leaking out of it.
I tried using a stack, but it is bugging the painted square and the image still would leak and not fill it completely.
SizedBox(
width: 50,
height: 50,
child: CustomPaint(
painter: ImperfectRectangleBorder(strokeColor: Colors.white),
child: QueryArtworkWidget(//from on_audio_query package
artworkBorder: BorderRadius.zero,
artworkFit: BoxFit.fill,
id: item.data![index].id,
type: ArtworkType.AUDIO,
),
),
),
class ImperfectRectangleBorder extends CustomPainter {
final Color strokeColor;
ImperfectRectangleBorder({this.strokeColor = Colors.white});
#override
void paint(Canvas canvas, Size size) {
var paint = Paint()
..color = strokeColor
..strokeWidth = 4.0
..style = PaintingStyle.stroke;
var path = Path();
path.moveTo(0, -5);
path.lineTo(size.width * (0.2), size.height + 10);
path.lineTo(size.width, size.height - 5);
path.lineTo(size.height * (0.8), -1);
path.close();
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
i have the simple following code
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
width: 300,
height: 100,
decoration: const BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.only(topRight: Radius.circular(30.0),topLeft: Radius.circular(30.0),)
),
),
),
);
}
the outputs looks like following
but i am trying to reverse it to be like following (sorry for the bad drawing)
How could i achieve this with simple way ? best regards :)
This can be done with a CustomPainter:
class InvertedRoundedRectanglePainter extends CustomPainter {
InvertedRoundedRectanglePainter({
required this.radius,
required this.color,
});
final double radius;
final Color color;
#override
void paint(Canvas canvas, Size size) {
final cornerSize = Size.square(radius * 2);
canvas.drawPath(
Path()
..addArc(
// top-left arc
Offset(0, -radius) & cornerSize,
// 180 degree startAngle (left of circle)
pi,
// -90 degree sweepAngle (counter-clockwise to the bottom)
-pi / 2,
)
..arcTo(
// top-right arc
Offset(size.width - cornerSize.width, -radius) & cornerSize,
// 90 degree startAngle (bottom of circle)
pi / 2,
// -90 degree sweepAngle (counter-clockwise to the right)
-pi / 2,
false,
)
// bottom right of painter
..lineTo(size.width, size.height)
// bottom left of painter
..lineTo(0, size.height),
Paint()..color = color,
);
}
#override
bool shouldRepaint(InvertedRoundedRectanglePainter oldDelegate) =>
oldDelegate.radius != radius || oldDelegate.color != color;
}
You can play with this example on DartPad: https://dartpad.dartlang.org/?id=18cfbcc696b43c7c002a5ac3c94dd520
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,
)
),
),
)
),
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));
I want to build an application where for one of my designs I would like to populate shapes with dynamic data. These will be custom shapes where I have two different shapes and they alternate one below the other. So I have a left shape and then the next one will be a right shape and so on. Is is possible to create this in Flutter and how would I do it?
Here is one way of doing it. I have simplify the shape with my custom triangle shape created with CustomPainter so you will have to modify it to your needs.
ListView(
children: <Widget>[
OverflowTitle(color: Colors.green),
OverflowTitle(color: Colors.blue),
OverflowTitle(color: Colors.red)
],
);
and custom overflow title
class OverflowTitle extends StatelessWidget {
final Color color;
const OverflowTitle({this.color});
#override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
height: 50,
child: OverflowBox(
alignment: Alignment.bottomCenter,
minHeight: 50,
maxHeight: 70,
child: Container(
height: 60,
child: CustomPaint(
painter: TrianglePainter(
strokeColor: color,
),
child: Text(
'NO1',
textAlign: TextAlign.center,
),
),
),
),
);
}
}
Here is output
Let me know if you need more help with it...
UPDATE
here is my custom triangle painter
import 'package:flutter/material.dart';
enum Direction { Up, Down, Left, Right }
class TrianglePainter extends CustomPainter {
final Color strokeColor;
final Direction direction;
TrianglePainter({this.strokeColor = Colors.white, this.direction});
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = strokeColor
..style = PaintingStyle.fill;
canvas.drawPath(getTrianglePath(size.width, size.height), paint);
}
Path getTrianglePath(double x, double y) {
if (direction == Direction.Right) {
return Path()
..moveTo(0, y)
..lineTo(x, y / 2)
..lineTo(0, 0)
..lineTo(0, y);
} else if (direction == Direction.Left) {
return Path()
..moveTo(x, 0)
..lineTo(0, y / 2)
..lineTo(x, y)
..lineTo(x, 0);
} else if (direction == Direction.Down) {
return Path()
..moveTo(0, 0)
..lineTo(x / 2, y)
..lineTo(x, 0)
..lineTo(0, 0);
} else {
return Path()
..moveTo(0, y)
..lineTo(x / 2, 0)
..lineTo(x, y)
..lineTo(0, y);
}
}
#override
bool shouldRepaint(TrianglePainter oldDelegate) {
return oldDelegate.strokeColor != strokeColor;
}
}