I am using CustomPainter to display some figures, however there are some that go on top of others, the problem I have is that I want them to be displayed like this :
And the results is like this :
I don't know why, but to show them i have the code like this :
class _ZonePaint extends CustomPainter {
_ZonePaint({
[...]
});
[...]
#override
void paint(Canvas canvas, Size size) {
/// defines the look of the strokes
final _pencilPaint = Paint()
..strokeCap = StrokeCap.round
..color = secondaryMaterialColor.withOpacity(0.2)
..strokeWidth = 2.0
..style = PaintingStyle.stroke;
/// defines the look of the filling
Paint _fillPaint = Paint()
..color = secondaryMaterialColor.withOpacity(0.2)
..style = PaintingStyle.fill;
final Path path = Path();
for (int i = 0; i < list.length; i++) {
final pointList = list[i].pointList;
path.moveTo(
pointList[0].dx,
pointList[0].dy,
);
for (int f = 1; f < pointList.length; f++) {
path.lineTo(pointList[f].dx, pointList[f].dy);
}
path.close();
}
/// draw the zone
canvas.drawPath(path, _pencilPaint);
canvas.drawPath(path, _fillPaint);
}
#override
bool shouldRepaint(_ZonePaint oldDelegate) => false;
}
Related
I can change both ends to square or rounded using the strokeCap property but can't figure out how to apply tailored settings for each end (ie one end rounded and one square).
How can I achieve this effect?
import 'package:flutter/material.dart';
class LinePainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
var height = size.height;
var width = size.width;
var paint = Paint()
..color = Colors.red
..strokeWidth = 20
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;
var path = Path();
path.moveTo(width * 0.25, height / 2);
path.lineTo(width * 0.75, height / 2);
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
class Example extends StatefulWidget {
const Example({Key? key}) : super(key: key);
#override
State<Example> createState() => _ExampleState();
}
class _ExampleState extends State<Example> with SingleTickerProviderStateMixin {
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomPaint(
painter: LinePainter(),
child: Container(),
),
);
}
}
There is no existing PaintingStyle or StrokeCap option for setting only one cap, currently both caps are controlled the same.
If you just want the rounded cap at one end, an alternative would be to draw the line with no caps, then draw a circle overlapping the end. This would only work for solid colors though.
#override
void paint(Canvas canvas, Size size) {
var height = size.height;
var width = size.width;
var thickness = 20;
var capRadius = thickness * 0.5 ;
//Line paint, is set to stroke
var linePaint = Paint()
..color = Colors.red
..strokeWidth = thickness
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.butt; //butt is default, no caps
//Cap paint, is set to fill by default
var capPaint = Paint()
..color = Colors.red;
//Draw line
var path = Path();
path.moveTo(width * 0.25, height / 2);
path.lineTo(width * 0.75, height / 2);
canvas.drawPath(path, linePaint);
//Draw cap
canvas.drawCircle( Offset(width * 0.75, height / 2), capRadius, capPaint );
}
enter image description here
enter image description here
It looks like this. The center is supposed to be filled with white, but it's transparent.
How can I fill this marker?
I used this code. It's from this link.
I wanted to set the marker's color to black.
So I used this code.
`
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
class MarkerGenerator {
final _markerSize;
double _circleStrokeWidth = 0;
double _circleOffset = 0;
double _outlineCircleWidth = 0;
double _fillCircleWidth = 0;
double _iconSize = 0;
double _iconOffset = 0;
MarkerGenerator(this._markerSize) {
// calculate marker dimensions
_circleStrokeWidth = _markerSize / 10.0;
_circleOffset = _markerSize / 2;
_outlineCircleWidth = _circleOffset - (_circleStrokeWidth / 2);
_fillCircleWidth = _markerSize / 3;
final outlineCircleInnerWidth = _markerSize - (2 * _circleStrokeWidth);
_iconSize = sqrt(pow(outlineCircleInnerWidth, 2) / 2);
final rectDiagonal = sqrt(2 * pow(_markerSize, 2));
final circleDistanceToCorners =
(rectDiagonal - outlineCircleInnerWidth) / 2;
_iconOffset = sqrt(pow(circleDistanceToCorners, 2) / 2);
}
/// Creates a BitmapDescriptor from an IconData
Future<BitmapDescriptor> createBitmapDescriptorFromIconData(IconData iconData,
Color iconColor, Color circleColor, Color backgroundColor) async {
final pictureRecorder = PictureRecorder();
final canvas = Canvas(pictureRecorder);
// _paintCircleFill(canvas, backgroundColor);
// _paintCircleStroke(canvas, circleColor);
_paintIcon(canvas, iconColor, iconData);
final picture = pictureRecorder.endRecording();
final image =
await picture.toImage(_markerSize.round(), _markerSize.round());
final bytes = await image.toByteData(format: ImageByteFormat.png);
return BitmapDescriptor.fromBytes(bytes!.buffer.asUint8List());
}
/// Paints the icon background
void _paintCircleFill(Canvas canvas, Color color) {
final paint = Paint()
..style = PaintingStyle.fill
..color = color;
canvas.drawCircle(
Offset(_circleOffset, _circleOffset), _fillCircleWidth, paint);
}
/// Paints a circle around the icon
void _paintCircleStroke(Canvas canvas, Color color) {
final paint = Paint()
..style = PaintingStyle.stroke
..color = color
..strokeWidth = _circleStrokeWidth;
canvas.drawCircle(
Offset(_circleOffset, _circleOffset), _outlineCircleWidth, paint);
}
/// Paints the icon
void _paintIcon(Canvas canvas, Color color, IconData iconData) {
final textPainter = TextPainter(textDirection: TextDirection.ltr);
textPainter.text = TextSpan(
text: String.fromCharCode(iconData.codePoint),
style: TextStyle(
letterSpacing: 0.0,
fontSize: _markerSize,
fontFamily: iconData.fontFamily,
color: color,
));
textPainter.layout();
// textPainter.paint(canvas, Offset(_iconOffset, _iconOffset));
textPainter.paint(canvas, Offset.zero);
}
}
`
I would like to fill in the marker.
I try to make custom progress indicator with Custom painter but painter is not updating the value
help:
can you help me to use custom painter?
also can we change circle pointer to square box with arrow pointing to progress
class MyCsPaint extends CustomPainter {
MyCsPaint({required this.progress});
final double progress;
#override
void paint(Canvas canvas, Size size) {
Paint paintBg = Paint()
..color = Colors.teal.shade100
..strokeWidth = 1
..strokeCap = StrokeCap.round;
Paint paint = Paint()
..color = Colors.teal
..strokeWidth = 5
..strokeCap = StrokeCap.round;
Offset p1bg = Offset(size.width, size.height);
Offset p2bg = Offset(0, size.height);
Offset p2 = Offset(0, size.height);
Offset p1 = Offset(size.width * progress, size.height);
canvas.drawLine(p1bg, p2bg, paintBg);
canvas.drawLine(p1, p2, paint);
Offset pc = Offset(size.width * progress, size.height * .7);
canvas.drawCircle(pc, 10, paint);
final textStyle = ui.TextStyle(
color: Colors.black,
fontSize: 16,
);
final paragraphStyle = ui.ParagraphStyle(
textDirection: TextDirection.ltr,
);
final paragraphBuilder = ui.ParagraphBuilder(paragraphStyle)
..pushStyle(textStyle)
..addText('${progress * 100} %');
final constraints = ui.ParagraphConstraints(width: size.width);
final paragraph = paragraphBuilder.build();
paragraph.layout(constraints);
canvas.drawParagraph(paragraph, pc);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
I'm making a whiteboard app like the image below.
After drawing the shape, I want to move the drawn shape and line.
The rectangle and circles can be used with containers() to determine if the shape contains the points currently being touched.
But sometimes it doesn't work out. (Clicking inside the shape does not confirm that the point being touched is included)
And in the case of lines, I don't know how to make sure that the points being touched are included.(I only use two points when drawing a line, and I don't know how to check if the touched point is between these two points.)
The following is part of the code for this.
List<Rectangle>? _rects;
List<Circle>? _circles;
List<Line>? _lines;
List<ArrowLine>? _arrowLines;
int _selectedRectIndex = -1;
int _selectedCircleIndex = -1;
int _selectedLineIndex = -1;
int _selectedArrowLineIndex = -1;
// ===================
return GestureDetector(
onScaleStart: (details) {
Tool tool = context.read<DrawProvider>().tool;
RenderBox box = context.findRenderObject() as RenderBox;
Offset point = box.globalToLocal(details.focalPoint);
// pointer
switch (tool) {
case Tool.pointer:
_rects = [];
_circles = [];
_lines = [];
// save shape list
for (shape in shapes) {
if (shape is Rectangle) {
_rects!.add(shape as Rectangle);
} else if (shape is Circle) {
_circles!.add(shape as Circle);
} else if (shape is Line) {
_lines!.add(shape as Line);
}
}
// check seleted
final selectedRectIndex =
_rects!.lastIndexWhere((rect) => rect.rect!.contains(point));
final selectedCircleIndex = _circles!
.lastIndexWhere((rect) => rect.rect!.contains(point));
// TODO check line selete
// rectangle
if (selectedRectIndex != -1) {
onRectangleSelected(selectedRectIndex);
_seletedType = SeletedType.rectangle;
}
// circle
else if (selectedCircleIndex != -1) {
onCircleSelected(selectedCircleIndex);
_seletedType = SeletedType.circle;
} else {
_seletedType = SeletedType.none;
logger.d("shapeIndex: $_selectedCircleIndex");
}
break;
default:
}
}
},
onScaleUpdate: (details) {
Tool tool = context.read<DrawProvider>().tool;
RenderBox box = context.findRenderObject() as RenderBox;
Offset point = box.globalToLocal(details.focalPoint);
switch (tool) {
case Tool.pointer:
// move shapes
// rectangle
if (_seletedType == SeletedType.rectangle) {
final newLeft = point.dx;
final newTop = point.dy;
final newRight = point.dx + _seletedRectWidth!;
final newBottom = point.dy + _seletedRectHeight!;
_rects![_selectedRectIndex].rect =
Rect.fromLTRB(newLeft, newTop, newRight, newBottom);
}
// circle
else if (_seletedType == SeletedType.circle) {
final newLeft = point.dx;
final newTop = point.dy;
final newRight = point.dx + _seletedCircleWidth!;
final newBottom = point.dy + _seletedCircleHeight!;
// 이동은 되지만 이상하게 보임!
_circles![_selectedCircleIndex].rect =
Rect.fromLTRB(newLeft, newTop, newRight, newBottom);
}
// TODO line
else {
return;
}
break;
},
},
// ==============
void onRectangleSelected(int index) {
_selectedRectIndex = index;
}
void onCircleSelected(int index) {
_selectedCircleIndex = index;
}
void onLineSelected(int index) {
_selectedLineIndex = index;
}
void onArrowLineSelected(int index) {
_selectedArrowLineIndex = index;
}
shape and customPainter
class Shape {} // Origin class for all the shapes
// Rectangle
class Rectangle extends Shape {
Rect? rect;
double? left, top;
/* Because of the implementation left and top values
have to be saved inside of a Rectangle */
final paint = Paint();
Rectangle(Color color, double width) {
this.paint
..color = color
..isAntiAlias = true
..strokeCap = StrokeCap.round
..strokeWidth = width
..style = PaintingStyle.stroke; // no fill
}
}
// A straight section defined by 2 points
class Line extends Shape {
Offset? p1, p2;
final paint = Paint();
Line(Color color, double width) {
this.paint
..color = color
..isAntiAlias = true
..strokeCap = StrokeCap.round
..strokeWidth = width
..style = PaintingStyle.stroke; // no fill
}
}
class ArrowLine extends Shape {
// sketche에서 화살표 추가
Offset? p1, p2;
final paint = Paint();
ArrowLine(Color color, double width) {
this.paint
..color = color
..isAntiAlias = true
..strokeCap = StrokeCap.round
..strokeWidth = width;
}
}
class Circle extends Shape {
Rect? rect;
double? left, top;
Size? size; // test
final paint = Paint();
Circle(Color color, double width, Size size) {
this.paint
..color = color
..isAntiAlias = true
..strokeCap = StrokeCap.round
..strokeWidth = width
..style = PaintingStyle.stroke; // no fill
}
}
// ===================
class Sketcher extends CustomPainter {
final List<Shape> shapes;
Sketcher(this.shapes) : super();
#override
void paint(Canvas canvas, Size size) {
for (var shape in shapes) {
if (shape is Rectangle) {
canvas.drawRect(shape.rect!, shape.paint);
} else if (shape is Circle) {
canvas.drawOval(shape.rect!, shape.paint);
} else if (shape is Line) {
canvas.drawLine(shape.p1!, shape.p2!, shape.paint);
}
}
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
May I know why the shape is sometimes not selected and how to check if the point is between two points on the line?
I am trying to make a box like this. If I press on the "Product" button, then I need to show a box like this. But I cannot make it. I tried both clippath and custom paint. I can use visibility widget to show and hide – this is not problem. The problem is that I need to show a box like the picture I added here.
You can do that with CustomPainter. this is an example:
class LinePainter extends CustomPainter {
final double progress;
LinePainter({this.progress});
Paint _paint = Paint()
..color = Colors.black
..strokeWidth = 4.0
..style = PaintingStyle.stroke
..strokeJoin = StrokeJoin.round;
#override
void paint(Canvas canvas, Size size) {
var path = Path();
path.moveTo(0, size.height / 2);
path.lineTo(size.width * progress, size.height / 2);
canvas.drawPath(path, _paint);
}
#override
bool shouldRepaint(LinePainter oldDelegate) {
return oldDelegate.progress != progress;
}
}
for more information you can see this page.