media_compressor 1.1.0-beta.2 copy "media_compressor: ^1.1.0-beta.2" to clipboard
media_compressor: ^1.1.0-beta.2 copied to clipboard

A Flutter plugin for efficient image and video compression on Android, iOS, and Web.

Media Compressor #

A Flutter plugin for compressing images and videos efficiently using native platform implementations โ€” now on Android, iOS, and Web.

Demo #

See the plugin in action:

Image Compression Demo Video Compression Demo

Latest Updates #

v1.1.0-beta.1 #

  • ๐ŸŒ Web support (beta) โ€” image (Canvas) and video (ffmpeg.wasm or MediaRecorder) compression in the browser
  • ๐Ÿ›‘ cancel() โ€” abort an in-flight video compression on every platform
  • ๐Ÿงน release() / releaseResult() โ€” free a compressed result (revokes blob URL on web, deletes temp file on mobile)
  • ๐Ÿ”’ Hardened: cache-scoped file deletion, single-flight video, managed native lifecycle
  • No breaking changes to the existing Dart API

v1.0.1 #

  • ๐Ÿ› Fixed iOS compilation error by adding the missing Flutter framework import
  • โœ… All functionality working as expected on Android and iOS

Features #

โœ… Image Compression โ€” quality and dimension control
โœ… Video Compression โ€” quality presets (low / medium / high)
โœ… Native Performance โ€” platform-specific compression for optimal results
โœ… Web Support (beta) โ€” in-browser compression via Canvas + ffmpeg.wasm / MediaRecorder
โœ… Progress Tracking โ€” real-time video progress on Android and Web
โœ… Cancel & Release โ€” abort jobs and free outputs
โœ… Error Handling โ€” comprehensive, typed error codes
โœ… Cross-platform โ€” Android, iOS, and Web
โœ… EXIF Orientation โ€” automatic image orientation correction

Platform Support #

Feature Android iOS Web (beta)
Image compression โœ… โœ… โœ…
Video compression โœ… Media3 (bitrate + resolution) โœ… AVAssetExportSession (presets) โœ… ffmpeg.wasm* or MediaRecorder
Progress โœ… ๐Ÿšง โœ…
Cancel โœ… โœ… โœ…
Output MP4 / JPEG (file path) MP4 / JPEG (file path) MP4 / WebM / JPEG (blob URL)

* ffmpeg.wasm is opt-in โ€” see Web video backends.

Installation #

Stable (Android + iOS):

dependencies:
  media_compressor: ^1.0.1

Beta (adds Web support):

dependencies:
  media_compressor: 1.1.0-beta.1

Then run:

flutter pub get

Usage #

Import the Package #

import 'package:media_compressor/media_compressor.dart';

Compress an Image #

final result = await MediaCompressor.compressImage(
  ImageCompressionConfig(
    path: '/path/to/image.jpg', // a blob URL on web (from image_picker)
    quality: 80,                // 0-100, where 100 is best quality
    maxWidth: 1920,             // Optional: max width in pixels
    maxHeight: 1080,            // Optional: max height in pixels
  ),
);

if (result.isSuccess) {
  print('Compressed image saved at: ${result.path}');
} else {
  print('Compression failed: ${result.error?.message}');
}

Compress a Video #

final result = await MediaCompressor.compressVideo(
  VideoCompressionConfig(
    path: '/path/to/video.mp4', // a blob URL on web
    quality: VideoQuality.medium,  // low, medium, high
  ),
);

if (result.isSuccess) {
  print('Compressed video saved at: ${result.path}');
} else {
  print('Compression failed: ${result.error?.message}');
}

Only one video compression runs at a time on every platform. A concurrent call returns the error code BUSY.

Cancel & Release #

// Abort the in-flight video compression (safe no-op if none).
await MediaCompressor.cancel();

// Free a result when you no longer need it
// (revokes the blob URL on web, deletes the temp file on mobile).
await MediaCompressor.release(result.path!);
await MediaCompressor.releaseResult(result);

โš ๏ธ Don't release a result that's still in use โ€” a revoked blob URL will no longer load in Image.network or a video player.

Progress Tracking (Android & Web) #

final eventChannel = EventChannel('native_compressor/progress');

eventChannel.receiveBroadcastStream().listen((event) {
  final percentage = event['percentage'] as int;
  print('Compression progress: $percentage%');
});

Video Quality Presets #

enum VideoQuality {
  low,     // 480p โ€” smaller file
  medium,  // 720p โ€” balanced (recommended)
  high,    // 1080p โ€” higher quality
}

Compression Result #

class CompressionResult {
  final bool isSuccess;
  final String? path;              // file path (mobile) or blob URL (web)
  final CompressionError? error;
  bool get isFailure => !isSuccess;
}

Error Handling #

class CompressionError {
  final String code;      // programmatic code
  final String message;   // human-readable message
  final dynamic details;  // optional platform details
}

Common error codes:

  • INVALID_ARGUMENT โ€” invalid arguments (missing path, quality out of range, bad dimensions)
  • FILE_NOT_FOUND โ€” input file doesn't exist
  • COMPRESSION_ERROR โ€” native/browser compression failed
  • NULL_RESULT โ€” compression returned null/empty
  • TIMEOUT โ€” exceeded the timeout
  • CANCELLED โ€” aborted via cancel()
  • BUSY โ€” another video compression is already in progress
  • UNSUPPORTED / UNSUPPORTED_PLATFORM โ€” no encoder available on this platform
  • LOAD_ERROR / PLAYBACK_ERROR โ€” web: failed to load/play the source
  • INPUT_TOO_LARGE โ€” web: source exceeds the in-browser size limit
  • UNKNOWN_ERROR โ€” unexpected error

Working with Results on Web #

On web, result.path is a blob object URL (e.g. blob:http://...), not a filesystem path. Use network/blob-aware APIs:

// Preview an image
Image.network(result.path!);

// Read bytes / size cross-platform (works for mobile paths AND web blob URLs)
final xfile = XFile(result.path!);
final bytes = await xfile.readAsBytes();
final size  = await xfile.length();

File(result.path) does not work on web โ€” avoid dart:io in shared code.

Web video backends #

  • MediaRecorder (default, zero setup): canvas re-encode. WebM on Chromium/Firefox, MP4 on Safari/iOS, best-effort audio. Real-time; bitrate is a hint; progress is a time estimate.
  • ffmpeg.wasm (opt-in, recommended for production): register a global window.mediaCompressorFfmpeg (shim provided in the header of media_compressor_web.dart) for off-thread H.264/MP4 with enforced bitrate and exact progress. Requires cross-origin isolation in web/index.html (COOP: same-origin, COEP: require-corp) and a review of the libx264 (LGPL/GPL) licensing for your use case.

Platform-specific Setup #

Android #

Add the following permissions to your AndroidManifest.xml (only if reading shared storage on API โ‰ค 32):

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="32" />
</manifest>

Note: For Android 13+ (API 33+), no storage permissions are needed for app-specific directories.

iOS #

Add the following to your Info.plist:

<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs access to compress photos and videos.</string>

Web #

No setup is required for the default MediaRecorder backend. For the ffmpeg.wasm backend, add the COOP/COEP headers and the shim (see Web video backends).

API Reference #

MediaCompressor #

Static entry point for all compression operations.

Method Returns Description
compressImage(ImageCompressionConfig) Future<CompressionResult> Compress an image
compressVideo(VideoCompressionConfig, {Duration? timeout}) Future<CompressionResult> Compress a video (default timeout 5 min)
cancel() Future<void> Abort the in-flight video job
release(String path) Future<void> Free a compressed result
releaseResult(CompressionResult) Future<void> Release result.path if successful

ImageCompressionConfig #

ImageCompressionConfig({
  required String path,
  int quality = 80,   // 0-100
  int? maxWidth,
  int? maxHeight,
})

VideoCompressionConfig #

VideoCompressionConfig({
  required String path,
  VideoQuality quality = VideoQuality.medium,
})

Video Compression Details

  • Android: AndroidX Media3 Transformer โ€” H.264/MP4, target resolution and bitrate per preset, audio preserved, progress events.
  • iOS: AVAssetExportSession โ€” system presets (low/medium/high), MP4, audio preserved, network-optimized.
  • Web: ffmpeg.wasm (H.264/MP4, enforced bitrate) when available; otherwise MediaRecorder (WebM/MP4, best-effort audio, real-time).
Quality Resolution Use Case
low 480p Quick sharing, minimal size
medium 720p General sharing (recommended)
high 1080p High-quality archival

Complete Example #

import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:media_compressor/media_compressor.dart';

class CompressionExample extends StatefulWidget {
  const CompressionExample({super.key});
  @override
  State<CompressionExample> createState() => _CompressionExampleState();
}

class _CompressionExampleState extends State<CompressionExample> {
  Uint8List? _bytes;
  String? _status;

  Future<void> _compressImage() async {
    final image = await ImagePicker().pickImage(source: ImageSource.gallery);
    if (image == null) return;

    final result = await MediaCompressor.compressImage(
      ImageCompressionConfig(
        path: image.path,
        quality: 80,
        maxWidth: 1920,
        maxHeight: 1080,
      ),
    );

    if (result.isSuccess) {
      // Cross-platform read (file path on mobile, blob URL on web).
      final bytes = await XFile(result.path!).readAsBytes();
      setState(() {
        _bytes = bytes;
        _status = 'Compressed: ${(bytes.length / 1024).toStringAsFixed(1)} KB';
      });
      await MediaCompressor.release(result.path!);
    } else {
      setState(() => _status = 'Error: ${result.error?.message}');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Media Compressor')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (_bytes != null) SizedBox(height: 240, child: Image.memory(_bytes!)),
            if (_status != null)
              Padding(padding: const EdgeInsets.all(16), child: Text(_status!)),
            ElevatedButton(
              onPressed: _compressImage,
              child: const Text('Compress Image'),
            ),
          ],
        ),
      ),
    );
  }
}

Best Practices #

  1. Quality Settings โ€” start with 80-85 for images: good compression, minimal loss.
  2. Dimension Limits โ€” set maxWidth/maxHeight to avoid memory spikes on large images.
  3. Error Handling โ€” always check result.isSuccess before using the output.
  4. Release Outputs โ€” call release() when done, especially on web.
  5. Timeout โ€” increase the timeout for large videos.
  6. Web Video โ€” output may be WebM on the fallback path; encoding runs in real time. Use the ffmpeg.wasm backend for MP4 + faster-than-realtime + exact progress.

Performance Tips #

  • Image compression is fast (typically milliseconds).
  • Video compression can take seconds to minutes depending on file size.
  • Compress files sequentially โ€” concurrent video calls return BUSY.
  • Show a progress indicator during video compression.

Troubleshooting #

"File not found" error โ€” verify the path and permissions.

Video compression timeout โ€” increase the timeout, lower the quality, check storage.

Out-of-memory errors โ€” reduce maxWidth/maxHeight; process sequentially; on web, keep source videos within the in-browser size limit.

Web: result.path won't load โ€” it's a blob URL; use Image.network / a network video source, not File(...).

Platform-Specific Features #

For detailed bitrates, resolutions, codecs, and per-platform behavior, see PLATFORM_FEATURES.md.

Contributing #

Contributions are welcome! Please feel free to submit a Pull Request.

License #

This project is licensed under the MIT License โ€” see the LICENSE file for details.

Support #

For issues, feature requests, or questions, please file an issue on the GitHub repository.

Media Credits #

The demo uses sample media for showcasing compression features.

๐Ÿ“ท Image Credit #

Photo by Zhen Yao
from Unsplash

๐ŸŽฅ Video Credit #

Video by tham nguyen
from Pixabay


Made with โค๏ธ for the Flutter community

37
likes
140
points
671
downloads

Documentation

API reference

Publisher

verified publisherharikrishnancr.com

Weekly Downloads

A Flutter plugin for efficient image and video compression on Android, iOS, and Web.

Homepage
Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter, flutter_web_plugins, plugin_platform_interface, web

More

Packages that depend on media_compressor

Packages that implement media_compressor