A high-performance Flutter plugin for cross-platform image format conversion and resizing using native APIs on iOS, macOS, Android, Windows, Linux, and Web.
- πΌοΈ Versatile Format Conversion: Supports conversion between JPEG, PNG, and WebP. It also handles HEIC/HEIF, allowing conversion from HEIC on all supported platforms and to HEIC on iOS/macOS, Windows (where the OS HEVC/HEIF codec is present), and Linux (where a writable
heifGdkPixbuf loader is installed; the libheif GdkPixbuf loader only decodes). AVIF output is supported on iOS/macOS (where ImageIO provides an AVIF encoder), Android 14+ (without an alpha channel), Windows (where the HEIF codec and an AV1 encoder are installed), and Linux (where GdkPixbuf has a writableavifformat with an AV1 encoder). - π High-Quality Resizing: Resize images with different modes (
Fit,Exact) while maintaining aspect ratio or targeting specific dimensions. - π§ EXIF Orientation: Bakes the source's EXIF orientation into the output by default (
ExifOrientationPolicy.apply) so camera photos come out upright and consistent on every platform; opt out withExifOrientationPolicy.ignore. - β‘ Native Performance: Achieves high speed by using platform-native APIs directly:
ImageIOandCore Graphicson iOS/macOS,ImageDecoder/BitmapFactory,Bitmapmethods andMediaCodec/MediaMuxeron Android, theWindows Imaging Component(WIC) on Windows,GdkPixbuf(GLib/GTK stack) on Linux, and theCanvas APIon the Web. - π Efficient Native Interop: Employs FFI and JNI to create a fast, type-safe bridge between Dart and native code, ensuring robust and reliable communication.
| Platform | Minimum Version | API Used |
|---|---|---|
| iOS | 14.0 | ImageIO, Core Graphics |
| macOS | 10.15 | ImageIO, Core Graphics |
| Android | 7 | ImageDecoder (9+), BitmapFactory, Bitmap compression, MediaCodec + MediaMuxer (AVIF, 14+) |
| Windows | 10 | Windows Imaging Component (WIC) |
| Linux | - | GdkPixbuf (GLib/GTK stack) |
| Web | - | Canvas API |
Note:
- On iOS and macOS, WebP input is supported but WebP output is not supported. AVIF output requires an AVIF encoder in ImageIO β when it is absent, AVIF throws
UnsupportedFormatExceptionwith reasoncodecUnavailable. - On Android, HEIC input is supported on Android 9+ but HEIC output is not supported. Use AVIF output on Android with care:
- Whether it works depends on the OS release, not only the API level: it needs Android 14+ (API 34) and does not work on Android 16 before QPR2 (SDK 36.0). Elsewhere AVIF throws
UnsupportedFormatExceptionwith reasoncodecUnavailable, so handle it and fall back to another format. - It has no alpha channel: transparent pixels are composited onto black.
- It is encoded by whichever AV1 encoder the device provides (hardware or software), so output size, quality and encoding time vary by device.
- Whether it works depends on the OS release, not only the API level: it needs Android 14+ (API 34) and does not work on Android 16 before QPR2 (SDK 36.0). Elsewhere AVIF throws
- On Windows, JPEG and PNG output are always supported. HEIC output is supported where the OS ships the HEVC/HEIF codec (Windows 11 22H2+ out of the box; older Windows via the Microsoft Store "HEVC Video Extensions") β when the codec is absent, HEIC throws
UnsupportedFormatExceptionwith reasoncodecUnavailable. AVIF output is supported where the Microsoft Store "HEIF Image Extension" and a Media Foundation AV1 encoder are installed. Windows ships no software AV1 encoder (the Store "AV1 Video Extension" only decodes); GPU drivers for hardware with AV1 encoding can provide one β when either is absent, AVIF throwsUnsupportedFormatExceptionwith reasoncodecUnavailable. WebP output is not supported (Windows provides a WebP decoder but no encoder). - On Linux, JPEG and PNG output are always supported (built-in GdkPixbuf loaders). WebP and HEIC output require a writable GdkPixbuf loader module (e.g.
webp-pixbuf-loader; the libheif GdkPixbuf loader only decodes, so it does not enable HEIC output), which varies by distribution β when none is installed, output throwsUnsupportedFormatExceptionwith reasoncodecUnavailable. AVIF output requires a writableavifGdkPixbuf format: a GdkPixbuf built with glycin provides one, which needs a libheif AV1 encoder plugin (e.g.libheif-plugin-aomenc); otherwise a loader module such aslibavif-gdk-pixbufprovides it β without them, AVIF throwsUnsupportedFormatExceptionwith reasoncodecUnavailable. - On Web, HEIC and AVIF output are not supported.
import 'package:platform_image_converter/platform_image_converter.dart';
import 'dart:typed_data';
// Convert HEIC image to JPEG
final jpegData = await ImageConverter.convert(
inputData: heicImageData,
format: OutputFormat.jpeg,
quality: 90,
);
// Convert and resize an image to fit within a width of 200, scaling height proportionally
final resizedData = await ImageConverter.convert(
inputData: imageData,
format: OutputFormat.png,
resizeMode: const FitResizeMode(width: 200),
);
// Convert any format to PNG
final pngData = await ImageConverter.convert(
inputData: imageData,
format: OutputFormat.png,
);- iOS/macOS: JPEG, PNG, HEIC, WebP, BMP, GIF, TIFF, and more
- Android: JPEG, PNG, WebP, GIF, BMP, HEIC (via ImageDecoder/BitmapFactory)
- Windows: JPEG, PNG, GIF, BMP, TIFF, and (with the relevant OS codec) HEIC, WebP (via WIC)
- Linux: JPEG, PNG, GIF, BMP, and (where the relevant GdkPixbuf loader is installed) WebP, HEIC
- Web: JPEG, PNG, WebP, GIF, BMP (via Canvas API)
The supported output formats are defined by the OutputFormat enum, with platform-specific limitations:
- JPEG: Supported on all platforms.
- PNG: Supported on all platforms.
- WebP: Supported on Android and Web, and on Linux where a writable GdkPixbuf WebP loader is installed.
- HEIC: Supported on iOS/macOS, on Windows where the OS HEVC/HEIF codec is present, and on Linux where a writable
heifGdkPixbuf loader is installed (the libheif GdkPixbuf loader only decodes). - AVIF: Supported on iOS/macOS where ImageIO provides an AVIF encoder, on Android 14+ (without an alpha channel), on Windows where the HEIF codec and an AV1 encoder are installed, and on Linux where GdkPixbuf has a writable
avifformat with an AV1 encoder (a GdkPixbuf built with glycin plus a libheif AV1 encoder plugin such aslibheif-plugin-aomenc, or a loader module such aslibavif-gdk-pixbuf).
static Future<Uint8List> convert({
required Uint8List inputData,
OutputFormat format = OutputFormat.jpeg,
int quality = 100,
ResizeMode resizeMode = const OriginalResizeMode(),
ExifOrientationPolicy orientation = ExifOrientationPolicy.apply,
bool runInIsolate = true,
}) asyncParameters:
inputData(Uint8List): Raw image data to convert.format(OutputFormat): Target image format (default: JPEG).quality(int): Compression quality 1-100 (default: 100, only for lossy formats). On Linux it has no effect when the installed encoder does not support a quality option.resizeMode(ResizeMode): The resize mode to apply. Defaults toOriginalResizeMode, which keeps the original dimensions.orientation(ExifOrientationPolicy): How to handle the source's EXIF orientation tag. Defaults toExifOrientationPolicy.apply, which bakes the orientation into the output pixels (a 90Β°/270Β° tag swaps the output width/height, and the resize is evaluated against the oriented dimensions). UseExifOrientationPolicy.ignoreto encode the raw decoded buffer instead.runInIsolate(bool): Whether to run the conversion in a background isolate (default:true). Set tofalseonly for very small images where isolate overhead is a concern.
Returns: Future<Uint8List> containing the converted image data.
Throws: (all conversion failures share the ImageConversionException base type)
ArgumentError: Ifqualityis outside the 1-100 range.UnsupportedPlatformException: If the current platform has no conversion backend.UnsupportedFormatException: If the output format is not available in the current environment. Inspectreasonto tell a permanent platform limitation (platformUnsupported) from a missing codec the user could install (codecUnavailable).ImageDecodingException: If the input image data cannot be decoded.ImageEncodingException: If the image cannot be encoded to the target format.ImageConversionException: For other general errors during the conversion process.
enum OutputFormat {
jpeg, // .jpg, .jpeg
png, // .png
webp, // .webp
heic, // .heic
avif, // .avif
}A sealed class representing different ways to resize an image.
OriginalResizeMode(): Keeps the original dimensions of the image.ExactResizeMode({required int width, required int height}): Resizes the image to exact dimensions, possibly changing the aspect ratio.FitResizeMode({int? width, int? height}): Fits the image within the specified dimensions while maintaining the aspect ratio. At least one ofwidthorheightmust be provided. If only one dimension is provided, the other is scaled proportionally. If the image is smaller than the specified dimensions, it will not be scaled up.
Controls how the source's EXIF orientation tag is handled. Output never carries orientation metadata (this package strips metadata), so the tag must be baked into the pixels to survive conversion.
apply(default): Bakes the EXIF orientation into the output pixels so the result is visually upright on every platform. A 90Β°/270Β° rotation swaps the output width and height, and the resize is evaluated against the oriented (display) dimensions.ignore: Encodes the decoded pixel buffer as-is, ignoring the orientation tag. Use this to keep the raw pixel layout when orientation is handled elsewhere.
The iOS/macOS implementation uses the ImageIO and Core Graphics frameworks via FFI bindings:
- Decoding:
CGImageSourceCreateWithDatareads input data. - Resizing:
CGBitmapContextCreatecreates a new bitmap context with the target dimensions.CGContextDrawImagedraws the original image into the context, scaling it in the process.CGContextSetInterpolationQualityis set to high for better quality.CGBitmapContextCreateImagecreates a newCGImagefrom the context.
- Encoding:
CGImageDestinationCreateWithDataencodes the finalCGImageto the target format. - Quality: Uses
kCGImageDestinationLossyCompressionQualityfor JPEG/HEIC/AVIF. ImageIO's AVIF encoder does not encode at a compression quality of 1.0, so AVIF at quality 100 is encoded at 0.99, the same as quality 99.
Note: Every conversion renders through a fixed 8-bit sRGB context β even when no resize is requested β so iOS/macOS output is always 8-bit sRGB. 16-bit and wide-gamut (e.g. Display P3) sources are not preserved, keeping output consistent with the Android and Web backends.
The Android implementation decodes with ImageDecoder or BitmapFactory and encodes with Bitmap.compress, or for AVIF with MediaCodec and MediaMuxer:
- Decoding: On Android 9+ with
ExifOrientationPolicy.apply,ImageDecoder.decodeBitmapdecodes and β viasetTargetSizein its header callback β resizes in a single pass, so the full-resolution bitmap is never materialized.ImageDecoderalso bakes in the source's EXIF orientation itself. OtherwiseBitmapFactory.decodeByteArraydecodes,ExifInterface+Matrixapply the orientation, andBitmap.createScaledBitmapresizes afterwards (filtering enabled).ExifOrientationPolicy.ignorealways takes this second path, becauseImageDecoderoffers no way to skip the orientation. - Compression:
Bitmap.compressencodes the final bitmap to the target format. WebP uses the explicitWEBP_LOSSY/WEBP_LOSSLESSformats on Android 11+ (quality 100 selects lossless, matching what the deprecated combinedWEBPformat did) andWEBPbelow that. - AVIF:
Bitmap.compresshas no AVIF format, so on Android 14+ (API 34) the bitmap is converted to 8-bit BT.601 full-range YUV 4:2:0, encoded with the platform's AV1 encoder (MediaCodec, constant-quality mode when the encoder offers it), and written byMediaMuxeras a HEIF grid of 512Γ512 AV1 tiles (one tile for smaller images) cropped to the image size. Below Android 14, which has neither the AV1 encoder nor AVIF muxing, and on Android 16 before QPR2 (SDK 36.0), whoseMediaMuxerapplies the HEVC minimum configuration size to AVIF tracks and so writes no image items, AVIF throwsUnsupportedFormatExceptionwith reasoncodecUnavailable. - Buffer Management:
ByteArrayOutputStreammanages output data.
Note: Use AVIF output on Android with care. It has no alpha channel: AVIF stores alpha as a separate monochrome AV1 image, which Android's AV1 encoders cannot produce (they only take YUV 4:2:0 input), so a source with alpha is composited onto black, as for JPEG. Its availability depends on the OS release (see above), so handle codecUnavailable and fall back to another format. The encoder is whichever AV1 encoder the device provides β one with a constant-quality mode is preferred β so output size, quality and encoding time vary by device.
The Windows implementation uses the Windows Imaging Component (WIC), a COM-based imaging stack in windowscodecs.dll, bound directly via dart:ffi (no native build step):
- Decoding:
IWICImagingFactory::CreateDecoderFromStreamβGetFramedecodes the input. - Normalization:
IWICFormatConverterre-renders the frame to a fixed 32bpp BGRA surface, so output is independent of the source's pixel format (8/16-bit, grayscale, indexed, CMYK). - Resizing:
IWICBitmapScalerwith high-quality cubic interpolation, when the target size differs. - Encoding:
CreateEncoder(by container GUID) βIWICBitmapFrameEncode::WriteSourceencodes into an in-memoryIStream. Quality for JPEG/HEIC/AVIF is set via the encoder'sImageQualityproperty. AVIF is written by the HEIF encoder with itsHeifCompressionMethodproperty set to AV1.
Note: HEIC output requires the OS HEVC/HEIF codec. It ships with Windows 11 22H2+; on older Windows the encoder is absent and HEIC throws UnsupportedFormatException with reason codecUnavailable. AVIF output requires the HEIF codec (the Microsoft Store "HEIF Image Extension") and a Media Foundation AV1 encoder, which Windows does not ship (the Store "AV1 Video Extension" only decodes) and GPU drivers for hardware with AV1 encoding can provide; without them AVIF throws UnsupportedFormatException with reason codecUnavailable. WebP output is not available (Windows provides a WebP decoder but no encoder). As with the other backends, every conversion renders through the fixed 8-bit surface even when no resize is requested.
The Linux implementation uses GdkPixbuf, the imaging library in the GLib/GTK stack that the Flutter Linux embedder already links, bound directly via dart:ffi (no native build step):
- Decoding:
GdkPixbufLoaderis fed the input bytes in memory and auto-detects the format via the installed loader modules. - Resizing:
gdk_pixbuf_scale_simplewithGDK_INTERP_HYPER(high-quality resampling) when the target size differs from the source. For JPEG output from a source with alpha, the resize happens in the samegdk_pixbuf_compositepass that flattens the alpha (see Encoding). - Encoding:
gdk_pixbuf_save_to_buffervwrites the target format to an in-memory buffer. Quality for lossy formats (JPEG/WebP/HEIC/AVIF) is passed via thequalityoption when the loader module supports it (gdk_pixbuf_format_is_save_option_supported); otherwise it has no effect, as for PNG. JPEG has no alpha channel, so a source with alpha is composited onto opaque black before JPEG encoding (transparent pixels become black).
Note: JPEG and PNG output are always available. WebP and HEIC depend on a writable GdkPixbuf loader module (e.g. webp-pixbuf-loader; the libheif GdkPixbuf loader only decodes, so it does not enable HEIC output), detected at runtime via gdk_pixbuf_get_formats. When none is installed, output throws UnsupportedFormatException with reason codecUnavailable. AVIF is saved through GdkPixbuf's avif format: on a GdkPixbuf built with glycin, glycin encodes it with libheif, which needs a libheif AV1 encoder plugin (e.g. libheif-plugin-aomenc); otherwise a loader module such as libavif-gdk-pixbuf encodes it. A missing format, glycin loader or AV1 encoder throws UnsupportedFormatException with reason codecUnavailable. GdkPixbuf decodes to an 8-bit RGB/RGBA surface (normalising bit depth like the other backends), but it performs no ICC colour management: unlike the iOS/macOS sRGB normalisation, non-sRGB or wide-gamut sources (e.g. Display P3, Adobe RGB) keep their original pixel values instead of being converted to sRGB. For the common case β sRGB or untagged JPEG/PNG, which is the bulk of real input β output matches the other backends; this caveat only affects colour-managed sources.
The Web implementation uses the Canvas API for image conversion:
- Decoding:
createImageBitmapdecodes the input Blob, applying the EXIF orientation (imageOrientation: 'from-image') or ignoring it ('none'). - Resizing & Rendering:
CanvasRenderingContext2D.drawImagerenders the image to a canvas with the target dimensions, effectively resizing it. - Encoding:
HTMLCanvasElement.toBlobencodes the canvas content to the target format. - Quality: Supports quality parameter for JPEG and WebP (0.0-1.0 scale).
Key Limitations:
- HEIC and AVIF output are not supported on Web platform.
- Output format depends on browser support (JPEG and PNG are universally supported, WebP is widely supported).