Skip to main content

cropigo 1.0.2 copy "cropigo: ^1.0.2" to clipboard
cropigo: ^1.0.2 copied to clipboard

A unified image picker and cropper for Flutter. Easily pick and crop images with customizable output size, aspect ratio, shape, and native UI theming via a simple, null-safe API.

cropigo #

pub package license: MIT

cropigo is a modern, unified image picker + cropper for Flutter. Pick an image from the gallery or camera and crop it with fully customizable output size, aspect ratio, shape, compression, and native crop-screen theming — all through one simple, null-safe API.

It wraps and coordinates two proven plugins (image_picker + image_cropper) behind a single, ergonomic surface, so you get one consistent API, one result type, and one cancellation model instead of juggling two packages.

final result = await Cropigo.pickAndCrop(source: CropigoSource.gallery);
if (result != null) {
  imageFile = result.file; // ready to display or upload
}

Table of contents #


Features #

  • 📷 One call, two sources — gallery or camera through the same method.
  • ✂️ Native crop UI — uses the platform's own crop screen (fast, familiar, accessible) instead of a hand-rolled widget.
  • 📐 Custom aspect ratios — lock to square, 4:3, 16:9, 3:4, any custom ratio, or leave it free-form.
  • ⭕ Rectangle or circular crop mask — perfect for avatars.
  • 🗜️ Custom output size & compression — set maxWidth/maxHeight, format (JPG/PNG/WebP-fallback), and quality.
  • 🎨 Themeable crop screen — toolbar color, grid visibility, locked ratios, and more, via CropigoTheme.
  • 🧩 Null-safe, cancellation-aware result — cancelling the picker or crop screen returns null instead of throwing, so happy-path code stays clean.
  • 📏 Decoded dimensions out of the box — CropigoResult.width/height and a human-readable file size ("482 KB"), no extra image-decoding code needed in your app.

Installation #

Add it to your pubspec.yaml:

dependencies:
  cropigo: ^1.0.2

Then:

flutter pub get

Note on the name: verify cropigo is still unclaimed on pub.dev before publishing — package names are first-come, first-served and availability can change.

Platform setup #

cropigo builds on image_picker and image_cropper, so both packages' native setup steps apply.

Android #

In android/app/src/main/AndroidManifest.xml, make sure you request camera and storage permissions (gallery access on modern Android uses scoped storage and generally needs no extra permission, but camera does):

<uses-permission android:name="android.permission.CAMERA" />

In android/app/build.gradle, set a minimum SDK compatible with the crop UI:

android {
    defaultConfig {
        minSdkVersion 21
    }
}

If you use ProGuard/R8, no extra rules are typically required for the crop screen (UCrop), but if you hit a ClassNotFoundException in a release build, add:

-keep class com.yalantis.ucrop** { *; }
-keep interface com.yalantis.ucrop** { *; }

iOS #

In ios/Runner/Info.plist, add usage descriptions for camera and photo library access — iOS will crash at runtime without these:

<key>NSCameraUsageDescription</key>
<string>This app needs camera access to take a photo.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs photo library access to pick a photo.</string>

Quick start #

import 'package:cropigo/cropigo.dart';

Future<void> pickAvatar() async {
  final result = await Cropigo.pickAndCrop(
    source: CropigoSource.gallery,
    options: const CropigoOptions(
      aspectRatio: CropigoAspectRatio.square,
      shape: CropigoCropShape.oval,
      maxWidth: 512,
      maxHeight: 512,
      compressQuality: 85,
    ),
  );

  if (result == null) {
    // User cancelled the picker or the crop screen — nothing to do.
    return;
  }

  print('Picked ${result.width}x${result.height}, ${result.readableSize}');
  // Use result.file / result.path to display or upload the image.
}

Core concepts #

CropigoSource #

Where to pick the image from:

enum CropigoSource { gallery, camera }

CropigoOptions #

The single configuration object passed to every Cropigo call. Every field has a default, so you only override what you need:

Field Default Description
aspectRatio null (free-form) Locks the crop box to a CropigoAspectRatio.
shape CropigoCropShape.rectangle rectangle or oval.
maxWidth/maxHeight 1080 / 1080 Maximum output dimensions in pixels.
compressFormat CropigoCompressFormat.jpg jpg, png, or webp (falls back to png where unsupported).
compressQuality 90 0–100 output quality.
enableCrop true Set false to skip the crop screen entirely.
lockAspectRatioList square/4:3/16:9/3:4 Quick-select ratio presets shown on the crop screen (Android).
theme CropigoTheme.modern Visual customization of the crop screen.
imageQuality 100 Quality applied by the OS picker itself, before cropping.
preferredCameraDevice CropigoCameraDevice.rear rear or front, used when picking from the camera.

CropigoAspectRatio #

const CropigoAspectRatio(double width, double height)

Built-in presets: CropigoAspectRatio.square, .classic (4:3), .widescreen (16:9), .portrait (3:4). Pass any custom ratio, e.g. CropigoAspectRatio(2, 1), or leave CropigoOptions.aspectRatio as null for a fully free-form crop.

CropigoTheme #

Customizes the native crop screen's look:

const CropigoTheme({
  String toolbarTitle = 'Crop Image',
  Color? toolbarColor,
  Color? toolbarWidgetColor,
  Color? statusBarColor,
  Color? backgroundColor,
  Color? activeControlsWidgetColor,
  Color? cropFrameColor,
  Color? cropGridColor,
  bool showCropGrid = true,
  bool lockAspectRatio = false,
  bool hideBottomControls = false,
});

Use CropigoTheme.modern as a sensible starting point, or copyWith(...) to tweak individual fields.

CropigoResult #

Returned on success:

class CropigoResult {
  final File file;
  final int width;
  final int height;
  final int sizeInBytes;
  final String path;
  String get readableSize; // e.g. "482 KB"
}

Error handling #

cropigo treats user cancellation as a normal outcome, not an error: if the user backs out of the gallery/camera picker or the crop screen, every Cropigo method simply returns null. You don't need a try/catch for the common "changed their mind" path.

Genuine failures — most commonly a denied camera/photo-library permission, or an unexpected I/O/platform error — throw a CropigoException:

try {
  final result = await Cropigo.pickAndCrop(source: CropigoSource.camera);
  if (result == null) return; // cancelled, not an error
  // use result...
} on CropigoException catch (e) {
  switch (e.reason) {
    case CropigoFailureReason.permissionDenied:
      // Prompt the user to enable camera/photos permission in Settings.
      break;
    case CropigoFailureReason.unknown:
    default:
      // Log / show a generic error message.
      break;
  }
}

Recipes #

Circular profile photo #

final result = await Cropigo.pickAndCrop(
  source: CropigoSource.gallery,
  options: const CropigoOptions(
    aspectRatio: CropigoAspectRatio.square,
    shape: CropigoCropShape.oval,
    maxWidth: 512,
    maxHeight: 512,
  ),
);

Free-form crop, no aspect lock #

final result = await Cropigo.pickAndCrop(
  source: CropigoSource.gallery,
  options: const CropigoOptions(aspectRatio: null),
);

Pick without cropping #

final result = await Cropigo.pickOnly(source: CropigoSource.camera);

Crop an image you already have #

Useful when the image didn't come from the picker (e.g. downloaded, or produced by a previous step):

final result = await Cropigo.cropExisting(
  imagePath: existingFile.path,
  options: const CropigoOptions(aspectRatio: CropigoAspectRatio.widescreen),
);

Custom themed crop screen #

final result = await Cropigo.pickAndCrop(
  source: CropigoSource.gallery,
  options: CropigoOptions(
    theme: CropigoTheme.modern.copyWith(
      toolbarTitle: 'Adjust your photo',
      toolbarColor: Colors.indigo,
      toolbarWidgetColor: Colors.white,
      activeControlsWidgetColor: Colors.indigoAccent,
      showCropGrid: true,
      lockAspectRatio: true,
    ),
  ),
);

API reference summary #

Method Description
Cropigo.pickAndCrop({source, options}) Picks an image and (by default) opens the crop screen.
Cropigo.pickOnly({source, options}) Picks an image, applying size/quality constraints, no crop screen.
Cropigo.cropExisting({imagePath, options}) Opens the crop screen for an already-existing file.

All three return Future<CropigoResult?> and throw CropigoException on genuine failure (see Error handling).

FAQ #

Does this replace image_picker or image_cropper? No — it composes them behind one API. You can still depend on either directly for advanced use cases (e.g. multi-image picking) that fall outside cropigo's scope.

Does WebP output actually produce a .webp file? The underlying native crop libraries only support JPG/PNG encoders, so CropigoCompressFormat.webp currently falls back to PNG. This is called out explicitly so it's never a silent surprise; convert to WebP afterward with a dedicated image-encoding package if you need the file itself in that format.

Does it work on web/desktop? image_picker supports web; the native crop screen (image_cropper) is mobile-first (Android/iOS). On unsupported platforms, use Cropigo.pickOnly() to pick without the crop step.

Contributing #

Issues and pull requests are welcome. Please include tests for any new behavior and run flutter analyze + flutter test before submitting.

License #

MIT — see LICENSE.

2
likes
160
points
207
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

A unified image picker and cropper for Flutter. Easily pick and crop images with customizable output size, aspect ratio, shape, and native UI theming via a simple, null-safe API.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

cross_file, flutter, image_cropper, image_picker

More

Packages that depend on cropigo