I'm using path drawing. I must scale that path. I'm using bounds for scale but It doesn't work same for clipper and painter. I need same output.
Here is codes.
class PaintSvg extends CustomPainter{
PaintSvg(this.pathStr);
String pathStr;
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint();
paint.style = PaintingStyle.fill;
paint.color = Colors.red;
Path path = Path();
path = parseSvgPathData(pathStr);
Rect pathBounds = path.getBounds();
Matrix4 matrix4 = Matrix4.identity();
matrix4.scale(size.width/pathBounds.width, size.height/pathBounds.height);
path = path.shift(Offset(0,0));
path = path.transform(matrix4.storage);
canvas.drawShadow(path, Colors.green, 2, true);
//canvas.rotate(-math.pi/1.18);
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
class ClipSvg extends CustomClipper<Path>{
String pathStr;
ClipSvg(this.pathStr);
#override
Path getClip(Size size) {
Path path = Path();
path = parseSvgPathData(pathStr);
Rect pathBounds = path.getBounds();
Matrix4 matrix4 = Matrix4.identity();
matrix4.scale(size.width/pathBounds.width, size.height/pathBounds.height);
path = path.transform(matrix4.storage);
path = path.shift(Offset(0,0));
return path;
}
#override
bool shouldReclip(ClipSvg oldClipper) => true;
}
Here is output.
ScreenShot of output
Brown one is clipper red one that behind the brown is painter. Two of them uses same svg data string.
Related
I'm trying to draw this kind of shape with flutter:
Expected result
So far I can draw an arc using drawArc():
class CurvePainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
var paint = Paint();
paint.color = Colors.green;
paint.style = PaintingStyle.fill;
paint.strokeWidth = 5;
final rect = Rect.fromLTRB(50, 100, 130, 200);
final startAngle = -pi;
final sweepAngle = pi;
canvas.drawArc(rect, startAngle, sweepAngle, false, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
But is that the right way to do it or should I use quadraticBezierTo and drawPath?
Help me in drawing the zigZag line/border as show in the image at bottom. I found a zigzag paint function in flutter doc https://api.flutter.dev/flutter/painting/paintZigZag.html but not sure how to use it.
You need to use CustomPaint() Widget.
Create your own CustomerPainter, say MyPainter, and place this in your widget tree:
CustomPaint(
size: MediaQuery.of(context).size,
painter: MyPainter(),
),
The class MyPainter should extend CustomPainter and also should override the paint() method which is where you specify the path to be painted on the canvas.
So you can just put the above 'paintZigZag' code there or call it like below along with appropriate parameters.
import 'package:flutter/material.dart';
import 'dart:math' as math;
class MyPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
var paint = Paint();
paint.color = Colors.blue;
paint.style = PaintingStyle.fill;
paintZigZag(canvas, paint, Offset(0, 100), Offset(200, 100), 100, 5);
}
void paintZigZag(Canvas canvas, Paint paint, Offset start, Offset end,
int zigs, double width) {
assert(zigs.isFinite);
assert(zigs > 0);
canvas.save();
canvas.translate(start.dx, start.dy);
end = end - start;
canvas.rotate(math.atan2(end.dy, end.dx));
final double length = end.distance;
final double spacing = length / (zigs * 2.0);
final Path path = Path()..moveTo(0.0, 0.0);
for (int index = 0; index < zigs; index += 1) {
final double x = (index * 2.0 + 1.0) * spacing;
final double y = width * ((index % 2.0) * 2.0 - 1.0);
path.lineTo(x, y);
}
path.lineTo(length, 0.0);
canvas.drawPath(path, paint);
canvas.restore();
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
I needed a loading widget that draws the moving sine and cosine functions into a canvas. I coded it with no problem using a CustomPaint widget and a CustomPainter, but when I profile it, i Have discovered it runs on about 49fps, and not on 60fps. The UI thread is working good, taking about 6ms for each frame, but the Raster thread is taking longer. I have tried painting less points on the canvas (doing i=i+5 instead of i++ on the for loop), but the result is quite the same.
¿Can somebody suggest me an idea on how could I improve the performance?. The widget code is below, and so is the DevTools screenshot of what the Raster thread is doing in every frame, in case it can be useful.
import 'dart:math';
import 'package:flutter/material.dart';
class LoadingChart extends StatefulWidget{
final Color color1;
final Color color2;
final double lineWidth;
final bool line;
final Size size;
const LoadingChart({
#required this.color1,
#required this.color2,
#required this.size,
#required this.lineWidth,
this.line = true,
Key key
}): super(key: key);
#override
State<StatefulWidget> createState() => _LoadingChartState();
}
class _LoadingChartState extends State<LoadingChart>
with SingleTickerProviderStateMixin{
AnimationController _controller;
double randomHeight(Random random, double max){
return random.nextDouble()*max;
}
#override
void initState() {
_controller = AnimationController(vsync: this, duration: Duration(seconds: 1));
_controller.addListener(() {setState(() {});});
_controller.repeat();
super.initState();
}
#override
void dispose(){
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SizedBox(
height: widget.size.height,
width: widget.size.width,
child: CustomPaint(
painter: PathPainter(
color1: widget.color1,
color2: widget.color2,
value: _controller.value,
line: widget.line,
),
)
);
}
}
class PathPainter extends CustomPainter {
final Color color1;
final Color color2;
final double lineWidth;
final bool line;
final double value;
PathPainter({
#required this.value,
this.color1=Colors.red,
this.color2=Colors.green,
this.line = true,
this.lineWidth=4.0,
}): super();
#override
void paint(Canvas canvas, Size size) {
final height = size.height;
final width = size.width;
Paint paint1 = Paint()
..color = color1
..style = PaintingStyle.stroke
..strokeWidth = lineWidth;
Paint paint2 = Paint()
..color = color2
..style = PaintingStyle.stroke
..strokeWidth = lineWidth;
Path path1 = Path();
Path path2 = Path();
/* If line is true, draw sin and cos functions, otherwise, just some points */
for (double i = 0; i < width; i=i+5){
double f = i*2*pi/width + 2*pi*value;
double g = i*2*pi/width - 2*pi*value;
if (i == 0){
path1.moveTo(0, height/2 + height/6*sin(f));
path2.moveTo(0, height/2 + height/6*cos(g));
continue;
}
path1.lineTo(i, height/2 + height/6*sin(f));
path2.lineTo(i, height/2 + height/6*cos(g));
}
/* Draw both lines */
canvas.drawPath(path1, paint1);
canvas.drawPath(path2, paint2);
}
#override
bool shouldRepaint(PathPainter oldDelegate) {
return oldDelegate.value != value || oldDelegate.color1 != color1
|| oldDelegate.color2 != color2 || oldDelegate.line != line
|| oldDelegate.lineWidth != lineWidth;
}
}
PS: I'm running the app on profile mode so that shouldn't be the problem. Also I wanted to mention that it's the only widget being redrawn on the screen.
Thanks a lot!!
CustomPainter can receive a listenable so maybe you can use the animation controller there to update it with every tick
class _LoadingChartState extends State<LoadingChart>
with SingleTickerProviderStateMixin{
AnimationController _controller;
double randomHeight(Random random, double max){
return random.nextDouble()*max;
}
#override
void initState() {
_controller = AnimationController(vsync: this, duration: Duration(seconds: 1));
//_controller.addListener(() {setState(() {});}); no need to setState
_controller.repeat();
super.initState();
}
#override
void dispose(){
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SizedBox(
height: widget.size.height,
width: widget.size.width,
child: CustomPaint(
willChange: true, //this can help (Whether the raster cache should be told that this painting is likely)
painter: PathPainter(
color1: widget.color1,
color2: widget.color2,
line: widget.line,
listenable: _controller //pass the controller as it is (An animationController extends a Listenable)
),
)
);
}
}
And in PathPainter you give to the constructor the listenable and pass it to the CustomPainter constructor that accepts a listenable called repaint
class PathPainter extends CustomPainter {
final Animation listenable;
final Color color1;
final Color color2;
final double lineWidth;
final bool line;
PathPainter({
this.listenable,
this.color1=Colors.red,
this.color2=Colors.green,
this.line = true,
this.lineWidth=4.0,
}): super(repaint: listenable); //don't forget calling the CustomPainter constructor with super
#override
void paint(Canvas canvas, Size size) {
double value = listenable.value; // get its value here
final height = size.height;
final width = size.width;
Paint paint1 = Paint()
..color = color1
..style = PaintingStyle.stroke
..strokeWidth = lineWidth;
Paint paint2 = Paint()
..color = color2
..style = PaintingStyle.stroke
..strokeWidth = lineWidth;
Path path1 = Path();
Path path2 = Path();
/* If line is true, draw sin and cos functions, otherwise, just some points */
for (double i = 0; i < width; i=i+5){
double f = i*2*pi/width + 2*pi*value;
double g = i*2*pi/width - 2*pi*value;
if (i == 0){
path1.moveTo(0, height/2 + height/6*sin(f));
path2.moveTo(0, height/2 + height/6*cos(g));
continue;
}
path1.lineTo(i, height/2 + height/6*sin(f));
path2.lineTo(i, height/2 + height/6*cos(g));
}
/* Draw both lines */
canvas.drawPath(path1, paint1);
canvas.drawPath(path2, paint2);
}
#override
bool shouldRepaint(PathPainter oldDelegate) {
//delete the oldDelegate.value, it doesn't exists anymore
return oldDelegate.color1 != color1
|| oldDelegate.color2 != color2 || oldDelegate.line != line
|| oldDelegate.lineWidth != lineWidth;
}
}
I'm in debug mode so I expect you get a better performance in profile mode
MyPainter class
class MyPainter extends CustomPainter {
Paint _paint;
**Constructor**
MyPainter(){
_paint = Paint()
..color = Colors.indigo;
}
#override
void paint(Canvas canvas, Size size){
draw one rectangle (size, color, style)
var rect1 = Rect.fromLTWH(0, 0, 20.0,20.0);
color
_paint.color = Color(0xffACCAF6);
style
_paint.style = PaintingStyle.fill;
draw rectangle
canvas.drawRect(rect1, _paint);
}
bool shouldRepaint
#override
bool shouldRepaint(CustomPainter oldDelegate)
return oldDelegate != this;
}
}
img
I am trying to get screen size in flutter inside a custom class which donot have build method in it. How can i get screen size without using buildcontext class?
The following code :
class ShapesPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
BuildContext context;
double width = MediaQuery.of(context).size.width;
double height = MediaQuery.of(context).size.height;
final paint = Paint();
paint.color = Colors.deepOrange;
var center = Offset(size.width / 2, size.height / 2);
print(height);
print(width);
Rect rect = Rect.fromLTWH(0.0, 0.0, width, height);
canvas.drawRect(rect, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
gives following error :
The following assertion was thrown during paint():
'package:flutter/src/widgets/media_query.dart': Failed assertion: line
689 pos 12: 'context != null': is not true.
use
MediaQueryData.fromWindow(WidgetsBinding.instance.window);
you can use it to get MediaQuery without need context
it depend on instance of window to get size information
You can directly pass the screen's width and height as a parameter for the widget ShapesPainter if that is all what you need.
Solution Code:
class ShapesPainter extends CustomPainter {
final double width;
final double height;
ShapesPainter({this.width,this.height});
#override
void paint(Canvas canvas, Size size) {
final paint = Paint();
paint.color = Colors.deepOrange;
var center = Offset(size.width / 2, size.height / 2);
print(height);
print(width);
Rect rect = Rect.fromLTWH(0.0, 0.0, width, height);
canvas.drawRect(rect, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
Usage:
// Wherever you'll be using it
ShapesPainter(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
)
In the following way, you can get the device screen size without declaring them in the build method.
void paint(BuildContext context)
{
double width = MediaQuery.of(context).size.width;
double height = MediaQuery.of(context).size.height;
}
And you can access them inside other file build methods after importing this method.
paint(context)