file_image_cropper 0.3.1 copy "file_image_cropper: ^0.3.1" to clipboard
file_image_cropper: ^0.3.1 copied to clipboard

Fast JPEG, PNG, and WebP cropping with a customizable Flutter UI and multithreaded Rust engine.

file_image_cropper #

A fast, multithreaded, file-first image cropper for Flutter, powered by a native Rust engine. Includes a ready-to-use crop screen, a fully customizable editor, and a code-driven API.

  • Android, iOS, Windows, macOS, and Linux
  • JPEG, PNG, static WebP, and platform-supported static HEIC input
  • JPEG, PNG, and WebP output
  • Free-form or fixed-aspect crop selection
  • Exact output dimensions when requested
  • Responsive UI while native processing runs in the background
  • Safe managed temporary files and atomic explicit outputs

Web is not supported.

Install #

flutter pub add file_image_cropper

Quick start: interactive crop screen #

The default editor lets the user choose any crop shape. Omitting outputSize keeps the selected crop's source-pixel dimensions.

import 'dart:io';

import 'package:file_image_cropper/file_image_cropper.dart';

final CropResult? result = await showFileImageCropper(
  context,
  FileImageCropperPage(
    input: File(imagePath),
    config: const CropperPageConfig(
      outputFormat: OutputFormat.jpeg,
      jpegOptions: JpegOptions(quality: 85),
    ),
  ),
);

if (result != null) {
  print(result.file.path);
}

Fixed aspect ratio and exact output size #

Supplying outputSize locks the default editor to the same aspect ratio and resizes the encoded result to those exact physical pixels.

FileImageCropperPage(
  input: File(imagePath),
  config: const CropperPageConfig(
    outputSize: PixelSize(width: 1080, height: 1080),
    outputFormat: OutputFormat.jpeg,
    jpegOptions: JpegOptions(quality: 85),
  ),
);

To lock only the selection shape without forcing an output resolution, set aspectRatio on the page and omit outputSize:

FileImageCropperPage(
  input: File(imagePath),
  aspectRatio: 4 / 3,
  config: const CropperPageConfig(
    outputFormat: OutputFormat.jpeg,
  ),
);

Output configuration #

Parameter Meaning
output Optional caller-owned destination file. Omit it for a managed temporary result.
overwrite Allows replacing an existing explicit output. Defaults to false.
outputSize Exact encoded width and height in physical pixels. Also locks the default UI aspect ratio.
resizeFilter Resize algorithm. Bilinear is the default; nearest is useful for pixel art.
aspectMismatchPolicy Controls how a crop is adjusted when its ratio differs from outputSize.
outputFormat jpeg, png, or webp. Defaults to PNG.
jpegOptions JPEG quality and background used when flattening transparency. Ignored for other formats.
webpOptions WebP quality and lossless mode. Ignored for other formats.

For photographs, JPEG quality 80-90 is usually the fastest and most practical choice. WebP generally produces smaller files but takes longer to encode. PNG is best when lossless pixels or transparency are required.

Crop through code #

The UI is optional. Use crop() directly when the crop region is already known:

final CropResult result = await crop(
  CropRequest(
    input: File('input.jpg'),
    region: const CropRegion.centered(aspectRatio: 4 / 3),
    outputSize: const PixelSize(width: 1200, height: 900),
    outputFormat: OutputFormat.webp,
    webpOptions: const WebpOptions(quality: 84),
  ),
);

print(result.file.path);

Available crop regions:

Region Use case
CropRegion.normalized(...) Fractions from 0 to 1; recommended for custom Flutter crop overlays.
CropRegion.centered(...) Largest centered crop with a requested aspect ratio.
CropRegion.pixels(...) Exact coordinates in orientation-normalized source pixels.

An inspection call is not required before normalized or centered crops. All regions are resolved against the orientation-normalized image, so callers do not need to reproduce JPEG EXIF rotations.

Using picker packages #

The input is always a dart:io File. No copy is needed for common picker results:

// image_picker or another API returning XFile:
final File input = File(xFile.path);

// photo_manager:
final File? input = await asset.originFile;

file_image_cropper does not depend on a picker package.

Customize the crop UI #

FileImageCropperPage exposes builders for the app bar, body, editor, controls, confirm/cancel buttons, processing state, errors, image, loading state, and crop overlay.

FileImageCropperPage(
  input: input,
  config: const CropperPageConfig(
    outputSize: PixelSize(width: 1200, height: 800),
    outputFormat: OutputFormat.jpeg,
  ),
  appBarBuilder: (context, actions) => AppBar(
    title: const Text('Edit photo'),
    leading: IconButton(
      onPressed: actions.cancel,
      icon: const Icon(Icons.arrow_back),
    ),
  ),
  overlayStyle: const CropOverlayStyle(
    maskColor: Color(0xaa000000),
    borderColor: Colors.amber,
    handleColor: Colors.amber,
    handleShape: CropHandleShape.circle,
    gridRows: 3,
    gridColumns: 3,
  ),
  confirmButtonBuilder: (context, onPressed, processing) => FilledButton(
    onPressed: onPressed,
    child: Text(processing ? 'Saving...' : 'Save'),
  ),
);

For a completely custom screen, embed only CropEditor and use its controller:

final CropEditorController controller = CropEditorController();

CropEditor(
  input: input,
  controller: controller,
  aspectRatio: 4 / 3,
  overlayBuilder: (context, details) => MyCropOverlay(details: details),
);

final CropResult result = await crop(
  CropRequest(input: input, region: controller.region),
);

The editor supports moving and resizing the selection, edge and corner handles, double-tap reset, and pan/zoom for precise work. requestBuilder and executor are injectable for custom request construction, state management, and testing.

Explicit and managed outputs #

When output is omitted, the package creates a managed result under Directory.systemTemp/file_image_cropper:

final CropResult result = await crop(
  CropRequest(
    input: input,
    region: const CropRegion.centered(aspectRatio: 1),
    outputFormat: OutputFormat.jpeg,
  ),
);

assert(result.isManaged);
await result.delete();

Managed results remain valid until explicitly deleted. Bulk cleanup removes only package-owned files:

await clearTemporaryFiles(olderThan: const Duration(days: 1));

To choose the destination yourself:

final CropResult result = await crop(
  CropRequest(
    input: input,
    output: File('exports/cropped.jpg'),
    overwrite: false,
    region: const CropRegion.normalized(
      left: 0.1,
      top: 0.1,
      width: 0.8,
      height: 0.8,
    ),
    outputFormat: OutputFormat.jpeg,
  ),
);

assert(!result.isManaged);

The explicit output's parent directory must already exist. Existing files are rejected unless overwrite is true. Calling CropResult.delete() on an explicit caller-owned output is a safe no-op.

Inputs are opened read-only and are never copied, changed, or deleted. Outputs are written to a unique staging file and atomically committed after a successful encode.

Inspect image metadata #

Read dimensions, format, and orientation without decoding all pixels:

final ImageInfo info = await inspectImage(File('photo.webp'));
print('${info.width} x ${info.height}, ${info.format}');

Inspection is useful for validation and mapping custom viewport coordinates, but it is not required before a normal crop.

Formats and platforms #

Capability JPEG PNG static WebP static HEIC
Inspect All platforms All platforms All platforms Android 9+, iOS, macOS, Windows*, Linux**
Input All platforms All platforms All platforms Android 9+, iOS, macOS, Windows*, Linux**
Output All platforms All platforms All platforms Not supported

Supported platforms are Android, iOS, Windows, macOS, and Linux. Animated WebP/GIF or HEIF, AVIF, TIFF, and camera RAW formats are not supported.

HEIC desktop requirements #

HEIC uses the native platform decoder. Crop regions, resizing, and output options behave the same as for the other input formats.

  • On Windows, install Microsoft HEIF Image Extensions and any HEVC component required by the image.
  • On Linux, install your distribution's libheif runtime package with an HEVC decoder. The package does not bundle or download libheif.

If an optional codec is missing, inspectImage() and crop() throw HeicCodecUnavailableException with a platform-specific helpUri. Restart the application after installing the codec.

Performance #

Long-running work stays off the Flutter UI isolate. The native engine uses a bounded thread pool for larger operations and avoids parallel scheduling overhead for small images.

Maintainer source builds #

Normal applications do not need this configuration. Maintainers can explicitly build the native engine from source in the root application's pubspec.yaml:

hooks:
  user_defines:
    file_image_cropper:
      build_from_source: true

This mode requires Rust and the relevant cross-compilation tools.

License #

MIT. See LICENSE.

1
likes
110
points
212
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Fast JPEG, PNG, and WebP cropping with a customizable Flutter UI and multithreaded Rust engine.

Homepage
Repository (GitHub)
View/report issues

Topics

#image #crop #resize #ffi #rust

License

MIT (license)

Dependencies

code_assets, crypto, ffi, flutter, hooks, logging, native_toolchain_rust

More

Packages that depend on file_image_cropper

Packages that implement file_image_cropper