使用 CustomPainter 和 InteractiveViewer 在容器中进行 Flutter 绘图,但无法正确显示
Flutter drawing in a container with CustomPainter and InteractiveViewer and not displaying it properly
自从我将我的项目迁移到2.8版本并使用了null-safety后,我遇到了这个错误,我不知道如何解决;下面我放视频
我的红色容器尺寸为 1700 x 1200,我向 CustomPainter 发送了相同的尺寸,但是在绘图时,它似乎采用了我的 phone 的尺寸,因为当我缩放它时(这就是我使用 Interactive 的原因, 缩放),它仍然具有相同的尺寸(我不知道它是什么)。
DrawnShape? currentShape; //My object
return InteractiveViewer(
/// * START
onInteractionStart: (details) {
currentShape = DrawnShape(
pointList: [],
paint: Paint()
..strokeCap = StrokeCap.round
..isAntiAlias = true
..color = Colors.black
..strokeWidth = 3.0
..style = PaintingStyle.stroke,
type: 1,
id: Uuid(),
);
},
/// * UPDATE
onInteractionUpdate: (details) {
currentShape.pointList.add(details.localFocalPoint);
// Some Bloc that returns a List<DrawnShape> in a DrawUpdate state
// and then go to the Class MyCanvas
BlocProvider.of<DrawBloc>(context).add(AddShapeEvent(
currentShape,
));
},
/// * END
onInteractionEnd: (details) {
currentShape = null;
},
constrained: false,
boundaryMargin: EdgeInsets.all(1200 * 0.2);,
maxScale: 5,
minScale: 0.1,
// * STACK
child: Stack(
children: [
SizedBox(
height: 1700,
width: 1200,
child: Container(
decoration: BoxDecoration(
color: Colors.red,
border: new Border.all(
color: Colors.grey, width: 1.5, style: BorderStyle.solid),
),
child: Text(""),
),
);
// * CANVAS TO DRAW ON
IgnorePointer(
ignoring: true,
child: MyCanvas(
height: 1700,
width: 1200,
),
),
],
),
);
class MyCanvas extends StatelessWidget {
final double height;
final double width;
const MyCanvas({
Key? key,
this.height,
this.width,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final canvasSize = Size(width, height);
return ClipRect(
// * CONSUMER
child: BlocConsumer<DrawBloc, DrawBlocState>(
listener: (context, state) {},
builder: (context, state) {
// * DRAWING UPDATED
if (state is DrawUpdate) {
// * CUSTOM PAINT
return CustomPaint(
isComplex: true,
size: canvasSize,
// * DRAWING PAINTER
painter: DrawingPainter(
state.lineList,
),
} else {
return Container();
}
},
),
);
}
}
//===========================================================================
// * DRAWING PAINTER (canvas to paint on)
//
class DrawingPainter extends CustomPainter {
DrawingPainter(
this.shapeList,
);
final List<DrawnShape> shapeList;
@override
void paint(Canvas canvas, Size size) {
canvas.saveLayer(Rect.fromLTWH(0, 0, size.width, size.height), Paint());
for (int i = 0; i < shapeList.length; i++) {
final currentShape = shapeList[i];
switch (currentShape.type) {
// case ActionType.RECTANGLE:
// this._drawRectangle(canvas, currentShape);
// break;
// case ActionType.CIRCLE:
// this._drawCircle(canvas, currentShape);
// break;
// case ActionType.CLOUD:
// this._drawCloud(canvas, currentShape);
// break;
default:
this._drawFromPointList(canvas, currentShape); // THE IMPORTANT
break;
}
}
canvas.restore();
}
@override
bool shouldRepaint(DrawingPainter oldDelegate) => true;
//===========================================================================
// POINT LIST
//
void _drawFromPointList(Canvas canvas, DrawnShape currentShape) {
final Path path = Path();
final pointList = currentShape.pointList;
/// starts at first point
path.moveTo(
pointList[0].dx,
pointList[0].dy,
);
/// goes through every point of the line, (starts at 1)
for (int f = 1; f < pointList.length; f++)
path.lineTo(pointList[f].dx, pointList[f].dy);
/// draw the line
canvas.drawPath(path, currentShape.paint);
}
}
看起来线条画的不错,但是我想画我手指的地方。
我以前做过,我可以建议我当时的做法。
首先我会使用 GestureDetector
而不是 InterActiveViewer
。使用它的 onPanUpdate
方法可以像这样添加偏移量,
setState(() {
RenderBox object = context.findRenderObject();
Offset _localPosition =
object.globalToLocal(details.globalPosition);
offsets = List.from(offsets)..add(_localPosition);
});
offsets 是您用来绘制的主要偏移变量。
我在自定义画家中使用的for循环是这样的,
for (int i = 0; i < offsets.length - 1; i++) {
if (offsets[i] != null && offsets[i + 1] != null) {
canvas.drawLine(offsets[i], offsets[i + 1], paint);
}
}
remove your code for starts at first point
或者你可以使用这个包perfect_freehand
好的,我在不更改小部件的情况下更正了我的错误,我唯一做的就是创建一个 InteractiveViewer 控制器,然后我就这样得到了一般偏移量:
final scenePoint =
_transformationController.toScene(details.localFocalPoint);
自从我将我的项目迁移到2.8版本并使用了null-safety后,我遇到了这个错误,我不知道如何解决;下面我放视频
我的红色容器尺寸为 1700 x 1200,我向 CustomPainter 发送了相同的尺寸,但是在绘图时,它似乎采用了我的 phone 的尺寸,因为当我缩放它时(这就是我使用 Interactive 的原因, 缩放),它仍然具有相同的尺寸(我不知道它是什么)。
DrawnShape? currentShape; //My object
return InteractiveViewer(
/// * START
onInteractionStart: (details) {
currentShape = DrawnShape(
pointList: [],
paint: Paint()
..strokeCap = StrokeCap.round
..isAntiAlias = true
..color = Colors.black
..strokeWidth = 3.0
..style = PaintingStyle.stroke,
type: 1,
id: Uuid(),
);
},
/// * UPDATE
onInteractionUpdate: (details) {
currentShape.pointList.add(details.localFocalPoint);
// Some Bloc that returns a List<DrawnShape> in a DrawUpdate state
// and then go to the Class MyCanvas
BlocProvider.of<DrawBloc>(context).add(AddShapeEvent(
currentShape,
));
},
/// * END
onInteractionEnd: (details) {
currentShape = null;
},
constrained: false,
boundaryMargin: EdgeInsets.all(1200 * 0.2);,
maxScale: 5,
minScale: 0.1,
// * STACK
child: Stack(
children: [
SizedBox(
height: 1700,
width: 1200,
child: Container(
decoration: BoxDecoration(
color: Colors.red,
border: new Border.all(
color: Colors.grey, width: 1.5, style: BorderStyle.solid),
),
child: Text(""),
),
);
// * CANVAS TO DRAW ON
IgnorePointer(
ignoring: true,
child: MyCanvas(
height: 1700,
width: 1200,
),
),
],
),
);
class MyCanvas extends StatelessWidget {
final double height;
final double width;
const MyCanvas({
Key? key,
this.height,
this.width,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final canvasSize = Size(width, height);
return ClipRect(
// * CONSUMER
child: BlocConsumer<DrawBloc, DrawBlocState>(
listener: (context, state) {},
builder: (context, state) {
// * DRAWING UPDATED
if (state is DrawUpdate) {
// * CUSTOM PAINT
return CustomPaint(
isComplex: true,
size: canvasSize,
// * DRAWING PAINTER
painter: DrawingPainter(
state.lineList,
),
} else {
return Container();
}
},
),
);
}
}
//===========================================================================
// * DRAWING PAINTER (canvas to paint on)
//
class DrawingPainter extends CustomPainter {
DrawingPainter(
this.shapeList,
);
final List<DrawnShape> shapeList;
@override
void paint(Canvas canvas, Size size) {
canvas.saveLayer(Rect.fromLTWH(0, 0, size.width, size.height), Paint());
for (int i = 0; i < shapeList.length; i++) {
final currentShape = shapeList[i];
switch (currentShape.type) {
// case ActionType.RECTANGLE:
// this._drawRectangle(canvas, currentShape);
// break;
// case ActionType.CIRCLE:
// this._drawCircle(canvas, currentShape);
// break;
// case ActionType.CLOUD:
// this._drawCloud(canvas, currentShape);
// break;
default:
this._drawFromPointList(canvas, currentShape); // THE IMPORTANT
break;
}
}
canvas.restore();
}
@override
bool shouldRepaint(DrawingPainter oldDelegate) => true;
//===========================================================================
// POINT LIST
//
void _drawFromPointList(Canvas canvas, DrawnShape currentShape) {
final Path path = Path();
final pointList = currentShape.pointList;
/// starts at first point
path.moveTo(
pointList[0].dx,
pointList[0].dy,
);
/// goes through every point of the line, (starts at 1)
for (int f = 1; f < pointList.length; f++)
path.lineTo(pointList[f].dx, pointList[f].dy);
/// draw the line
canvas.drawPath(path, currentShape.paint);
}
}
看起来线条画的不错,但是我想画我手指的地方。
我以前做过,我可以建议我当时的做法。
首先我会使用 GestureDetector
而不是 InterActiveViewer
。使用它的 onPanUpdate
方法可以像这样添加偏移量,
setState(() {
RenderBox object = context.findRenderObject();
Offset _localPosition =
object.globalToLocal(details.globalPosition);
offsets = List.from(offsets)..add(_localPosition);
});
offsets 是您用来绘制的主要偏移变量。
我在自定义画家中使用的for循环是这样的,
for (int i = 0; i < offsets.length - 1; i++) {
if (offsets[i] != null && offsets[i + 1] != null) {
canvas.drawLine(offsets[i], offsets[i + 1], paint);
}
}
remove your code for starts at first point
或者你可以使用这个包perfect_freehand
好的,我在不更改小部件的情况下更正了我的错误,我唯一做的就是创建一个 InteractiveViewer 控制器,然后我就这样得到了一般偏移量:
final scenePoint =
_transformationController.toScene(details.localFocalPoint);