I am trying to implement custom curved shape like below left image.
Below is the code what I have achieved so far by using quadraticBezierTo:
class CustomNotchedShape extends NotchedShape {
final BuildContext context;
const CustomNotchedShape(this.context);
#override
Path getOuterPath(Rect host, Rect? guest) {
const radius = 110.0;
const lx = 40.0;
const ly = 20;
const bx = 10.0;
const by = 50.0;
var x = (MediaQuery.of(context).size.width - radius) / 2 - lx;
return Path()
..moveTo(host.left, host.top)
..lineTo(x, host.top)
..quadraticBezierTo(x + bx, host.top, x += lx, host.top - ly)
..quadraticBezierTo(
x + radius / 2, host.top - by, x += radius, host.top - ly)
..quadraticBezierTo((x += lx) - bx, host.top, x, host.top)
..lineTo(host.right, host.top)
..lineTo(host.right, host.bottom)
..lineTo(host.left, host.bottom);
}
}
Related
I want to make circle container with wavy border. For that, I used flutter_custom_clippers 2.0.0 package's StarClipper with some changes. But, I can't make it same as I want. Can anyone help?
If any other way is available then please let me know. Thank you in advance :)
Here is my code:
import 'dart:math' as math;
import 'package:flutter/widgets.dart';
/// Clip widget in star shape
class StarClipper extends CustomClipper<Path> {
StarClipper(this.numberOfPoints);
/// The number of points of the star
final int numberOfPoints;
late int counter = 0;
#override
Path getClip(Size size) {
double width = size.width;
double halfWidth = width / 2;
double bigRadius = halfWidth;
double radius = halfWidth / 1.11;
double degreesPerStep = _degToRad(360 / numberOfPoints) as double;
double halfDegreesPerStep = degreesPerStep / 2;
var path = Path();
double max = 2 * math.pi;
path.moveTo(width, halfWidth);
debugPrint('halfWidth: $halfWidth');
debugPrint('bigRadius: $bigRadius');
debugPrint('degreesPerStep: $degreesPerStep');
debugPrint('max: $max');
path.moveTo(
halfWidth + radius * math.cos(0 + halfDegreesPerStep),
halfWidth + radius * math.sin(0 + halfDegreesPerStep),
);
for (double step = 0; step < max + 1; step += degreesPerStep) {
debugPrint('math.cos(step): ${math.cos(step)}');
debugPrint('math.sin(step): ${math.sin(step)}');
debugPrint(
'math.cos(step + halfDegreesPerStep): ${math.cos(step + halfDegreesPerStep)}');
debugPrint(
'math.sin(step + halfDegreesPerStep): ${math.sin(step + halfDegreesPerStep)}');
debugPrint('------step-------: $step');
debugPrint('x 1: ${halfWidth + bigRadius * math.cos(step)}');
debugPrint('y 1: ${halfWidth + bigRadius * math.sin(step)}');
debugPrint(
'x 2: ${halfWidth + radius * math.cos(step + halfDegreesPerStep)}');
debugPrint(
'y 2: ${halfWidth + radius * math.sin(step + halfDegreesPerStep)}');
/*path.quadraticBezierTo(
halfWidth + bigRadius * math.cos(step),
halfWidth + bigRadius * math.sin(step),
halfWidth + radius * math.cos(step + halfDegreesPerStep),
halfWidth + radius * math.sin(step + halfDegreesPerStep),
);*/
if (counter % 2 == 0) {
path.quadraticBezierTo(
halfWidth + bigRadius * math.cos(step),
halfWidth + bigRadius * math.sin(step),
halfWidth + radius * math.cos(step + halfDegreesPerStep),
halfWidth + radius * math.sin(step + halfDegreesPerStep),
);
} else {
path.quadraticBezierTo(
halfWidth + radius * math.cos(step),
halfWidth + radius * math.sin(step),
halfWidth + bigRadius * math.cos(step + halfDegreesPerStep),
halfWidth + bigRadius * math.sin(step + halfDegreesPerStep),
);
}
counter++;
}
path.close();
return path;
}
num _degToRad(num deg) => deg * (math.pi / 180.0);
#override
bool shouldReclip(CustomClipper<Path> oldClipper) {
StarClipper oldie = oldClipper as StarClipper;
return numberOfPoints != oldie.numberOfPoints;
}
}
Output:
I want like below
I achieved it using the same StarClipper file of flutter_custom_clippers 2.0.0 package's by applying some minor changes in above code.
double outerCurveRadius = width / 2.08;
double innerCurveRadius = width / 2.42;
for (double step = 0; step < max + 1; step += degreesPerStep) {
if (counter % 2 == 0) {
path.quadraticBezierTo(
halfWidth + outerCurveRadius * math.cos(step),
halfWidth + outerCurveRadius * math.sin(step),
halfWidth + radius * math.cos(step + halfDegreesPerStep),
halfWidth + radius * math.sin(step + halfDegreesPerStep),
);
} else {
path.quadraticBezierTo(
halfWidth + innerCurveRadius * math.cos(step),
halfWidth + innerCurveRadius * math.sin(step),
halfWidth + radius * math.cos(step + halfDegreesPerStep),
halfWidth + radius * math.sin(step + halfDegreesPerStep),
);
}
counter++;
}
Output:
Check this package flutter_custom_clippers. I think it's helpful.
Text overlapping the another enter image description heretext in Layout Customized Chart.js
enter image description here
Please , somebody can helpe me?
const { x, y } = datapoint.tooltipPosition();
const halfWidth = width / 2;
const halfHeight = width / 2;
const xLine = x >= halfWidth ? x + 100 : x - 25;
const yLine = y >= halfHeight ? y + 100 : y - 25;
const extraLine = x >= halfWidth ? 40 : -40;
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(xLine, yLine);
ctx.lineTo(xLine + extraLine, yLine);
ctx.strokeStyle = dataset.backgroundColor[index];
ctx.stroke();
ctx.font = 'bold 12px Arial';
const textXPosition = x >= halfWidth ? 'left' : 'right';
const plusFivePx = x >= halfWidth ? 5 : -5;
ctx.textAlign = textXPosition;
ctx.fillStyle = 'black';
ctx.fillText(
chart.data.labels[index],
xLine + extraLine + plusFivePx,
yLine
);
How to make chat bubble shape use path ?
expect :
class ChatBubbleShapeBorder extends ShapeBorder {
final double arrowWidth;
final double arrowHeight;
final double arrowArc;
final double radius;
ChatBubbleShapeBorder({
this.radius = 12.0,
this.arrowWidth = 16.0,
this.arrowHeight = 8.0,
this.arrowArc = 0.5,
}) : assert(arrowArc <= 1.0 && arrowArc >= 0.0);
#override
EdgeInsetsGeometry get dimensions => EdgeInsets.only(bottom: arrowHeight);
#override
Path getInnerPath(Rect rect, {TextDirection? textDirection}) =>
null ?? Path();
#override
Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
rect = Rect.fromPoints(
rect.topLeft, rect.bottomRight - Offset(0, arrowHeight));
double x = arrowWidth, y = arrowHeight, r = 1 - arrowArc;
return Path()
..addRRect(RRect.fromRectAndRadius(rect, Radius.circular(radius)))
..moveTo(rect.topRight.dx - 30, rect.topRight.dy)
..relativeLineTo(-x / 2 * r, -y * r)
..relativeQuadraticBezierTo(
-x / 2 * (1 - r), -y * (1 - r), -x * (1 - r), 0)
..relativeLineTo(-x / 2 * r, y * r);
}
#override
void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {}
#override
ShapeBorder scale(double t) => this;
}
and after tries here is my shot
actual:
i try move arrow to left and right for chat bubble.
could you help me to move arrow?
update :
i try to change rect point and move position of arrow. look good, But, i get problem with arrow
rect =
Rect.fromPoints(rect.topLeft + Offset(arrowWidth, 0), rect.bottomRight - Offset(arrowWidth, 0));
double x = arrowWidth, y = arrowHeight, r = 1 - arrowArc;
return Path()
..addRRect(RRect.fromRectAndRadius(rect, Radius.circular(radius)))
..moveTo(rect.topRight.dx + arrowWidth, rect.topRight.dy + 30)
..relativeLineTo(-x / 2 * r, -y * r)
..relativeQuadraticBezierTo(
-x / 2 * (1 - r), -y * (1 - r), -x * (1 - r), 0)
..relativeLineTo(-x / 2 * r, y * r);
shot :
I am trying to make a custom curved app bar, look like the example below
and after many tries here is my shot
source code :
class CustomShapeBorder extends ContinuousRectangleBorder {
#override
Path getOuterPath(Rect rect, {TextDirection textDirection}) {
final double innerCircleRadius = 150.0;
Path path = Path();
path.lineTo(0, rect.height);
path.cubicTo(rect.width / 1.5 - 40, rect.height + innerCircleRadius - 40, rect.width / 1.5 + 40,
rect.height + innerCircleRadius - 40, rect.width / 1.5 + 75, rect.height + 50);
path.quadraticBezierTo(
rect.width / 1.5 + (innerCircleRadius / 2) + 30, rect.height + 35, rect.width, rect.height);
path.lineTo(rect.width, 0.0);
path.close();
return path;
}
}
is there an easy way to make it for example SVG.
I could come up with this result:
Here is the Path:
double x = 150, y = 45, r = 0.5;
Path path = Path()
..addRRect(RRect.fromRectAndCorners(rect))
..moveTo(rect.bottomRight.dx - 30, rect.bottomCenter.dy)
..relativeQuadraticBezierTo(((-x / 2)+(x/6)) * (1 - r), 0, -x / 2 * r, y * r)
..relativeQuadraticBezierTo(-x / 6 * r, y * (1 - r), -x / 2 * (1 - r), y * (1 - r))
..relativeQuadraticBezierTo(((-x / 2)+(x/6)) * (1 - r), 0, -x / 2 * (1 - r), -y * (1 - r))
..relativeQuadraticBezierTo(-x/6*r , -y * r, -x / 2 * r, -y * r);
And here is a good article to see the basics to make shapes: Paths in Flutter: A Visual Guide
I've created the following function for drawing boxes in Cairo with rounded rectangles
void square (Context cr, int x, int y, int sizex, int sizey, int radius)
{
cr.move_to (x + radius, y);
cr.arc (x + sizex - radius, y + radius, radius, 1.5 * PI, 0);
cr.arc (x + sizex - radius, y + sizey - radius, radius, 0, 0.5 * PI);
cr.arc (x + radius, y + sizey - radius, radius, 0.5 * PI, PI);
cr.arc (x + radius, y + radius, radius, PI, 1.5 * PI);
}
This is a very C like way of doing it. I would prefer to do this in a more object orientated way. Like implementing the function as a method of Cairo.Context.
You can't add methods to existing classes without modifying the definition of that class (cairo.vapi in this case). What you can do, however, is subclass Cairo.Context and just use that instead of Cairo.Context. Something like this should do the trick:
[Compact]
public class Context : Cairo.Context {
public void square (int x, int y, int sizex, int sizey, int radius) {
this.move_to (x + radius, y);
this.arc (x + sizex - radius, y + radius, radius, 1.5 * Math.PI, 0);
this.arc (x + sizex - radius, y + sizey - radius, radius, 0, 0.5 * Math.PI);
this.arc (x + radius, y + sizey - radius, radius, 0.5 * Math.PI, Math.PI);
this.arc (x + radius, y + radius, radius, Math.PI, 1.5 * Math.PI);
}
public Context (Cairo.Surface target) {
base (target);
}
}