I'm trying to export an image from a Widget without adding this Widget to screen.
Is this even possible?
I already succeeded in exporting it by adding to a scrollable container, now I want to render it without adding it to screen and save it to a temp file for sharing.
I think there should be a "paint" call somewhere in there but can't figure out exactly where.
Here's my code:
var shareImage = await ShareImageWidget.builder(context,
item: item, definition: definition);
var widget = shareImage.build(context);
var repaint = RepaintBoundary.wrap(widget, 0);
var render = RenderRepaintBoundary(child:repaint.createRenderObject(context));
ui.Image image = await render.toImage(pixelRatio: 1.0);
ByteData byteData = await image.toByteData(format:ui.ImageByteFormat.png);
var pngBytes = byteData.buffer.asUint8List();
var bs64 = base64Encode(pngBytes);
var dir = await getTemporaryDirectory();
Exemple:
/// Creates an image from the given widget by first spinning up a element and render tree,
/// then waiting for the given [wait] amount of time and then creating an image via a [RepaintBoundary].
///
/// The final image will be of size [imageSize] and the the widget will be layout, ... with the given [logicalSize].
Future<Uint8List> createImageFromWidget(Widget widget, {Duration wait, Size logicalSize, Size imageSize}) async {
final RenderRepaintBoundary repaintBoundary = RenderRepaintBoundary();
logicalSize ??= ui.window.physicalSize / ui.window.devicePixelRatio;
imageSize ??= ui.window.physicalSize;
assert(logicalSize.aspectRatio == imageSize.aspectRatio);
final RenderView renderView = RenderView(
window: null,
child: RenderPositionedBox(alignment: Alignment.center, child: repaintBoundary),
configuration: ViewConfiguration(
size: logicalSize,
devicePixelRatio: 1.0,
),
);
final PipelineOwner pipelineOwner = PipelineOwner();
final BuildOwner buildOwner = BuildOwner();
pipelineOwner.rootNode = renderView;
renderView.prepareInitialFrame();
final RenderObjectToWidgetElement<RenderBox> rootElement = RenderObjectToWidgetAdapter<RenderBox>(
container: repaintBoundary,
child: widget,
).attachToRenderTree(buildOwner);
buildOwner.buildScope(rootElement);
if (wait != null) {
await Future.delayed(wait);
}
buildOwner.buildScope(rootElement);
buildOwner.finalizeTree();
pipelineOwner.flushLayout();
pipelineOwner.flushCompositingBits();
pipelineOwner.flushPaint();
final ui.Image image = await repaintBoundary.toImage(pixelRatio: imageSize.width / logicalSize.width);
final ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
return byteData.buffer.asUint8List();
}
Reference: https://github.com/flutter/flutter/issues/40064#issuecomment-620081426
There is a package with this feature: https://pub.dev/packages/screenshot#capturing-widgets-that-are-not-in-the-widget-tree
You can use the Transform.translate Widget to move the widget offscreen. Transform moves the widget before painting it.
I am using the function from here:
How to create an image from a widget in Flutter-web?
this feature allows you to translate widgets into images without worrying about hiding those widgets from the screen.
More recently, this has been working on the web as well.
Related
I want to crop a rectangular image-file and make it circular or square or hexagon shape and save it as image-file locally. I have a bitmap and I want to crop a circular or hexagon region from this bitmap. All pixels outside the circle should be transparent. How can I do this?
I found the similar question in android platform. But unable to find a good resource that really help or direct me to achieve this in flutter. Please share me the idea to achieve this result.
These simple steps get the required result.
Create a PictureRecorder.
Create a Canvas with your PictureRecorder.
Draw circle in canvas using canvas.drawCircle().
Call endRecording() on the PictureRecorder to get a Picture.
Call toImage() on the Picture.
Convert image toByteData() .
Save image locally using getApplicationDocumentsDirectory().
Canvas Screen
Saved Image Canvas as png
Full Source Code:
import 'dart:io';
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
import 'package:flutter/services.dart' show rootBundle;
import 'dart:async';
import 'dart:typed_data';
import 'dart:math' as math;
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
class CustomImageCanvas extends StatefulWidget {
CustomImageCanvas({Key key, this.title}) : super(key: key);
final String title;
#override
_CustomImageCanvasState createState() => _CustomImageCanvasState();
}
class _CustomImageCanvasState extends State<CustomImageCanvas> {
ui.Image image;
bool isImageloaded = false;
void initState() {
super.initState();
init();
}
Future<Null> init() async {
final ByteData data = await rootBundle.load('assets/image.jpeg');
image = await loadImage(Uint8List.view(data.buffer));
}
Future<ui.Image> loadImage(List<int> img) async {
final Completer<ui.Image> completer = Completer();
ui.decodeImageFromList(img, (ui.Image img) {
setState(() {
isImageloaded = true;
});
return completer.complete(img);
});
return completer.future;
}
Widget _buildImage() {
if (this.isImageloaded) {
return CustomPaint(
painter: PngImagePainter(image: image),
);
} else {
return Center(child: Text('loading'));
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
child: _buildImage(),
));
}
}
class PngImagePainter extends CustomPainter {
PngImagePainter({
this.image,
});
ui.Image image;
#override
void paint(Canvas canvas, Size size) {
_drawCanvas(size, canvas);
_saveCanvas(size);
}
Canvas _drawCanvas(Size size, Canvas canvas) {
final center = Offset(150, 50);
final radius = math.min(size.width, size.height) / 8;
// The circle should be paint before or it will be hidden by the path
Paint paintCircle = Paint()..color = Colors.black;
Paint paintBorder = Paint()
..color = Colors.white
..strokeWidth = size.width / 36
..style = PaintingStyle.stroke;
canvas.drawCircle(center, radius, paintCircle);
canvas.drawCircle(center, radius, paintBorder);
double drawImageWidth = 0;
var drawImageHeight = -size.height * 0.8;
Path path = Path()
..addOval(Rect.fromLTWH(drawImageWidth, drawImageHeight,
image.width.toDouble(), image.height.toDouble()));
canvas.clipPath(path);
canvas.drawImage(image, Offset(drawImageWidth, drawImageHeight), Paint());
return canvas;
}
_saveCanvas(Size size) async {
var pictureRecorder = ui.PictureRecorder();
var canvas = Canvas(pictureRecorder);
var paint = Paint();
paint.isAntiAlias = true;
_drawCanvas(size, canvas);
var pic = pictureRecorder.endRecording();
ui.Image img = await pic.toImage(image.width, image.height);
var byteData = await img.toByteData(format: ui.ImageByteFormat.png);
var buffer = byteData.buffer.asUint8List();
// var response = await get(imgUrl);
var documentDirectory = await getApplicationDocumentsDirectory();
File file = File(join(documentDirectory.path,
'${DateTime.now().toUtc().toIso8601String()}.png'));
file.writeAsBytesSync(buffer);
print(file.path);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
You can use the simple_image_crop package.
See my answer on a similar question here-
Use Clipper to crop your image
use ClipRRect, ClipOval and you can also use custom Clipper to give your Custom shape to image
check this link https://www.woolha.com/tutorials/flutter-using-clipper-examples
new ClipRRect(
borderRadius: new BorderRadius.circular(8.0),
child: your image child,
)
I see there are a lot of examples on how to upload an image using flutter to firebase storage but nothing on actually downloading/reading/displaying one that's already been uploaded.
In Android, I simply used Glide to display the images, how do I do so in Flutter? Do I use the NetworkImage class and if so, how do I first get the url of the image stored in Storage?
To view the images inside your storage, what you need is the name of the file in the storage. Once you've the file name for the specific image you need.
In my case if i want the testimage to be loaded,
final ref = FirebaseStorage.instance.ref().child('testimage');
// no need of the file extension, the name will do fine.
var url = await ref.getDownloadURL();
print(url);
Once you've the url,
Image.network(url);
That's all :)
New alternative answer based on one of the comments.
I don't see anywhere google is charging extra money for downloadURL.
So if you're posting some comments please attach a link to it.
Once you upload a file to storage, make that filename unique and save that name somewhere in firestore, or realtime database.
getAvatarUrlForProfile(String imageFileName) async {
final FirebaseStorage firebaseStorage = FirebaseStorage(
app: Firestore.instance.app,
storageBucket: 'gs://your-firebase-app-url.com');
Uint8List imageBytes;
await firebaseStorage
.ref()
.child(imageFileName)
.getData(100000000)
.then((value) => {imageBytes = value})
.catchError((error) => {});
return imageBytes;
}
Uint8List avatarBytes =
await FirebaseServices().getAvatarUrlForProfile(userId);
and use it like,
MemoryImage(avatarBytes)
update
In newer versions use
await ref.getDownloadURL();
See How to get full downloadUrl from UploadTaskSnapshot in Flutter?
original
someMethod() async {
var data = await FirebaseStorage.instance.ref().child("foo$rand.txt").getData();
var text = new String.fromCharCodes(data);
print(data);
}
see Download an image from Firebase to Flutter
or
final uploadTask = imageStore.putFile(imageFile);
final url = (await uploadTask.future).downloadUrl;
In the later case you'd need to store the downloadUrl somewhere and then use NetworkImage or similar to get it rendered.
Here's an example of a stateful widget that loads an image from Firebase Storage object and builds an Image object:
class _MyHomePageState extends State<MyHomePage> {
final FirebaseStorage storage = FirebaseStorage(
app: Firestore.instance.app,
storageBucket: 'gs://my-project.appspot.com');
Uint8List imageBytes;
String errorMsg;
_MyHomePageState() {
storage.ref().child('selfies/me2.jpg').getData(10000000).then((data) =>
setState(() {
imageBytes = data;
})
).catchError((e) =>
setState(() {
errorMsg = e.error;
})
);
}
#override
Widget build(BuildContext context) {
var img = imageBytes != null ? Image.memory(
imageBytes,
fit: BoxFit.cover,
) : Text(errorMsg != null ? errorMsg : "Loading...");
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new ListView(
children: <Widget>[
img,
],
));
}
}
Note that FirebaseApp initialization is handled by the Firestore class, so no further initialization code is necessary.
The way I did it to respect the Storage rules and keep the image in cache is downloading the image as a File and store in the device. Next time I want to display the image I just check if the file already exists or not.
This is my widget:
import 'dart:io';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
class FirebaseImage extends StatefulWidget {
final String storagePath;
FirebaseImage({
required this.storagePath,
}) : super(key: Key(storagePath));
#override
State<FirebaseImage> createState() => _FirebaseImageState();
}
class _FirebaseImageState extends State<FirebaseImage> {
File? _file;
#override
void initState() {
init();
super.initState();
}
Future<void> init() async {
final imageFile = await getImageFile();
if (mounted) {
setState(() {
_file = imageFile;
});
}
}
Future<File?> getImageFile() async {
final storagePath = widget.storagePath;
final tempDir = await getTemporaryDirectory();
final fileName = widget.storagePath.split('/').last;
final file = File('${tempDir.path}/$fileName');
// If the file do not exists try to download
if (!file.existsSync()) {
try {
file.create(recursive: true);
await FirebaseStorage.instance.ref(storagePath).writeToFile(file);
} catch (e) {
// If there is an error delete the created file
await file.delete(recursive: true);
return null;
}
}
return file;
}
#override
Widget build(BuildContext context) {
if (_file == null) {
return const Icon(Icons.error);
}
return Image.file(
_file!,
width: 100,
height: 100,
);
}
}
Note: The code can be improved to show a loading widget, error widget, etc.
In Flutter/Dart, how can I perform the following 3 steps:
Read an image from disk,
Read its original dimensions (width and height),
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.
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));
}
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/
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.
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.
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,
),
),
),
);
}
}
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
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}');
});
});
final pickedFile = await picker.getImage(
source: ImageSource.gallery,
imageQuality: 25,
maxHeight: 1024,
maxWidth: 1024);
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);
}
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
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
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!
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);
UPDATED SOLUTION:
With the new version of flutter old solution is obsolete. Now the addListener needs an ImageStreamListener.
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(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.
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,
],
),
);
}
}
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
You can resolve the ImageProvider to get an ImageStream, then use addListener to be notified when the image is ready.
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...');
}
},
),
),
);
}
}
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);
}));
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;
A way for who only want decode image bounds:
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.dipose();
buffer.dipose();
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;
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);
Easy way to get asset images size.
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());
}
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".
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.
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(() {});
}
}
I'm using a manager class to either pull images from a cache or make a network request. I'm using a placeholder image. What's the best way to replace that placeholder image when the proper image is retrieved?
final ItemManager _manager;
final Item _item;
var _itemImage =
new Image.asset('assets/images/icons/ic_placeholder.png');
#override
Widget build(BuildContext context) {
_loadImage();
return new Container(
child: _itemImage,
);
}
_loadImage() async {
var file = await _manager.itemImageForImageUrl(_item.imageUrl);
_stickerImage = new Image.file(file);
}
The FutureBuilder class is designed for cases like this. I would modify _loadImage to return the image instead of setting a member variable. Then you can get rid of initState and modify your build() method as follows:
#override
Widget build(BuildContext context) {
return new FutureBuilder(
future: _loadImage(),
builder: (BuildContext context, AsyncSnapshot<Image> image) {
if (image.hasData) {
return image.data; // image is ready
} else {
return new Container(); // placeholder
}
},
);
}
As an aside, you should never mutate member variables of your State without calling setState. Your build function won't be called and this is something that the linter will eventually complain about (as soon as we implement it). But FutureBuilder is a much better fit for your use case because you won't have to worry about what happens if your State is disposed by the time the image finishes loading.
I'd recommend using flutter_image "to load images from the network with a retry mechanism."
You can pair it with a placeholder like this:
new FadeInImage(
placeholder: _itemImage,
image: new NetworkImageWithRetry('https://example.com/img.jpg'),
),