简体   繁体   中英

flutter CustomPaint and GestureDetector resize both to Image Size

I want to give users the chance to create a polygon with their mobile phone on a CustomPaint . For this feature there is also a GestureDetector involved. The image is being loaded from a file with the ImagePicker library. The problem here is how to resize the canvas and gesture detector to the size of the picture. Any ideas?

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 ;

}`

How it currently looks like: (You can see the canvas goes beyond. If I log the coordinates emitted by the Gesture Detector they're also above 0 even when clicking outside of the picture. The green border shows how it SHOULD be)

图像

You can clip your Canvas using canvas.clipRect() method:

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));
  }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM