6

This is how I show a picture:

return Scaffold(
   body: Center(
     child: Image.asset('man_face.jpg'),
   ),
);

And this the result: https://i.stack.imgur.com/eHHjx.jpg

I want to show only special part of the picture. For example a rectangle with x: 250 and y: 360 and width: 200 and height: 150.

Which it should be something like this: https://i.stack.imgur.com/W56nM.jpg

How can I do that?

Alireza Beitari
  • 436
  • 4
  • 15

4 Answers4

3

you might want to look into this library. brendan-duncan/image. a great tool to manipulate images in flutter.

Star Lut
  • 416
  • 6
  • 11
  • How about using a Clipper?! – Alireza Beitari Jan 24 '19 at 12:57
  • i think it will do the as well. found a guide about it a few days back. you can read it if you want [clipping in flutter](https://medium.com/flutter-community/clipping-in-flutter-e9eaa6b1721a) – Star Lut Jan 24 '19 at 13:01
  • @hhhal hope you got your answer – Star Lut Jan 24 '19 at 13:13
  • My problem is that I cannot make the clipped part, fit for width. This is the result: [link](https://imgur.com/a/1V69yX6) And this is my code: [link](https://gist.github.com/AliRezaBeitari/eedaf16d6dc1b5fc0fa4d5b70e1e8dc3) – Alireza Beitari Jan 26 '19 at 07:46
  • did you follow the link? @hhhal also, follow the library i posted. it gets you the desired cropping. – Star Lut Jan 26 '19 at 08:35
  • I am not using `brendan-duncan/image` library. I wanted to use ClipRect instead! – Alireza Beitari Jan 26 '19 at 08:56
  • Now the main problem is, how to make a cropped widget center?! – Alireza Beitari Jan 26 '19 at 08:57
  • use the image widget inside any column/row, so that you can use the mainAxisAlignment property. i am not sure which other widget has the same property. or simply use the widget inside Aling(). @hhhal – Star Lut Jan 26 '19 at 09:45
  • or you can use a boolean value and after recropping call setState and set the boolean value to true inside setState(). then in build method check the boolean value and set it accordingly. – Star Lut Jan 26 '19 at 10:00
  • 1
    No, it didn't work. What about using a combination of `Transform.scale` and `Transform.translate` to do so?! This is my code: [link](https://gist.github.com/AliRezaBeitari/b3f20c548895f06449e8f67e35eea041). I have hardcoded those `209` and `180` coordinates of `Transform.translate` offset. The `x` and `y` should be dynamic and by changing does, the cropped area position doesn't work anymore. How can I fix that?! Thanks for answering. – Alireza Beitari Jan 26 '19 at 11:13
  • i believe what you are looking for is similar to this question [here](https://stackoverflow.com/questions/50989513/how-do-i-crop-images-in-flutter) @hhhal – Star Lut Jan 26 '19 at 14:10
  • or just go and see the library i provide, it will give you the cropped image in Image format, later on you can load the image with any image loading widget, which will achieve the best result. – Star Lut Jan 26 '19 at 14:13
2

Here is a code I came up with

it accept an image url and a rect and display only the rect part of the image

import 'dart:async';

import 'package:flutter/material.dart';
import 'dart:ui' as ui;

class PartImagePainter extends StatefulWidget {
  String imageUrl;
  Rect rect;

  PartImagePainter(this.imageUrl, this.rect);

  @override
  _PartImagePainterState createState() => _PartImagePainterState();
}

class _PartImagePainterState extends State<PartImagePainter> {
  Future<ui.Image> getImage(String path) async {
    Completer<ImageInfo> completer = Completer();
    var img = new NetworkImage(path);
    img
        .resolve(ImageConfiguration())
        .addListener(ImageStreamListener((ImageInfo info, bool _) {
      completer.complete(info);
    }));
    ImageInfo imageInfo = await completer.future;
    return imageInfo.image;
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: getImage(widget.imageUrl),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            // If the Future is complete, display the preview.
            return paintImage(snapshot.data);
          } else {
            // Otherwise, display a loading indicator.
            return Center(child: CircularProgressIndicator());
          }
        });
  }

  paintImage(image) {
    return CustomPaint(
      painter: ImagePainter(image, widget.rect),
      child: SizedBox(
        width: MediaQuery.of(context).size.width,
        height: widget.rect.height,
      ),
    );
  }
}

class ImagePainter extends CustomPainter {
  ui.Image resImage;

  Rect rectCrop;

  ImagePainter(this.resImage, this.rectCrop);

  @override
  void paint(Canvas canvas, Size size) {
    if (resImage == null) {
      return;
    }
    final Rect rect = Offset.zero & size;
    final Size imageSize =
        Size(resImage.width.toDouble(), resImage.height.toDouble());
    FittedSizes sizes = applyBoxFit(BoxFit.fitWidth, imageSize, size);

    Rect inputSubRect = rectCrop;
    final Rect outputSubRect =
        Alignment.center.inscribe(sizes.destination, rect);

    final paint = Paint()
      ..color = Colors.white
      ..style = PaintingStyle.fill
      ..strokeWidth = 4;
    canvas.drawRect(rect, paint);

    canvas.drawImageRect(resImage, inputSubRect, outputSubRect, Paint());
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}
Elia Weiss
  • 8,324
  • 13
  • 70
  • 110
2

try using Alignment property and set fit to none.

Image.network(
  "your image.png",
// move on the X axis to right 10% of the image and 0% on the Y Axis
   alignment: const Alignment(0.1,0),
// set fit to none
   fit: BoxFit.none,
// use scale to zoom out of the image
   scale: 2,
)
0

One way suggested by the official doc of Flutter, is to:

To display a subpart of an image, consider using a CustomPainter and Canvas.drawImageRect.

ref: https://api.flutter.dev/flutter/painting/DecorationImage/alignment.html

Thus here is my full code. Use PartImage to show what you want.

class PartImage extends StatefulWidget {
  const PartImage({
    Key key,
    @required this.imageProvider,
    @required this.transform,
  })  : assert(imageProvider != null),
        super(key: key);

  final ImageProvider imageProvider;
  final Matrix4 transform;

  @override
  _PartImageState createState() => _PartImageState();
}

class _PartImageState extends State<PartImage> {
  ImageStream _imageStream;
  ImageInfo _imageInfo;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _getImage();
  }

  @override
  void didUpdateWidget(PartImage oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.imageProvider != oldWidget.imageProvider) _getImage();
  }

  void _getImage() {
    final oldImageStream = _imageStream;
    _imageStream = widget.imageProvider.resolve(createLocalImageConfiguration(context));
    if (_imageStream.key != oldImageStream?.key) {
      final listener = ImageStreamListener(_updateImage);
      oldImageStream?.removeListener(listener);
      _imageStream.addListener(listener);
    }
  }

  void _updateImage(ImageInfo imageInfo, bool synchronousCall) {
    setState(() {
      _imageInfo = imageInfo;
    });
  }

  @override
  void dispose() {
    _imageStream.removeListener(ImageStreamListener(_updateImage));
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return RawPartImage(
      image: _imageInfo?.image, // this is a dart:ui Image object
      scale: _imageInfo?.scale ?? 1.0,
      transform: widget.transform,
    );
  }
}

/// ref: [RawImage]
class RawPartImage extends StatelessWidget {
  final ui.Image image;
  final double scale;
  final Matrix4 transform;

  const RawPartImage({Key key, this.image, this.scale, this.transform}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: _RawPartImagePainter(
        image: image,
        scale: scale,
        transform: transform,
      ),
    );
  }
}

class _RawPartImagePainter extends CustomPainter {
  final ui.Image image;
  final double scale;
  final Matrix4 transform;

  final painter = Paint();

  _RawPartImagePainter({this.image, this.scale, this.transform});

  @override
  void paint(Canvas canvas, Size size) {
    if (image == null) {
      return;
    }

    final transformInv = Matrix4.inverted(transform);

    final dst = Offset.zero & size;
    final src = Rect.fromPoints(
      transformOffset(transformInv, dst.topLeft),
      transformOffset(transformInv, dst.bottomRight),
    );
    // print('src=$src dst=$dst');

    canvas.drawImageRect(image, src, dst, painter);
  }

  @override
  bool shouldRepaint(covariant _RawPartImagePainter oldDelegate) {
    return oldDelegate.image != image || //
        oldDelegate.scale != scale ||
        oldDelegate.transform != transform;
  }
}

Offset transformOffset(Matrix4 transform, Offset offset) {
  Vector4 vecOut = transform * Vector4(offset.dx, offset.dy, 0, 1);
  return Offset(vecOut.x, vecOut.y);
}


By the way, if you are interested in knowing what happens behind drawImageRect:

  1. Have a search https://github.com/flutter/engine/search?q=drawImageRect
  2. This seems the C++ code that drawImageRect (i.e. _drawImageRect actually calls: https://github.com/flutter/engine/blob/6bc70e4a114ff4c01b60c77bae754bace5683f6d/lib/ui/painting/canvas.cc#L330
  3. It calls canvas_->drawImageRect. What is canvas_? From the header file we see it is of type SkCanvas* canvas_;.
  4. Then we go into the world of Skia (not Flutter or Dart anymore). https://skia.org/user/api/skcanvas_overview for an overview of SkCanvas. And https://api.skia.org/classSkCanvas.html#a680ab85c3c7b5eab23b853b97f914334 for the actual SkCanvas.drawImageRect documentation.
ch271828n
  • 15,854
  • 5
  • 53
  • 88