60

In Flutter/Dart, how can I perform the following 3 steps:

  1. Read an image from disk,
  2. Read its original dimensions (width and height),
  3. Resize it.

Note: I must be able to display the final result with a regular Flutter Image widget.

CLARIFICATION: I don't want to save the image, but I do want to actually resize it in memory.

Marcelo Glasberg
  • 29,013
  • 23
  • 109
  • 133

11 Answers11

70

You can read image from the disk using the image.file constructor.

For more features you can use the Image library

A Dart library providing the ability to load, save and manipulate images in a variety of different file formats.

Sample from the documentation examples

Load a jpeg, resize it and save it as a png

    import 'dart:io' as Io;
    import 'package:image/image.dart';
    void main() {
      // Read a jpeg image from file.
      Image image = decodeImage(new Io.File('test.jpg').readAsBytesSync());

      // Resize the image to a 120x? thumbnail (maintaining the aspect ratio).
      Image thumbnail = copyResize(image, width: 120);

      // Save the thumbnail as a PNG.
      new Io.File('out/thumbnail-test.png')
            ..writeAsBytesSync(encodePng(thumbnail));
    }
julianobrasil
  • 8,954
  • 2
  • 33
  • 55
Raouf Rahiche
  • 28,948
  • 10
  • 85
  • 77
  • But then how do I use this Image with the Flutter Image widget? I need to be able to display it. – Marcelo Glasberg Apr 06 '18 at 22:52
  • 3
    You really need to be clearer about what you're trying to do; if you just want to display it at a different scale, flutter will do that for you (if you use `Image.file`). I believe it will even cache the scaled raw image data... – rmtmckenzie Apr 06 '18 at 23:10
  • @rmtmckenzie If you think I am not clear, can you tell me what did you not understand? I want to read an image from disk. In it's original size. Then I want to know its dimensions. Then I want to be able to resize it to whatever new dimension I wish (without having to reload it from disk everytime obviously). And then display it in a Flutter app. – Marcelo Glasberg Apr 06 '18 at 23:25
  • FYI - if you need to do it the manual way, you can still get back to a Flutter image, but you'll have to use the image library's encodePng/encodeJpg,encodeBmp and feed that into a Memory.image – rmtmckenzie Apr 06 '18 at 23:25
  • @MarcG from your comment, `Image.file` as @Raouf answered with does exactly what you want already. So if you're looking for something other than what it does, you need to be specific about what exactly that is so that we can help figure it out =) – rmtmckenzie Apr 06 '18 at 23:28
  • I want something like: Image img = Image.fromFile("..."); var width = img.width; var height = img.height; Image newImg = img.resizedTo(100,200); – Marcelo Glasberg Apr 06 '18 at 23:29
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/168432/discussion-between-rmtmckenzie-and-marcg). – rmtmckenzie Apr 06 '18 at 23:31
  • Raouf's answer uses some Image class which is not Flutter's image widget class. I can't find a way to convert his Image class into Flutter's Image widget class. – Marcelo Glasberg Apr 06 '18 at 23:34
  • the library i provide in my answer give you the ability to manipulate the image as a file on the disk so you can resize or convert it and if you need to display it just use the file constructor or if you load it already use the memory constructor and in the part of displaying you can use the SizedBox as @rmtmckenzie said – Raouf Rahiche Apr 06 '18 at 23:50
  • @RaoufRahiche Your answer is useful and I upvoted it. But I don't want to save the file on disk. If I use the library you provided, can it export in bytes to memory, in the same format the Flutter Image.memory constructor uses to read? – Marcelo Glasberg Apr 07 '18 at 19:07
  • yes image.memory require Uint8List and the [getBytes](https://www.dartdocs.org/documentation/image/1.1.22/image/Image/getBytes.html) function return Uint8List no need to save the file on disk – Raouf Rahiche Apr 07 '18 at 19:26
  • 2
    The process is very expensive. Both saving to file and reading from memory take approximately same time. if I have 200 images which I want to display as thumbnails, this resizing takes at least 15 min before I show the thumbs. So ineffective. – Vijay Kumar Kanta Sep 13 '18 at 07:48
  • 1
    @VijayKumarKanta that's not the use case here – Raouf Rahiche Sep 13 '18 at 09:14
  • 1
    The image.dart package does work, but decodeImage is very slow. (see this: https://github.com/brendan-duncan/image/issues/55) – Assaf S. Nov 14 '18 at 12:17
15

To resize an image that is defined in pubspec.yaml use "BoxFit":

@override
Widget build(BuildContext context) {
  return (new Container(
    width: 250.0,
    height: 250.0,
      alignment: Alignment.center,
      decoration: new BoxDecoration(

      image: DecorationImage(
          image: AssetImage('assets/Launcher_Icon.png'),
          fit: BoxFit.fill
      ),
    ),
  ));
}

also reference how to access images: https://flutter.io/assets-and-images/

Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
  • 1
    Hey Stephen, I really wanted to resize the image in memory, not simply keep its original size and display it in a BoxFit. I have added a clarification to my question, to make this clear. Thank you. – Marcelo Glasberg Oct 23 '18 at 18:59
15

It's not a very good way to resize picture via Image library, since it blocks ui thread, and it brings very bad UX. There is a a maxWidth argument in image_picker lib, you can set it, so these writing files manipulation will be unnecessary in some cases.

korgara
  • 359
  • 2
  • 6
  • This works only if you want to get the image from the camera or file. But if you get an image, then crop it and then want to resize the result - you can't use it. – Nils Tierecke Nov 01 '22 at 22:22
13

Use the ResizeImage image provider.

Using a separate package is nice if you want to use many of the functionality, or if you can't do otherwise. But just to depend on something instead of what the framework itself (and its underlying graphics engine) can do easily... :-)

If you have an ImageProvider now, say, to display an image from the bytes in memory:

Image(image: MemoryImage(bytes))

Just wrap it inside a ResizeImage:

Image(image: ResizeImage(MemoryImage(bytes), width: 50, height: 100))

And if you want even more control, just create your own image provider based on the source code of this one.

Gábor
  • 9,466
  • 3
  • 65
  • 79
  • would be nice if you provided example on how to use it, because official page just describes but doesn't show the usage. – temirbek May 29 '20 at 09:49
  • thank you, and is it possible to get Unit8List of resized image? – temirbek Jun 02 '20 at 06:18
  • If you don't need to display it, just to resize the original bytes, then I wouldn't do either of those. Use the relevant functions in `dart.ui` instead (eg. `instantiateImageCodec()` and `Image.toByteData()`). This one is described in other answers here. – Gábor Jun 03 '20 at 20:15
  • I tried it with no luck, can you look at it please? https://stackoverflow.com/questions/62188074/how-to-resize-image-using-multi-image-picker-in-flutter – temirbek Jun 04 '20 at 06:23
  • I think `ResizeImage` is of flawed design, and should be deprecated from the Flutter library. From one perspective, I can understand `ImageProvider` provides the image, and `ResizeImage` resizes that provided image. But from another perspective, un-resized bytes should not be sent to `ImageProvider` in the first place, and `ResizeImage` is doing work that should be done by `ImageProvider` or another class which is independent of `ImageProvider`. Either way, these perspectives are not aligned, which hints there is a design flaw. – tim-montague Nov 25 '21 at 06:49
  • The underlying Skia (dart.ui) engine has support for loading some image formats with resizing but not all. And it still fails on the web, I guess. But for a universal case, well, you can't really avoid it. – Gábor Nov 25 '21 at 10:38
12

Here's an example Thumbnail widget which does this on the flight

It uses Isolate to offload CPU-intensive work to background thread and have UI thread jank-free

import 'dart:io';
import 'dart:isolate';
import 'package:flutter/material.dart';
import 'package:image/image.dart' as IMG;
import 'package:path/path.dart';

class Thumbnail extends StatefulWidget {
  final Size size;
  final File image;

  const Thumbnail({Key key, this.size, this.image}) : super(key: key);
  @override
  _ThumbnailState createState() => _ThumbnailState();
}

class _ThumbnailState extends State<Thumbnail> {
  List<int> imgBytes;
  Isolate isolate;

  @override
  void initState() {
    _asyncInit();

    super.initState();
  }

  static _isolateEntry(dynamic d) async {
    final ReceivePort receivePort = ReceivePort();
    d.send(receivePort.sendPort);

    final config = await receivePort.first;

    print(config);

    final file = File(config['path']);
    final bytes = await file.readAsBytes();

    IMG.Image image = IMG.decodeImage(bytes);
    IMG.Image thumbnail = IMG.copyResize(
      image,
      width: config['size'].width.toInt(),
    );

    d.send(IMG.encodeNamedImage(thumbnail, basename(config['path'])));
  }

  _asyncInit() async {
    final ReceivePort receivePort = ReceivePort();
    isolate = await Isolate.spawn(_isolateEntry, receivePort.sendPort);

    receivePort.listen((dynamic data) {
      if (data is SendPort) {
        if (mounted) {
          data.send({
            'path': widget.image.path,
            'size': widget.size,
          });
        }
      } else {
        if (mounted) {
          setState(() {
            imgBytes = data;
          });
        }
      }
    });
  }

  @override
  void dispose() {
    if (isolate != null) {
      isolate.kill();
    }
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: widget.size.height,
      width: widget.size.width,
      child: imgBytes != null
          ? Image.memory(
              imgBytes,
              fit: BoxFit.cover,
            )
          : Container(
              decoration: BoxDecoration(
                gradient: LinearGradient(
                  colors: [Colors.grey[100], Colors.grey[300]],
                  begin: Alignment.centerLeft,
                  end: Alignment.centerRight,
                ),
              ),
            ),
    );
  }
}
Andrei Lesnitsky
  • 1,038
  • 7
  • 14
7

There are many solutions :

Use ResizeImage class

ResizeImage class instructs Flutter to decode the image at the specified dimensions instead of at its native size.

Usage : Just wrap your ImageProvider with ResizeImage class

Example :

Image(image: ResizeImage(AssetImage('eg.png'), width: 70, height: 80)),

ImageProvider includes AssetImage , NetworkImage , FileImage and MemoryImage.

Use cacheHeight and cacheWidth property in your Image widget

These properties create a widget that displays an [ImageStream] obtained from an asset , network , memory or file .

Example :

Image.asset('assets/image.png', cacheHeight:120 , cacheWidth: 150),

There are these properties in Image.asset ,Image.network , Image.file and Image.memory

Kab Agouda
  • 6,309
  • 38
  • 32
5

you can use the image class from dart ui library, get the image object with your desired width and height using the frameInfo from intantiateImageCodec and then save it in your desired path

 import 'dart:ui' as ui;

        Uint8List m = File(path).readAsBytesSync();
        ui.Image x = await decodeImageFromList(m);
        ByteData bytes = await x.toByteData();
        print('height is ${x.height}'); //height of original image
        print('width is ${x.width}'); //width of oroginal image

        print('array is $m');
        print('original image size is ${bytes.lengthInBytes}');

            ui.instantiateImageCodec(m, targetHeight: 800, targetWidth: 600)
            .then((codec) {
          codec.getNextFrame().then((frameInfo) async {
            ui.Image i = frameInfo.image;
            print('image width is ${i.width}');//height of resized image
            print('image height is ${i.height}');//width of resized image
            ByteData bytes = await i.toByteData();
            File(path).writeAsBytes(bytes.buffer.asUint32List());
            print('resized image size is ${bytes.lengthInBytes}');
          });
        });
N3R4ZZuRR0
  • 2,400
  • 4
  • 18
  • 32
Sachin Singh
  • 51
  • 1
  • 3
3
 final pickedFile = await picker.getImage(
        source: ImageSource.gallery,
        imageQuality: 25,
        maxHeight: 1024,
        maxWidth: 1024);
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
shadow_pro
  • 41
  • 1
  • 2
    What does this do exactly? Does it just show the image resized? Or does it actually resize the image? I want to upload the image ... I have a rezise function in Cloud Functions, but it doesn't do a great job. – William Sep 07 '21 at 07:38
  • I think this is using this library import 'package:image_picker/image_picker.dart'; – toha Apr 17 '22 at 13:09
3

I would say use the dart image package.

import 'package:image/image.dart' as image show
  decodedImage, copyResize //, encodePng
;
import 'dart:convert' as convert show
  base64encode
;

void resizeImage() async {
  List<int> fileBytes = await file.readAsBytes();
  image.Image decodedImage = image.decodeImage(fileBytes) as image.Image;
  image.Image thumbnail = image.copyResize(decodedImage, width: 60);
  List<int> resizedIntList = thumbnail.getBytes();
  // Or compress as a PNG image
  // List<int> resizedIntList = image.encodePng(thumbnail, level: 6);

  String resizedBase64Image = convert.base64Encode(resizedIntList);
}

You could also reference the following code, if you don't want the overhead of the package.

import 'dart:ui' as ui show
  Codec, instantiateImageCodec, FrameInfo;
import 'dart:typed_data' as typedData show
  ByteData, Uint8List
;
import 'dart:convert' as convert show
  base64Encode
;

void resizeImage() async {
  typedData.Uint8List fileBytes = await file.readAsBytes();

  // Resize image
  // ----------
  ui.Codec codec = await ui.instantiateImageCodec(
    fileBytes,
    targetWidth: 60
  );
  ui.FrameInfo frameInfo = await codec.getNextFrame();
  ui.Image resizedImage = frameInfo.image;
  // ----------

  // Convert to List<int>
  // ----------
  typedData.ByteData resizedByteData = await resizedImage.toByteData() as typedData.ByteData;
  typedData.Uint8List resizedUint8List = resizedByteData.buffer
    .asUint8List(resizedByteData.offsetInBytes, resizedByteData.lengthInBytes);
  List<int> resizedIntList = resizedUint8List.cast<int>();
  // ----------

  String resizedBase64Image = convert.base64Encode(resizedIntList);
}
tim-montague
  • 16,217
  • 5
  • 62
  • 51
2

You can use the dart image package: https://pub.dartlang.org/packages/image.

The package provide various services such as resize, crop and rotate.

While this package does work, unfortunately it is very slow.

See discussion: https://github.com/brendan-duncan/image/issues/55

Assaf S.
  • 4,676
  • 2
  • 22
  • 18
-1

You can use the Image widget in the Scaffold widget,

First of all you need to create assets folder in the root and add an images folder, after that add,

    flutter:
      assets:
        - assets/images/

to the pubspec.yaml file, after that

new Image(
          image: AssetImage('assets/images/pizzaFont.png'),
          height: 12,
          width:12, ......
   )

You can use width and height to change the size of the image.

For more information follow,

https://medium.com/@suragch/how-to-include-images-in-your-flutter-app-863889fc0b29

Achintha Isuru
  • 2,662
  • 18
  • 24
  • Thanks for your answer. But as I said under CLARIFICATION: I don't want to save the image, but I do want to actually resize it in memory. I'd like to have an in memory file with the given size. Not only to display it bigger or smaller in the screen. – Marcelo Glasberg Sep 28 '19 at 21:16