61

Assume I have declared my image in my pubspec.yaml like this:

  assets:
    - assets/kitten.jpg

And my Flutter code is this:

void main() {
  runApp(
    new Center(
      child: new Image.asset('assets/kitten.jpg'),
    ),
  );
}

Now that I have a new Image.asset(), how do I determine the width and height of that image? For example, I just want to print out the image's width and height.

(It looks like dart:ui's Image class has width and height, but not sure how to go from widget's Image to dart:ui's Image.)

Thanks!

Seth Ladd
  • 112,095
  • 66
  • 196
  • 279

13 Answers13

112

The other answers seem overly complicated if you just want the width and height of an image in an async function. You can get the image resolution using flutter lib directly like this:

import 'dart:io';

File image = new File('image.png'); // Or any other way to get a File instance.
var decodedImage = await decodeImageFromList(image.readAsBytesSync());
print(decodedImage.width);
print(decodedImage.height);
qwertzguy
  • 15,699
  • 9
  • 63
  • 66
  • Looks like it's in the UI lib - import 'dart:ui'; – ajplumlee33 Apr 03 '19 at 02:45
  • @ajplumlee33 I didn't have to import it in my code. But maybe it's imported from another import? Are imports transcient in dart? – qwertzguy Apr 04 '19 at 04:58
  • 3
    This returns a lot more height than the actual height of image. When I use this value to set height I get a lot of extra space at top and bottom. – sakina Jun 04 '20 at 11:40
  • @sakina: open the image in an image editor to check the height and compare it with what this function returns. It should be the same. The issue might be in the layout you are using. – qwertzguy Jun 08 '20 at 18:09
  • @qwertzguy I think its related to scaling of the image, i.e conversion from actual resolution to logical pixels. Because if I give the height returned by the above function to the image there is a lot of empty space, but if no height is given it renders properly. Any idea how to determine this scale? – sakina Jun 09 '20 at 11:57
  • @sakina what size did you see when you opened the image in an image editor? – qwertzguy Jun 11 '20 at 01:51
  • 2
    simplest solution and still works fine as of July 26 2020 – DaReal Jul 26 '20 at 23:11
  • 5
    I don't understand this part. I have an image in my assets folder and it says it can't read the image. `File image = new File("lib/pages/assets/images/road_map.png");` – Michael T Oct 02 '20 at 13:12
  • 1
    hi! how do i supply a File path if the image is in assets? @qwertzguy – nrion May 05 '21 at 12:59
  • 4
    @nrion I think you can use rootBundle.load('assets/image.png') and pass that directly to decodeImageFromList – qwertzguy May 05 '21 at 23:02
  • 1
    @qwertzguy It's not possible to pass it directly since decodeImageFromList does not accept `ByteData`. You need to convert it to `asUint8List` first: `decodeImageFromList(imgByteData.buffer.asUint8List())` – Burnash May 26 '21 at 17:21
  • load the image from bundle: final ByteData bytes = await rootBundle.load('image.png'); var decodedImage = await decodeImageFromList(bytes.buffer.asUint8List()); – Cristi Sep 01 '22 at 19:11
  • Beautifully simple. – chai Jul 03 '23 at 09:32
58

UPDATED SOLUTION:

With the new version of flutter old solution is obsolete. Now the addListener needs an ImageStreamListener.

import 'dart:ui' as ui;

Widget build(BuildContext context) {
    Image image = Image.network('https://i.stack.imgur.com/lkd0a.png');
    Completer<ui.Image> completer = Completer<ui.Image>();
    image.image
      .resolve(ImageConfiguration())
      .addListener(
          ImageStreamListener(
              (ImageInfo info, bool _) => completer.complete(info.image)));
    ...
    ...

ORIGINAL VERSION:

If you already have an Image widget, you can read the ImageStream out of it by calling resolve on its ImageProvider.

screenshot

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

void main() {
  runApp(new MaterialApp(
    home: new MyHomePage(),
  ));
}

class MyHomePage extends StatelessWidget {

  Widget build(BuildContext context) {
    Image image = new Image.network('https://i.stack.imgur.com/lkd0a.png');
    Completer<ui.Image> completer = new Completer<ui.Image>();
    image.image
      .resolve(new ImageConfiguration())
      .addListener((ImageInfo info, bool _) => completer.complete(info.image));
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Image Dimensions Example"),
      ),
      body: new ListView(
        children: [
          new FutureBuilder<ui.Image>(
            future: completer.future,
            builder: (BuildContext context, AsyncSnapshot<ui.Image> snapshot) {
              if (snapshot.hasData) {
                return new Text(
                  '${snapshot.data.width}x${snapshot.data.height}',
                  style: Theme.of(context).textTheme.display3,
                );
              } else {
                return new Text('Loading...');
              }
            },
          ),
          image,
        ],
      ),
    );
  }
}
Luke Hutchison
  • 8,186
  • 2
  • 45
  • 40
Collin Jackson
  • 110,240
  • 31
  • 221
  • 152
  • 12
    we need to update this answer. The addListener needs an ImageStreamListener. Even with that I get an error at this line `completer.complete(info.image)` saying `The argument type 'Image' can't be assigned to the parameter type 'FutureOr` – Ionel Lupu Aug 06 '19 at 13:10
  • If offline, how to calculate the size? – John Joe Jun 14 '20 at 14:29
  • 2
    Will this cause the image be loaded ***twice***, thus waste a lot of network and cpu? – ch271828n Aug 16 '20 at 02:12
  • 3
    Needs to be: `image.image.resolve(ImageConfiguration()).addListener( ImageStreamListener((ImageInfo info, bool synchronousCall) { completer.complete(info.image); }));` – Gregory Ray Mar 28 '21 at 22:13
  • @lonel Lupu import 'dart:ui' as ui; Completer... use ui.Image as generic type. – blob Nov 24 '21 at 10:46
28

Create a method, like:

Future<Size> _calculateImageDimension() {
  Completer<Size> completer = Completer();
  Image image = Image.network("https://i.stack.imgur.com/lkd0a.png");
  image.image.resolve(ImageConfiguration()).addListener(
    ImageStreamListener(
      (ImageInfo image, bool synchronousCall) {
        var myImage = image.image;
        Size size = Size(myImage.width.toDouble(), myImage.height.toDouble());
        completer.complete(size);
      },
    ),
  );
  return completer.future;
}

And use it like:

_calculateImageDimension().then((size) => print("size = ${size}")); // 487.0,696.0
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
22

You can resolve the ImageProvider to get an ImageStream, then use addListener to be notified when the image is ready.

screenshot

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

void main() {
  runApp(new MaterialApp(
    home: new MyHomePage(),
  ));
}

class MyHomePage extends StatelessWidget {

  Future<ui.Image> _getImage() {
    Completer<ui.Image> completer = new Completer<ui.Image>();
    new NetworkImage('https://i.stack.imgur.com/lkd0a.png')
      .resolve(new ImageConfiguration())
      .addListener((ImageInfo info, bool _) => completer.complete(info.image));
    return completer.future;
  }

  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Image Dimensions Example"),
      ),
      body: new Center(
        child: new FutureBuilder<ui.Image>(
          future: _getImage(),
          builder: (BuildContext context, AsyncSnapshot<ui.Image> snapshot) {
            if (snapshot.hasData) {
              ui.Image image = snapshot.data;
              return new Text(
                '${image.width}x${image.height}',
                style: Theme.of(context).textTheme.display4);
            } else {
              return new Text('Loading...');
            }
          },
        ),
      ),
    );
  }
}
Collin Jackson
  • 110,240
  • 31
  • 221
  • 152
10

With new version of flutter old solution not working example:

  image.image
  .resolve(new ImageConfiguration())
  .addListener((ImageInfo info, bool _) => completer.complete(info.image));

Below the Working version:

_image.image
    .resolve(new ImageConfiguration())
    .addListener(new ImageStreamListener((ImageInfo image, bool _) {
  completer.complete(image.image);
}));
  • 2
    I can confirm this is the new way of adding a listener, but I get an error on `completer.complete(image.image);` : `The argument of type Image can't be assigned to the type parameter FutureOr`. How can this solved? – Ionel Lupu Jul 26 '19 at 13:13
8

If you don't want to use FutureBuilder or then, you can also use await like this:

Don't forget to import the Image. But as there are two Image classes, import it like this and use with ui.Image

 import 'dart:ui' as ui

Then you can fetch the dimensions of the image as follows.

 final Image image = Image(image: AssetImage('assets/images/someimage.png'));
 Completer<ui.Image> completer = new Completer<ui.Image>();
    image.image
        .resolve(new ImageConfiguration())
        .addListener(new ImageStreamListener((ImageInfo image, bool _) {
      completer.complete(image.image);
    }));
 ui.Image info = await completer.future;
 int width = info.width;
 int height = info.height;
Jannunen
  • 344
  • 5
  • 9
8

A way for who only want decode image bounds:

import 'dart:ui' as ui;

final buffer = await ui.ImmutableBuffer.fromUint8List(bytes);
final descriptor = await ui.ImageDescriptor.encoded(buffer);

final imageWidth = descriptor.width;
final imageHeight = descriptor.height;
print("imageWidth: $imageWidth, imageHeight: $imageHeight");

descriptor.dispose();
buffer.dispose();
Benjamin Buch
  • 4,752
  • 7
  • 28
  • 51
Summerly
  • 109
  • 1
  • 4
  • 1
    Where do you get the "bytes" from? It really helps soooooo much if you just write these things. It takes so much time to have to guess. – Karolina Hagegård Jun 09 '22 at 08:13
  • @KarolinaHagegård `bytes` is the byte data of the image. For example, you can get the bytes by `File('you_image_path').readAsBytesSync()` from a image file. – Summerly Jun 14 '22 at 03:13
  • Thanks, but please edit your answer to contain complete sets of code! – Karolina Hagegård Jun 16 '22 at 13:33
  • @Summerly thanks, its very faster than `decodeImageFromList` i test with this method and `decodeImageFromList` , this method 5x faster – Mohsen Haydari Sep 13 '22 at 08:42
  • As of 2023, the decodeImageFromList() solution now returns void and accepts a callback, where this solution will more intuitively return a descriptor with width and height. – Matthew Rideout Mar 26 '23 at 19:10
  • This is exactly what I was looking for, I don't want to decode the image before knowing the bounds. This is so that I can read the image aspect ratio and dimensions, and then decode a scaled-down version of the image with a maximum `cacheWidth` and `cacheHeight` to save RAM. Thanks! – Luke Hutchison Jun 16 '23 at 06:33
5

Here's a handy helper function, based on other solutions

helper function

Future<ImageInfo> getImageInfo(Image img) async {
  final c = new Completer<ImageInfo>();
  img.image
    .resolve(new ImageConfiguration())        
    .addListener(new ImageStreamListener((ImageInfo i, bool _) {
      c.complete(i);
    }));
  return c.future;    
}

usage

Image image = Image.network("https://example.com/pic.png");
ImageInfo info = await getImageInfo(image);
Ben Winding
  • 10,208
  • 4
  • 80
  • 67
4

A simple way how to check image dimensions that is loaded from assets.

var img = await rootBundle.load("Your image path");
var decodedImage = await decodeImageFromList(img.buffer.asUint8List());
int imgWidth = decodedImage.width;
int imgHeight = decodedImage.height;
Borouch
  • 103
  • 4
  • 1
    `await` can be used in `async` functions, so may you describe how dose this code will work? Or sample code to use it. – Mamrezo Apr 01 '21 at 18:41
0

Easy way to get asset images size.

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

          Future<Size> _loadAssetImageSize(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 Size(fi.image.width.toDouble(), fi.image.height.toDouble());
      }

 
MCB
  • 503
  • 1
  • 8
  • 21
Sher Ali
  • 5,513
  • 2
  • 27
  • 29
  • And where do you get this "ui" from? It helps sooooo much if you just write these things... rather than forcing us to find the info elsewhere... – Karolina Hagegård Jun 09 '22 at 08:15
0

Does any of the solution works for Flutter web? I am unable to find any solution, if i try to use image_size_getter package its throws "Error: Unsupported operation: _Namespace".

  • instead answering other question, and you will not get any feedback, its better to raise new question for your issue. – pmatatias Oct 26 '22 at 01:37
  • If you have a new question, please ask it by clicking the [Ask Question](https://stackoverflow.com/questions/ask) button. Include a link to this question if it helps provide context. - [From Review](/review/late-answers/33007108) – Fastnlight Oct 27 '22 at 21:00
0

All the answers on this page currently only get the raw image size: that is the pixel height and width of the original image. Not the size of the image on the Flutter app. I needed that to resize bounding boxes as the page was resized. I need the current image size, and also know when it changes so I can resize the bounding box.

GIF of app being resized with image and bounding box overlay resizing

My solution involves:

  • GlobalKey and addPostFrameCallback to get the image size
  • Some logic to render when the image size changes

At the top of my build method:

  final _imageKey = GlobalKey();
  Size imageSize = Size.zero;

  @override
  Widget build(BuildContext context) {
    MediaQuery.of(context); // Trigger rebuild when window is resized. This updates the bounding box sizes.
    final image = Image.asset(exampleImagePath, key: _imageKey);
    // Yes, we call this every time the widget rebuilds, so we update our understanding of the image size.
    WidgetsBinding.instance.addPostFrameCallback(_updateImageSize);

My _updateImageSize

  void _updateImageSize(Duration _) {
    final size = _imageKey.currentContext?.size;
    if (size == null) return;
    if (imageSize != size) {
      imageSize = size;
      // When the window is resized using keyboard shortcuts (e.g. Rectangle.app),
      // The widget won't rebuild AFTER this callback. Therefore, the new
      // image size is not used to update the bounding box drawing. 
      // So we call setState
      setState(() {});
    }
  }
Ben Butterworth
  • 22,056
  • 10
  • 114
  • 167
0

This code loads an image asset asynchronously and obtains its width and height. This code loads an image asset named "my_image.png" from the "assets" directory using the Image.asset() method.

A Completer object is created to handle the asynchronous loading of the image. The Completer is used to complete the future when the image is loaded.

An ImageStreamListener is added to the image's image stream using the addListener() method. The ImageStreamListener is triggered when the image is fully loaded and provides an ImageInfo object containing information about the loaded image. The Completer is then completed with the loaded ui.Image object.

Finally, the future from the Completer is awaited to get the ui.Image object. The width and height of the ui.Image object are then obtained using the width and height properties respectively.

Image image = Image.asset('assets/my_image.png');
Completer<ui.Image> completer = Completer<ui.Image>();
image.image.resolve(ImageConfiguration()).addListener(  
ImageStreamListener((ImageInfo info, bool _) {
    completer.complete(info.image); 
 }), 
);
ui.Image myImage = await completer.future; 
int width = myImage.width;
int height = myImage.height;