image_pixels_plus 1.0.0
image_pixels_plus: ^1.0.0 copied to clipboard
Read the width/height and the color of any pixel of an image — raster or SVG. A modern, Flutter-web-compatible fork of image_pixels.
image_pixels_plus #
Build widgets that depend on the width, height, or color of any pixel of an image — raster images and SVGs alike, on all Flutter platforms, including web.
ImagePixelsPlus(
imageProvider: imageProvider,
builder: (context, img) => Text("Img size is: ${img.width} × ${img.height}"),
);
Why this fork exists #
image_pixels_plus is a fork of
image_pixels by Marcelo Glasberg.
The original is a great idea that had fallen out of maintenance: it crashed on
Flutter web, used deprecated framework internals, and shipped without tests.
This fork keeps the original API you know, fixes what was broken, and extends
it where the ecosystem needed it:
image_pixels |
image_pixels_plus |
|
|---|---|---|
| Flutter web | ❌ throws Failed to encode the image into bytes |
✅ works |
| SVG support | ❌ | ✅ ImagePixelsPlusSvg + rasterizeSvg() |
| Load errors | silently swallowed | ✅ optional errorBuilder |
| Pre-decoded images | ❌ | ✅ ImagePixelsPlus.uiImage() |
Deprecated APIs (loadImage()) |
used | ✅ removed |
| Tests | none | ✅ full widget + unit suite |
The web fix, in one paragraph #
On several Flutter web renderer configurations, ui.Image.toByteData() cannot
read back the pixels of a decoded image and throws
Failed to encode the image into bytes. This package first tries the direct
readback and, if it fails, re-rasterizes the image into a fresh engine-owned
offscreen surface whose pixels are always readable. If even that fails (e.g.,
cross-origin images without CORS headers), pixel reads degrade gracefully to
the defaultColor instead of crashing your app.
Extend the background-color of an image #
ImagePixelsPlus.container() paints a background with the color of the pixel at
the colorAlignment position — perfect for making an image blend into its
surroundings:
ImagePixelsPlus.container(
imageProvider: myImageProvider,
colorAlignment: Alignment.topLeft,
child:
Container(
width: 250,
height: 100,
alignment: Alignment.center,
child:
Container(
width: 40.0,
height: 60.0,
child: Image(image: myImageProvider),
),
),
);
Using a builder #
The default constructor gives you full control through an ImgDetails object:
img.hasImageis true once the image is available, along withimg.widthandimg.height.img.pixelColorAt(x, y)reads any pixel —(0,0)is top-left,(width-1, height-1)is bottom-right. Coordinates outside the image return thedefaultColor.img.pixelColorAtAlignment(alignment)does the same with-1..1alignment coordinates, e.g.,Alignment.center.img.uiImageexposes the rawui.Image,img.byteDatathe raw RGBA bytes.
ImagePixelsPlus(
imageProvider: imageProvider,
defaultColor: Colors.grey,
builder: (context, img) => Text("Img size is: ${img.width} × ${img.height}"),
);
If loading fails, an optional errorBuilder is called; otherwise the widget
degrades gracefully to empty details.
Already have a decoded image? #
Skip the provider entirely:
final ui.Image image = await decodeSomehow();
ImagePixelsPlus.uiImage(
image: image,
builder: (context, img) => ...,
);
Note: ImagePixelsPlus never disposes images you pass in — you own them.
Performance: lazy pixel extraction #
By default, the raw RGBA bytes of the whole image are extracted as soon as the
image arrives (width × height × 4 bytes + a GPU→CPU readback). If you only
need the image dimensions, or read pixels rarely, opt out with lazyBytes —
extraction then happens on demand at the first pixel read:
ImagePixelsPlus(
imageProvider: provider,
lazyBytes: true, // Extract pixels only when actually read.
builder: (context, img) => ...,
);
While the bytes have not been extracted yet, hasPixelData is false and pixel
reads return the defaultColor; when extraction completes the widget rebuilds.
SVG support #
SVGs aren't decodable by Flutter's codecs, so this package rasterizes them
(via flutter_svg) into real pixels you can read — on all platforms including
web:
1) The turnkey widget:
ImagePixelsPlusSvg.asset(
assetName: 'assets/my_image.svg',
width: 300, // Raster width in pixels; height follows the aspect ratio.
builder: (context, img) => Container(
color: img.pixelColorAtAlignment(Alignment.topLeft),
child: Text("SVG size is: ${img.width} × ${img.height}"),
),
);
Also available: ImagePixelsPlusSvg.string(), ImagePixelsPlusSvg() (any vector
graphics BytesLoader), plus optional placeholder, errorBuilder,
width/height and clipViewbox.
The built-in flutter_svg loaders compare by value, so creating them inline is fine. If you use a custom
BytesLoaderthat doesn't implement==, pass a constantcacheKeyso the SVG isn't re-rasterized on every rebuild.
2) Do it yourself:
final ui.Image image = await rasterizeSvg(assetName: 'assets/my_image.svg', width: 300);
// Then use it directly:
ImagePixelsPlus.uiImage(image: image, builder: (context, img) => ...);
Declare your .svg files as assets in pubspec.yaml:
flutter:
assets:
- assets/
Picking the tapped pixel color #
Wrap the image in a Listener/GestureDetector, convert the local position
to pixel coordinates, and read the color — works for raster images and SVGs.
The example app implements exactly this, including a
live alignment picker for the container demo. Run it with:
cd example
flutter run -d chrome # or any device
Migrating from image_pixels? #
Change the import and you're done — the core API is source-compatible:
- import 'package:image_pixels/image_pixels.dart';
+ import 'package:image_pixels_plus/image_pixels_plus.dart';
Credits #
Built on top of image_pixels by
Marcelo Glasberg. Thank you for the original idea and
API design.