How to create quarter ring shape in Flutter? - flutter

I'm trying to create the quarter ring shape in the top left corner without success, any help?
I tried creating a container with border radius and then give it a border for the color, but it didn't go as I expected

You can create custom ring circle by this way
Firstly you need to Create custom class same as below
import 'dart:math' as math;
class MyCustomPainter extends CustomPainter {
final learned;
final notLearned;
final range;
final totalQuestions;
double pi = math.pi;
MyCustomPainter({this.learned, this.totalQuestions, this.notLearned, this.range});
#override
void paint(Canvas canvas, Size size) {
double strokeWidth = 7;
Rect myRect = const Offset(-50.0, -50.0) & const Size(100.0, 100.0);
var paint1 = Paint()
..color = Colors.red
..strokeWidth = strokeWidth
..style = PaintingStyle.stroke;
double firstLineRadianStart = 0;
double _unAnswered = (totalQuestions - notLearned - learned) * range / totalQuestions;
double firstLineRadianEnd = (360 * _unAnswered) * math.pi / 180;
canvas.drawArc(
myRect, firstLineRadianStart, firstLineRadianEnd, false, paint1);
double _learned = (learned) * range / totalQuestions;
double secondLineRadianEnd = getRadians(_learned);
canvas.drawArc(myRect, firstLineRadianEnd, secondLineRadianEnd, false, paint1);
}
double getRadians(double value) {
return (360 * value) * pi / 180;
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
Then call that class in following way
Stack(
children: [
//Your body widget will be here
CustomPaint(
painter: MyCustomPainter(
totalQuestions: 300,
learned: 75,
notLearned: 75,
range: 10),
)
],
),
It will look like this

Related

Flutter circular progress indicator - repeatable-Custom painter

I want to create something like that:
Want to achieve
I have achieved this:
Done up until now
I am struggling to add just vertical line at state of this circular progress bar just like the line at trailing.
import 'dart:math';
import 'package:flutter/material.dart';
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);
final paint = Paint()
..strokeCap = StrokeCap.round
..color = Colors.white
..style = PaintingStyle.stroke
..strokeWidth = 2;
canvas.drawLine(
Offset(midOffset.dy, 10),
Offset(midOffset.dy,-10),
paint,
);
canvas.drawArc(
Rect.fromCenter(center: midOffset, width: size.width, height: size.height),
deg2Rand(-90),
deg2Rand(360 * percentage),
false,
paint,
);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
Calculate the end position of the line by using the formula found in this stackoverflow answer. Since we start the angle at -90 degrees we must take that away from the sweepAngle.
Calculate the points 10 units before and after the end position of the line using the formula in this answer, plugging in the center of the circle and the end position of the line.
Draw the line with canvas.drawLine
Here is what your updated LoaderPaint class looks like with these changes:
class LoaderPaint extends CustomPainter {
final double percentage;
const LoaderPaint({
required this.percentage,
});
deg2Rand(double deg) => deg * pi / 180;
#override
void paint(Canvas canvas, Size size) {
final radius = size.width / 2;
final sweepAngle = deg2Rand(360 * percentage);
final theta = deg2Rand(-90) + sweepAngle;
final midOffset = Offset(radius, radius);
final endOffset = Offset(radius + radius * cos(theta), radius + radius * sin(theta));
final midEndDiff = sqrt(pow(endOffset.dx - midOffset.dx, 2) + pow(endOffset.dy - midOffset.dy, 2));
final paint = Paint()
..strokeCap = StrokeCap.round
..color = Colors.white
..style = PaintingStyle.stroke
..strokeWidth = 2;
canvas.drawLine(
Offset(midOffset.dy, 10),
Offset(midOffset.dy,-10),
paint,
);
canvas.drawArc(
Rect.fromCenter(center: midOffset, width: size.width, height: size.height),
deg2Rand(-90),
sweepAngle,
false,
paint,
);
canvas.drawLine(
Offset(endOffset.dx + (10/midEndDiff) * (endOffset.dx - midOffset.dx), endOffset.dy + (10/midEndDiff) * (endOffset.dy - midOffset.dy)),
Offset(endOffset.dx - (10/midEndDiff) * (endOffset.dx - midOffset.dx), endOffset.dy - (10/midEndDiff) * (endOffset.dy - midOffset.dy)),
paint,
);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
This dartpad shows it working. You can easily change the percentage to see it work at all angles.
You must bear in mind this will only work if the width and height of the CustomPaint widget are exactly the same, however your example without the end cap would also break if the width and height were different.

Flutter reverse a text in a canvas

I want to make a data wheel.
This is what it looks like :
My problem:
I want all the text that is between 181 degrees and 360 degrees, i.e. the left part of the circle, to be flipped 180 degrees so that the reading direction is easier.
My code:
class CanvasPainter extends CustomPainter
{
List<AreaEntity> areas;
CanvasPainter(this.areas)
: dotsPerRing = areas.length;
final int dotsPerRing;
final double dotRadius = 6;
#override
void paint(Canvas canvas, Size size) {
// General variable
final Offset ringCenter = size.center(Offset.zero);
final double centerCircleRadius = size.width / 6;
final double deltaAngle = 2 * pi / dotsPerRing;
// Main Circle
_drawBigCircle(
canvas: canvas,
offset: ringCenter,
radius: centerCircleRadius,
);
_drawDots(
canvas: canvas,
offset: ringCenter,
radius: centerCircleRadius,
deltaAngle: deltaAngle
);
_drawTextDot(
canvas: canvas,
size: size,
offset: ringCenter,
radius: centerCircleRadius,
deltaAngle: deltaAngle,
areas: areas
);
}
void _drawCenterCircle({
required Canvas canvas,
required Offset offset,
required double radius
}){
Paint outCirclePaint = Paint()
..color = AppColors.kcolor_bleu
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke
..strokeWidth = 2;
canvas.drawCircle(offset, 20, outCirclePaint);
Paint inCirclePaint = Paint()
..color = AppColors.kcolor_bleu
..strokeCap = StrokeCap.round;
canvas.drawCircle(offset, 5, inCirclePaint);
}
void _drawBigCircle({
required Canvas canvas,
required Offset offset,
required double radius
}){
Paint defaultCirclePaint = Paint()
..color = AppColors.kBg_light
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke
..strokeWidth = 1;
canvas.drawCircle(offset, radius, defaultCirclePaint);
}
void _drawDots({
required Canvas canvas,
required Offset offset,
required double radius,
required double deltaAngle
}){
// Dot
final Paint dotBackground = Paint()
..color = AppColors.kBg_normal
..strokeCap = StrokeCap.round;
final Paint dotPaint = Paint()
..color = AppColors.kFont_grey
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke
..strokeWidth = 3;
// Draw a single ring
Path dotCirclePath = Path();
for(int i = 0; i < dotsPerRing; i++){
dotCirclePath.addOval(
Rect.fromCircle(
center: offset + _rotateVector(
Offset(0, radius),
deltaAngle * i
),
radius: dotRadius
)
);
}
canvas.drawPath(dotCirclePath, dotBackground);
canvas.drawPath(dotCirclePath, dotPaint);
}
void _drawTextDot({
required Canvas canvas,
required Size size,
required Offset offset,
required double radius,
required double deltaAngle,
required List<AreaEntity> areas
}){
areas.forEach((m) {
double w3 = deltaAngle * m.id;
drawTextSlant(canvas, offset, radius, w3, m.text, TextStyle(
color: Colors.white,
fontSize: 8.0,
fontWeight: FontWeight.bold,
));
});
}
void drawTextSlant(
Canvas canvas,
Offset arcCenter,
double radius,
double w,
String text,
TextStyle style
){
final pos = Offset(radius, 0);
canvas.save();
canvas.translate(arcCenter.dx, arcCenter.dy);
canvas.rotate(w - degreeToRadian(90));
if(radianToDegree(w) > 180){
// => how to reverse text ??? -------------------------------------------------
}
final tp = measureText(canvas, text, style);
final ww = tp.height;
tp.paint(canvas, pos + Offset(15 , -ww / 2.0));
canvas.restore();
}
TextPainter measureText(Canvas canvas, String text, TextStyle style)
{
final textSpan = TextSpan(text: text, style: style);
final textPainter = TextPainter(text: textSpan, textDirection: TextDirection.ltr);
textPainter.layout(minWidth: 0, maxWidth: double.maxFinite);
return textPainter;
}
Offset _rotateVector(Offset vector, double angleInRadians)
{
return Offset(
(cos(angleInRadians) * vector.dx) - (sin(angleInRadians) * vector.dy),
(sin(angleInRadians) * vector.dx) - (cos(angleInRadians) * vector.dy)
);
}
double degreeToRadian(double degree) {
return degree * pi / 180;
}
double radianToDegree(double radian) {
return radian * 180 / pi;
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
I'm new to canvas so if you have any suggestions to improve my code I'll gladly accept the advice
Any guidance on the best way to accomplish this would be appreciated.
EDIT : I succeeded I edit the code
void _drawTextDot({
required Canvas canvas,
required Size size,
required Offset offset,
required double radius,
required double deltaAngle,
required List<PaxsAreaEntity> areas
}){
areas.forEach((m) {
double w3 = deltaAngle * m.id;
drawTextSlant(canvas, offset, radius, w3, m.text, TextStyle(
color: AppColors.kFont_grey,
fontSize: 6.0,
fontWeight: FontWeight.bold,
), size);
});
}
void drawTextSlant(
Canvas canvas,
Offset arcCenter,
double radius,
double w,
String text,
TextStyle style,
Size size
){
double distanceFromDot = 20;
final pos = Offset(0, 0);
canvas.save();
canvas.translate(size.width / 2, size.height / 2);
canvas.rotate(degreeToRadian(-90));
canvas.translate(radius * cos(w), radius * sin(w));
canvas.rotate(w);
final tp = measureText(canvas, text, style);
final ww = tp.height;
if(radianToDegree(w) > 180){
canvas.rotate(degreeToRadian(180));
canvas.translate(-tp.width, 0);
distanceFromDot = distanceFromDot * -1;
}
tp.paint(canvas, pos + Offset(distanceFromDot , -ww / 2.0));
canvas.restore();
}

Add A Custom Decoration To Container, Using Painter Class

I know I can give decoration to my container, and then provide a border to it.
But I am trying to Wrap my Container with some Custom border using Custom Paint class, The Custom Paint Class should surround or provide a border to container like a dashed border or any custom shape border,
Note:- Please Don't suggest me to use any custom package like dashed Border, I want the answer in the form of Custom Painter like BoxPainter class etc.
So, Far I have wrote this code
import 'dart:math';
import 'package:flutter/material.dart';
class CustPaint extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
width: 300,
height: 300,
decoration: CustomDecoration(),
child: new Icon(Icons.wb_sunny),
)),
);
}
}
class CustomDecoration extends Decoration {
#override
BoxPainter createBoxPainter([void Function() onChanged]) {
return BoxDecorationPainter();
}
}
class BoxDecorationPainter extends BoxPainter {
#override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
final Rect bounds = offset & configuration.size;
_drawDecoration(canvas, bounds);
}
}
void _drawDecoration(Canvas canvas, Rect size) {
var radius = min(size.width / 2, size.height / 2);
var circleRaidus = radius * .012;
var paint = Paint()
..color = Colors.teal
..strokeWidth = circleRaidus * .5
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;
for (int i = 0; i < 60; i++) {
Offset topRight = Offset(size.left + i * 4, size.top);
Offset downLeft = Offset(size.left, size.top + i * 4);
Offset bottomRight = Offset(size.left + i * 4, size.bottom);
Offset downRight = Offset(size.right, size.top + i * 4);
canvas.drawCircle(topRight, circleRaidus * 2, paint);
canvas.drawCircle(bottomRight, circleRaidus * 2, paint);
canvas.drawCircle(downRight, circleRaidus * 2, paint);
canvas.drawCircle(downLeft, circleRaidus * 2, paint);
}
}
And What I have Achieved is
But The OUTPUT
I want is that all the lines (with tiny circles) should join and there should be no gap in between them while meeting corner to corner. (means they should join to cover the container like a square or rectangle)
Also I want to give some space between those tiny circles like
some padding.
It would be a plus if those circles can be
replaced by any image.
Thanks in Advance
The number of circles has to be adapted to the size of the box:
final nbShapes = [
((bounds.width + gap) / (shapeSize + gap)).floor(),
((bounds.height + gap) / (shapeSize + gap)).floor(),
];
In my Solution, the CustomDecorationBox can be configured with:
double shapeSize, the size of the shapes,
double shapeGap, the gap between two shapes,
Paint shapePaint, the paint used to draw the shapes (support for both strokes and fill paints)
ShapePainter paintShape, a function painting the shapes
I provided 3 ShapePainter samples:
paintCircle()
paintRectangle()
ShapePainter createNGonPainter(int n), a factory for n-gon painters
Full source code:
import 'dart:math';
import 'package:flutter/material.dart';
typedef ShapePainter = void Function(Canvas, Rect, Paint);
Random random = Random();
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'CustomDecoration Demo',
home: Scaffold(
body: MyWidget(),
),
),
);
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: LayoutBuilder(
builder: (context, constraints) {
final size = Size(1, .6) * constraints.biggest.width;
print(size);
return GridView.count(
crossAxisCount: 3,
children: List.generate(
120,
(index) => Padding(
padding: EdgeInsets.all(size.width * .01),
child: Container(
decoration: CustomDecoration(
shapeSize: 5.0 + random.nextInt(10),
shapeGap: 2.0 + random.nextInt(3),
shapePaint: Paint()
..color = Color(0x99000000 + random.nextInt(0xffffff))
..strokeWidth = random.nextInt(3).toDouble()
..style = random.nextInt(3) == 2
? PaintingStyle.fill
: PaintingStyle.stroke,
paintShape: random.nextInt(4) == 0
? paintCircle
: createNGonPainter(3 + random.nextInt(5)),
),
child: Center(
child: Text(
index.toString(),
style: TextStyle(fontSize: 24.0),
),
),
),
),
),
);
},
),
);
}
}
class CustomDecoration extends Decoration {
final double shapeSize;
final double shapeGap;
final Paint shapePaint;
final ShapePainter paintShape;
CustomDecoration({
this.shapeSize,
this.shapeGap,
this.shapePaint,
this.paintShape,
}) : super();
#override
BoxPainter createBoxPainter([void Function() onChanged]) {
return BoxDecorationPainter(
shapeSize: shapeSize ?? 10,
shapeGap: shapeGap ?? 4,
shapePaint: shapePaint ?? Paint(),
paintShape: paintShape ?? paintCircle);
}
}
class BoxDecorationPainter extends BoxPainter {
final double shapeSize;
final double shapeGap;
final Paint shapePaint;
final ShapePainter paintShape;
BoxDecorationPainter({
this.shapeSize,
this.shapeGap,
this.shapePaint,
this.paintShape,
}) : super();
#override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
final Rect bounds = offset & configuration.size;
_drawDecoration(canvas, bounds, shapeSize, shapeGap);
}
void _drawDecoration(
Canvas canvas, Rect bounds, double shapeSize, double gap) {
final nbShapes = [
((bounds.width + gap) / (shapeSize + gap)).floor(),
((bounds.height + gap) / (shapeSize + gap)).floor(),
];
final correctedGaps = [
(bounds.width - nbShapes[0] * shapeSize) / (nbShapes[0] - 1),
(bounds.height - nbShapes[1] * shapeSize) / (nbShapes[1] - 1),
];
final steps = [
correctedGaps[0] + shapeSize,
correctedGaps[1] + shapeSize,
];
for (int i = 0; i < nbShapes[0]; i++) {
paintShape(
canvas,
Rect.fromLTWH(
bounds.left + steps[0] * i,
bounds.top,
shapeSize,
shapeSize,
),
shapePaint,
);
paintShape(
canvas,
Rect.fromLTWH(
bounds.left + steps[0] * i,
bounds.bottom - shapeSize,
shapeSize,
shapeSize,
),
shapePaint,
);
}
for (int i = 1; i < nbShapes[1] - 1; i++) {
paintShape(
canvas,
Rect.fromLTWH(
bounds.left,
bounds.top + steps[1] * i,
shapeSize,
shapeSize,
),
shapePaint,
);
paintShape(
canvas,
Rect.fromLTWH(
bounds.right - shapeSize,
bounds.top + steps[1] * i,
shapeSize,
shapeSize,
),
shapePaint,
);
}
}
}
void paintCircle(Canvas canvas, Rect bounds, Paint paint) {
canvas.drawCircle(
Offset(
bounds.left + bounds.width / 2,
bounds.top + bounds.height / 2,
),
bounds.shortestSide / 2,
paint,
);
}
void paintRectangle(Canvas canvas, Rect bounds, Paint paint) {
canvas.drawRect(bounds, paint);
}
ShapePainter createNGonPainter(int n) => (canvas, bounds, paint) {
Path path = Path();
path.moveTo(
bounds.left + (bounds.width + cos(2 * pi / n) * bounds.width) / 2,
bounds.top + (bounds.height + sin(2 * pi / n) * bounds.height) / 2,
);
for (var k = 2; k <= n; k++) {
path.lineTo(
bounds.left + (bounds.width + cos(2 * k * pi / n) * bounds.width) / 2,
bounds.top +
(bounds.height + sin(2 * k * pi / n) * bounds.height) / 2,
);
}
path.close();
canvas.drawPath(path, paint);
};

How to style the edges of a circular chart in Flutter

I'm using fl_chart package, and it has been helpful, but it lacks the possibility to edit the circular chart styling to make the edges more circular, so if anyone could point out to a better way to achieve this design :
What I achieved so far
Use CustomPainter
import 'dart:math';
import 'package:flutter/material.dart';
class ProgressRings extends CustomPainter {
/// From 0.0 to 1.0
final double completedPercentage;
final double circleWidth;
final List<Color> gradient;
final num gradientStartAngle;
final num gradientEndAngle;
final double progressStartAngle;
final double lengthToRemove;
ProgressRings({
this.completedPercentage,
this.circleWidth,
this.gradient,
this.gradientStartAngle = 3 * pi / 2,
this.gradientEndAngle = 4 * pi / 2,
this.progressStartAngle = 0,
this.lengthToRemove = 0,
});
#override
void paint(Canvas canvas, Size size) {
Offset center = Offset(size.width / 2, size.height / 2);
double radius = min(size.width / 2, size.height / 2);
double arcAngle = 2 * pi * (completedPercentage);
Rect boundingSquare = Rect.fromCircle(center: center, radius: radius);
paint(List<Color> colors,
{double startAngle = 0.0, double endAngle = pi * 2}) {
final Gradient gradient = SweepGradient(
startAngle: startAngle,
endAngle: endAngle,
colors: colors,
);
return Paint()
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke
..strokeWidth = circleWidth
..shader = gradient.createShader(boundingSquare);
}
canvas.drawArc(
boundingSquare,
-pi / 2 + progressStartAngle,
arcAngle - lengthToRemove,
false,
paint(
gradient,
startAngle: gradientStartAngle,
endAngle: gradientEndAngle,
),
);
}
#override
bool shouldRepaint(CustomPainter painter) => true;
}
Full Source here
Video Link here

Flutter : How to set dynamic color in CustomPainter object

Setting paint color dynamically for CustomPainter's constructor not working.
lines_painter.dart
class LinesPainter extends CustomPainter {
final double lineHeight = 8;
final int maxLines = 60;
final Color customColor;
LinesPainter(this.customColor);
#override
void paint(Canvas canvas, Size size) {
canvas.translate(size.width / 2, size.height / 2);
canvas.save();
final Paint linePainter = Paint()
..color = customColor
..style = PaintingStyle.stroke
..strokeWidth = 1.5;
final radius = size.width / 2;
List.generate(maxLines, (i) {
var newRadius = (i % 5 == 0) ? radius - 15 : radius - 5;
canvas.drawLine(Offset(0, radius), Offset(0, newRadius), linePainter);
canvas.rotate(2 * pi / maxLines);
});
canvas.restore();
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
utils.dart
class Utils{
List<Color> getColorsArray (){
return [
Color(0xff5733),
Color(0xc70039),
Color(0x900c3e),
Color(0x571845),
Color(0x251e3e),
Color(0x051e3e),
];
}
}
Below code should paint Round shape with lines
LinesPainter(Utils().getColorsArray()[0])
Expected result:
Current result:
As #pskink mentioned in comments I have read the documentation and i got the idea that I am missing the Alpha value in hex code.
Make a change in utils.dart file like below and it works fine for me.
class Utils{
List<Color> getColorsArray (){
return [
Color(0xffff5733),
Color(0xffc70039),
Color(0xff900c3e),
Color(0xff571845),
Color(0xff251e3e),
Color(0xff051e3e),
];
}
}