我想让用户有机会在CustomPaint
上用手机创建多边形。对于此功能,还涉及一个GestureDetector
。正在使用ImagePicker
库从文件中加载图像。这里的问题是如何将画布和手势检测器的大小调整为图片的大小。有什么想法吗?
Code: `import 'dart:io';
import 'dart:math';
import 'dart:ui' as ui;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:image_picker/image_picker.dart';
import 'package:listassist/services/auth.dart';
class PictureShow extends StatefulWidget {
@override
_PictureShowState createState() => _PictureShowState();
}
class _PictureShowState extends State<PictureShow> {
ui.Image _image;
Image _imageWidget;
List<ui.Offset> _points = [ui.Offset(90, 120), ui.Offset(90, 370), ui.Offset(320, 370), ui.Offset(320, 120)];
bool _clear = false;
int _currentlyDraggedIndex = -1;
@override
Widget build(BuildContext context) {
final AppBar appBar = AppBar(
backgroundColor: Theme.of(context).colorScheme.primary,
title: Text("Rechungserkennung"),
);
return Scaffold(
floatingActionButton:
FloatingActionButton(
child: Icon(Icons.clear),
onPressed: () {
setState(() {
_clear = true;
_points = [];
});
}
),
appBar: appBar,
backgroundColor: _imageWidget != null ? Colors.black : Colors.white,
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
if (_imageWidget == null) ...[
FlatButton(
onPressed: () => _pickImage(ImageSource.camera),
color: Colors.blueAccent,
padding: EdgeInsets.all(40.0),
child: Column(
children: <Widget>[
Icon(Icons.camera_alt, color: Colors.white,),
Text("Aus der Kamera", style: TextStyle(color: Colors.white),)
],
),
),
Padding(
padding: const EdgeInsets.only(top: 40, bottom: 40),
child: Text("oder", textScaleFactor: 2,),
),
FlatButton(
onPressed: () => _pickImage(ImageSource.gallery),
color: Colors.brown,
padding: EdgeInsets.all(40.0),
child: Column(
children: <Widget>[
Icon(Icons.photo, color: Colors.white,),
Text("Aus der Gallerie", style: TextStyle(color: Colors.white),)
],
),
),
],
if (_imageWidget != null) ...[
GestureDetector(
onPanStart: (DragStartDetails details) {
// get distance from points to check if is in circle
int indexMatch = -1;
for (int i = 0; i < _points.length; i++) {
double distance = sqrt(pow(details.localPosition.dx - _points[i].dx, 2) + pow(details.localPosition.dy - _points[i].dy, 2));
if (distance <= 30) {
indexMatch = i;
break;
}
}
if (indexMatch != -1) {
_currentlyDraggedIndex = indexMatch;
}
},
onPanUpdate: (DragUpdateDetails details) {
if (_currentlyDraggedIndex != -1) {
setState(() {
_points = List.from(_points);
_points[_currentlyDraggedIndex] = details.localPosition;
});
}
},
onPanEnd: (_) {
setState(() {
_currentlyDraggedIndex = -1;
});
},
child: CustomPaint(
size: Size.fromHeight(MediaQuery.of(context).size.height - appBar.preferredSize.height),
painter: RectanglePainter(points: _points, clear: _clear, image: _image),
),
)
]
],
),
);
}
Future _pickImage(ImageSource imageSource) async {
try {
File imageFile = await ImagePicker.pickImage(source: imageSource);
ui.Image finalImg = await _load(imageFile.path);
setState(() {
_imageWidget = Image.file(imageFile);
_image = finalImg;
});
} on Exception {
ResultHandler
.showInfoSnackbar(Text("Ein Fehler ist aufgetreten beim Öffnen des Bildes. Die App benötigt Zugriff auf die Galerie"));
}
}
Future<ui.Image> _load(String asset) async {
ByteData data = await rootBundle.load(asset);
ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
ui.FrameInfo fi = await codec.getNextFrame();
return fi.image;
}
}
class RectanglePainter extends CustomPainter {
List<Offset> points;
bool clear;
final ui.Image image;
RectanglePainter({@required this.points, @required this.clear, @required this.image});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.red
..strokeCap = StrokeCap.square
..style = PaintingStyle.fill
..strokeWidth = 2;
final outputRect = Rect.fromPoints(ui.Offset.zero, ui.Offset(size.width, size.height));
final Size imageSize = Size(image.width.toDouble(), image.height.toDouble());
final FittedSizes sizes = applyBoxFit(BoxFit.contain, imageSize, outputRect.size);
final Rect inputSubrect = Alignment.center.inscribe(sizes.source, Offset.zero & imageSize);
final Rect outputSubrect = Alignment.center.inscribe(sizes.destination, outputRect);
canvas.drawImageRect(image, inputSubrect, outputSubrect, paint);
if (!clear) {
final circlePaint = Paint()
..color = Colors.red
..strokeCap = StrokeCap.square
..style = PaintingStyle.fill
..blendMode = BlendMode.multiply
..strokeWidth = 2;
for (int i = 0; i < points.length; i++) {
if (i + 1 == points.length) {
canvas.drawLine(points[i], points[0], paint);
} else {
canvas.drawLine(points[i], points[i + 1], paint);
}
canvas.drawCircle(points[i], 10, circlePaint);
}
}
}
@override
bool shouldRepaint(RectanglePainter oldPainter) => oldPainter.points != points || clear ;
}`
当前的外观:(您可以看到画布超出了范围。如果我记录了手势检测器发出的坐标,即使单击图片外部,它们也都位于0上方。绿色边框显示应该如何)
答案 0 :(得分:1)
您可以使用Canvas
方法裁剪canvas.clipRect()
:
class RectanglePainter extends CustomPainter {
//...
RectanglePainter({@required this.points, @required this.clear, @required this.image});
@override
void paint(Canvas canvas, Size size) {
//...
canvas.clipRect(Rect.fromLTWH(0, 0, size.width, size.height));
}
}