flutter_image_compress 0.6.5+1

  • Readme
  • Changelog
  • Example
  • Installing
  • 99

flutter_image_compress #

ImageCompress pub package GitHub GitHub stars Awesome

Compresses image as native plugin (Obj-C/Kotlin)

This library can works on Android and iOS.

Why don't you use dart to do it #

Q:Dart already has image compression libraries. Why use native?

A:For unknown reasons, image compression in Dart language is not efficient, even in release version. Using isolate does not solve the problem.

Usage #

dependencies:
  flutter_image_compress: ^0.6.5+1
import 'package:flutter_image_compress/flutter_image_compress.dart';

Use as:

See full example

There are several ways to use the library api.


  // 1. compress file and get a List<int>
  Future<List<int>> testCompressFile(File file) async {
    var result = await FlutterImageCompress.compressWithFile(
      file.absolute.path,
      minWidth: 2300,
      minHeight: 1500,
      quality: 94,
      rotate: 90,
    );
    print(file.lengthSync());
    print(result.length);
    return result;
  }

  // 2. compress file and get file.
  Future<File> testCompressAndGetFile(File file, String targetPath) async {
    var result = await FlutterImageCompress.compressAndGetFile(
        file.absolute.path, targetPath,
        quality: 88,
        rotate: 180,
      );

    print(file.lengthSync());
    print(result.lengthSync());

    return result;
  }

  // 3. compress asset and get List<int>.
  Future<List<int>> testCompressAsset(String assetName) async {
    var list = await FlutterImageCompress.compressAssetImage(
      assetName,
      minHeight: 1920,
      minWidth: 1080,
      quality: 96,
      rotate: 180,
    );

    return list;
  }

  // 4. compress List<int> and get another List<int>.
  Future<List<int>> testComporessList(List<int> list) async {
    var result = await FlutterImageCompress.compressWithList(
      list,
      minHeight: 1920,
      minWidth: 1080,
      quality: 96,
      rotate: 135,
    );
    print(list.length);
    print(result.length);
    return result;
  }

About common params #

minWidth and minHeight #

minWidth and minHeight are constraints on image scaling.

For example, a 4000*2000 image, minWidth set to 1920, minHeight set to 1080, the calculation is as follows:

// Using dart as an example, the actual implementation is Kotlin or OC.
import 'dart:math' as math;

void main() {
  var scale = calcScale(
    srcWidth: 4000,
    srcHeight: 2000,
    minWidth: 1920,
    minHeight: 1080,
  );

  print("scale = $scale"); // scale = 1.8518518518518519
  print("target width = ${4000 / scale}, height = ${2000 / scale}"); // target width = 2160.0, height = 1080.0
}

double calcScale({
  double srcWidth,
  double srcHeight,
  double minWidth,
  double minHeight,
}) {
  var scaleW = srcWidth / minWidth;
  var scaleH = srcHeight / minHeight;

  var scale = math.max(1.0, math.min(scaleW, scaleH));

  return scale;
}

If your image width is smaller than minWidth or height samller than minHeight, scale will be 1, that is, the size will not change.

rotate #

If you need to rotate the picture, use this parameter.

autoCorrectionAngle #

This property only exists in the version after 0.5.0.

And for historical reasons, there may be conflicts with rotate attributes, which need to be self-corrected.

Modify rotate to 0 or autoCorrectionAngle to false.

quality #

Quality of target image.

If format is png, the param will be ignored in iOS.

format #

Supports jpeg or png, default is jpeg.

The format class sign enum CompressFormat.

Heif and webp Partially supported.

Webp

Support android by the system api (speed very nice).

And support iOS, but However, no system implementation, using third-party libraries used, it is not recommended due to encoding speed. In the future, libwebp by google (c / c ++) may be used to do coding work, bypassing other three-party libraries, but there is no guarantee of implementation time.

HEIF(Heic)

Heif for iOS

Only support iOS 11+.

Heif for Android

Use https://developer.android.com/reference/androidx/heifwriter/HeifWriter.html to implemation.

Only support API 28+.

And may require hardware encoder support, does not guarantee that all devices above API28 are available

inSampleSize #

The param is only support android.

For a description of this parameter, see the Android official website.

keepExif #

If this parameter is true, EXIF information is saved in the compressed result.

Attention should be paid to the following points:

  1. Default value is false.
  2. Even if set to true, the direction attribute is not included.
  3. Only support jpg format, PNG format does not support.

Result #

The result of returning a List collection will not have null, but will always be an empty array.

The returned file may be null. In addition, please decide for yourself whether the file exists.

About List<int> and Uint8List #

You may need to convert List<int> to Uint8List to display images.

To use Uint8List, you need import package to your code like this:

img

final image = Uint8List.fromList(imageList)
ImageProvider provider = MemoryImage(Uint8List.fromList(imageList));

Usage in Image Widget:

List<int> image = await testCompressFile(file);
ImageProvider provider = MemoryImage(Uint8List.fromList(image));

Image(
  image: provider ?? AssetImage("img/img.jpg"),
),

Write to file usage:

void writeToFile(List<int> image, String filePath) {
  final file = File(filePath);
  file.writeAsBytes(image, flush: true, mode: FileMode.write);
}

Runtime Error #

Because of some support issues, all APIs will be compatible with format and system compatibility, and an exception (UnsupportError) may be thrown, so if you insist on using webp and heic formats, please catch the exception yourself and use it on unsupported devices jpeg compression.

Example:

Future<List<int>> compressAndTryCatch(String path) async {
    List<int> result;
    try {
      result = await FlutterImageCompress.compressWithFile(path,
          format: CompressFormat.heic);
    } on UnsupportedError catch (e) {
      print(e);
      result = await FlutterImageCompress.compressWithFile(path,
          format: CompressFormat.jpeg);
    }
    return result;
  }

Android #

You may need to update Kotlin to version 1.2.71(Recommend 1.3.50) or higher.

iOS #

No problems currently found.

Troubleshooting or common error #

Compressing returns null #

Sometimes, compressing will return null. You should check if you can read/write the file, and the parent folder of the target file must exist.

For example, use the path_provider plugin to access some application folders, and use a permission plugin to request permission to access SD cards on Android/iOS.

Android build error #

Caused by: org.gradle.internal.event.ListenerNotificationException: Failed to notify project evaluation listener.
        at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:86)
        ...
Caused by: java.lang.AbstractMethodError
        at org.jetbrains.kotlin.gradle.plugin.KotlinPluginKt.resolveSubpluginArtifacts(KotlinPlugin.kt:776)
        ...

See flutter/flutter/issues#21473

You need to upgrade your Kotlin version to 1.2.71+(recommended 1.3.31).

If Flutter supports more platforms (Windows, Mac, Linux, etc) in the future and you use this library, propose an issue or PR!

About EXIF information #

Using this library, EXIF information will be removed by default.

EXIF information can be retained by setting keepExif to true, but not direction information.

LICENSE #

The code under MIT style.

PNG/JPEG encoder #

Each using system API.

Webp encoder #

Use SDWebImageWebPCoder to encode the UIImage in iOS. (Under MIT)

Android code use the Android system api.

HEIF encoder #

Use iOS system api in iOS.

Use HeifWriter(androidx component by Google) to encode in androidP or higher.

About Exif handle code #

The iOS code was copied from dvkch/SYPictureMetadata, LICENSE

The android code was copied from flutter/plugin/image_picker and edit some. (BSD 3 style)

CHANGELOG #

0.6.5+1 #

Fix:

  • Web format error
  • Import header error for iOS.

0.6.5 #

New feature

  • Support webp on iOS.

0.6.4 #

New feature:

  • Add Params inSampleSize for methods.
  • Heif and webp Partially supported.

0.6.3 #

Fix:

  • Android: When the register of the calling plugin is not in the main process or there is no Activity.

0.6.2 #

Optimization:

  • Reduce the speed required for ios to add dependencies by copying the SYPictureMetadata source code into the project.

0.6.1 #

Fix:

  • autoCorrectionAngle switches image width and height.

New feature:

  • Keep exif (no have orientation), use keepExif

0.6.0 #

BREAKING CHANGE :

  • remove method getImageInfo.

For the time being, the follow-dev branch is no longer used, but only the master branch is needed to unify the pub version number.

New Feature:

  • It is now supported to set the compression target to png format.

0.5.2 #

Fix:

  • #49: A problem of reading Exif information.

0.5.1 #

Change reportError with flutter stable version.

Breaking Change: The autoCorrectionAngle parameter causes a number of situations to behave differently than 0.4.0. See readme for details.

0.5.0 #

(don't use)

Breaking Change: Because FlutterError.reportError method's param context type changed. So this library will add the constraints of flutter SDK so that users before 1.5.9 will not use version 0.5.0 incorrectly.

0.4.0 #

Some code has been added to ensure that parameters that do not pass in native do not trigger crash.

0.3.1 #

Fix:

  • Android close file output stream.

0.3.0 #

Fix:

  • optimize compress scale.

0.2.4 #

Updated Kotlin version

Breaking change. Migrate from the deprecated original Android Support Library to AndroidX. This shouldn't result in any functional changes, but it requires any Android apps using this plugin to also migrate if they're using the original support library.

0.2.3 #

change iOS return type

0.2.2 #

add some dart doc

0.2.1 #

update readme

0.2.0 #

The version number is updated so that people who can use the higher version of gradle can use it. see pr #8

if android run error, you must update your kotlin'version to 1.2.71+

0.1.4 #

add optional params rotate

fix bug

update example

0.1.3 #

fix the ios flutter.h bug

0.1.1 #

update readme

0.1.0 #

first version

example/lib/main.dart

import 'dart:async';
import 'dart:io';
import 'dart:math' as math;
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:path_provider/path_provider.dart' as path_provider;

import 'const/resource.dart';
import 'time_logger.dart';
// import 'package:image_picker/image_picker.dart';

void main() {
  runApp(new MyApp());
  FlutterImageCompress.showNativeLog = true;
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
  }

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> compress() async {
    final img = AssetImage("img/img.jpg");
    print("pre compress");
    final config = new ImageConfiguration();

    AssetBundleImageKey key = await img.obtainKey(config);
    final ByteData data = await key.bundle.load(key.name);

    final beforeCompress = data.lengthInBytes;
    print("beforeCompress = $beforeCompress");

    final result =
        await FlutterImageCompress.compressWithList(data.buffer.asUint8List());

    print("after = ${result?.length ?? 0}");
  }

  ImageProvider provider;

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(
        appBar: new AppBar(
          title: const Text('Plugin example app'),
        ),
        body: ListView(
          children: <Widget>[
            AspectRatio(
              child: Image(
                image: provider ?? AssetImage("img/img.jpg"),
                width: double.infinity,
                fit: BoxFit.contain,
              ),
              aspectRatio: 1 / 1,
            ),
            FlatButton(
              child: Text('CompressFile and rotate 180'),
              onPressed: _testCompressFile,
            ),
            FlatButton(
              child: Text('CompressAndGetFile and rotate 90'),
              onPressed: getFileImage,
            ),
            FlatButton(
              child: Text('CompressAsset and rotate 135'),
              onPressed: () => testCompressAsset("img/img.jpg"),
            ),
            FlatButton(
              child: Text('CompressList and rotate 270'),
              onPressed: compressListExample,
            ),
            FlatButton(
              child: Text('test compress auto angle'),
              onPressed: _compressAssetAndAutoRotate,
            ),
            FlatButton(
              child: Text('Test png '),
              onPressed: _compressPngImage,
            ),
            FlatButton(
              child: Text('Format transparent PNG'),
              onPressed: _compressTransPNG,
            ),
            FlatButton(
              child: Text('Restore transparent PNG'),
              onPressed: _restoreTransPNG,
            ),
            FlatButton(
              child: Text('Keep exif image'),
              onPressed: _compressImageAndKeepExif,
            ),
            FlatButton(
              child: Text('Convert to heic format and print the file url'),
              onPressed: _compressHeicExample,
            ),
            FlatButton(
              child: Text('Convert to webp format, Just support android'),
              onPressed: _compressAndroidWebpExample,
            ),
          ],
        ),
        floatingActionButton: FloatingActionButton(
          child: Icon(Icons.computer),
          onPressed: () => setState(() => this.provider = null),
          tooltip: "show origin asset",
        ),
      ),
    );
  }

  Future<Directory> getTemporaryDirectory() async {
    return Directory.systemTemp;
  }

  void _testCompressFile() async {
    final img = AssetImage("img/img.jpg");
    print("pre compress");
    final config = new ImageConfiguration();

    AssetBundleImageKey key = await img.obtainKey(config);
    final ByteData data = await key.bundle.load(key.name);
    final dir = await path_provider.getTemporaryDirectory();
    print('dir = $dir');

    File file = File("${dir.absolute.path}/test.png");
    file.writeAsBytesSync(data.buffer.asUint8List());

    List<int> list = await testCompressFile(file);
    ImageProvider provider = MemoryImage(Uint8List.fromList(list));
    this.provider = provider;
    setState(() {});
  }

  Future<String> getExampleFilePath() async {
    final img = AssetImage("img/img.jpg");
    print("pre compress");
    final config = new ImageConfiguration();

    AssetBundleImageKey key = await img.obtainKey(config);
    final ByteData data = await key.bundle.load(key.name);
    final dir = await path_provider.getTemporaryDirectory();

    File file = File("${dir.absolute.path}/test.png");
    file.writeAsBytesSync(data.buffer.asUint8List());
    return file.absolute.path;
  }

  void getFileImage() async {
    final img = AssetImage("img/img.jpg");
    print("pre compress");
    final config = new ImageConfiguration();

    AssetBundleImageKey key = await img.obtainKey(config);
    final ByteData data = await key.bundle.load(key.name);
    final dir = await path_provider.getTemporaryDirectory();

    File file = File("${dir.absolute.path}/test.png");
    file.writeAsBytesSync(data.buffer.asUint8List());

    final targetPath = dir.absolute.path + "/temp.jpg";
    final imgFile = await testCompressAndGetFile(file, targetPath);

    provider = FileImage(imgFile);
    setState(() {});
  }

  Future<List<int>> testCompressFile(File file) async {
    print("testCompressFile");
    final result = await FlutterImageCompress.compressWithFile(
      file.absolute.path,
      minWidth: 2300,
      minHeight: 1500,
      quality: 94,
      rotate: 180,
    );
    print(file.lengthSync());
    print(result.length);
    return result;
  }

  Future<File> testCompressAndGetFile(File file, String targetPath) async {
    print("testCompressAndGetFile");
    final result = await FlutterImageCompress.compressAndGetFile(
      file.absolute.path,
      targetPath,
      quality: 90,
      minWidth: 1024,
      minHeight: 1024,
      rotate: 90,
    );

    print(file.lengthSync());
    print(result.lengthSync());

    return result;
  }

  Future testCompressAsset(String assetName) async {
    print("testCompressAsset");
    final list = await FlutterImageCompress.compressAssetImage(
      assetName,
      minHeight: 1920,
      minWidth: 1080,
      quality: 96,
      rotate: 135,
    );

    this.provider = MemoryImage(Uint8List.fromList(list));
    setState(() {});
  }

  Future compressListExample() async {
    final data = await rootBundle.load("img/img.jpg");
    var list = List<int>.from(data.buffer.asUint8List());

    list = await testComporessList(list);

    final memory = Uint8List.fromList(list);
    setState(() {
      this.provider = MemoryImage(memory);
    });
  }

  Future<List<int>> testComporessList(List<int> list) async {
    final result = await FlutterImageCompress.compressWithList(
      list,
      minHeight: 1080,
      minWidth: 1080,
      quality: 96,
      rotate: 270,
    );
    print(list.length);
    print(result.length);
    return result;
  }

  void writeToFile(List<int> list, String filePath) {
    final file = File(filePath);
    file.writeAsBytes(list, flush: true, mode: FileMode.write);
  }

  void _compressAssetAndAutoRotate() async {
    final result = await FlutterImageCompress.compressAssetImage(
      R.IMG_AUTO_ANGLE_JPG,
      minWidth: 1000,
      quality: 95,
      // autoCorrectionAngle: false,
    );
    final u8list = Uint8List.fromList(result);
    this.provider = MemoryImage(u8list);
    setState(() {});
  }

  void _compressPngImage() async {
    final result = await FlutterImageCompress.compressAssetImage(
      R.IMG_HEADER_PNG,
      minWidth: 300,
      minHeight: 500,
    );

    final u8list = Uint8List.fromList(result);
    this.provider = MemoryImage(u8list);
    setState(() {});
  }

  void _compressTransPNG() async {
    final bytes =
        await getAssetImageUint8List(R.IMG_TRANSPARENT_BACKGROUND_PNG);
    final result = await FlutterImageCompress.compressWithList(
      bytes,
      minHeight: 100,
      minWidth: 100,
      format: CompressFormat.png,
    );

    final u8list = Uint8List.fromList(result);
    this.provider = MemoryImage(u8list);
    setState(() {});
  }

  void _restoreTransPNG() async {
    this.provider = AssetImage(R.IMG_TRANSPARENT_BACKGROUND_PNG);
    setState(() {});
  }

  void _compressImageAndKeepExif() async {
    final result = await FlutterImageCompress.compressAssetImage(
      R.IMG_AUTO_ANGLE_JPG,
      minWidth: 500,
      minHeight: 600,
      // autoCorrectionAngle: false,
      keepExif: true,
    );

    this.provider = MemoryImage(Uint8List.fromList(result));
    setState(() {});

    // final dir = (await path_provider.getTemporaryDirectory()).path;
    // final f = File("$dir/tmp.jpg");
    // f.writeAsBytesSync(result);
    // print("f.path = ${f.path}");
  }

  void _compressHeicExample() async {
    print("start compress");
    final logger = TimeLogger();
    logger.startRecoder();
    final tmpDir = (await getTemporaryDirectory()).path;
    final target = "$tmpDir/${DateTime.now().millisecondsSinceEpoch}.heic";
    final srcPath = await getExampleFilePath();
    final result = await FlutterImageCompress.compressAndGetFile(
      srcPath,
      target,
      format: CompressFormat.heic,
      quality: 90,
    );
    print("Compress heic success.");
    logger.logTime();
    print("src, path = $srcPath length = ${File(srcPath).lengthSync()}");
    print(
        "Compress heic result path: ${result.absolute.path}, size: ${result.lengthSync()}");
  }

  void _compressAndroidWebpExample() async {
    // Android compress very nice, but the iOS encode UIImage to webp is slow.
    final logger = TimeLogger();
    logger.startRecoder();
    print("start compress webp");
    final quality = 90;
    final tmpDir = (await getTemporaryDirectory()).path;
    final target =
        "$tmpDir/${DateTime.now().millisecondsSinceEpoch}-$quality.webp";
    final srcPath = await getExampleFilePath();
    final result = await FlutterImageCompress.compressAndGetFile(
      srcPath,
      target,
      format: CompressFormat.webp,
      minHeight: 800,
      minWidth: 800,
      quality: quality,
    );
    print("Compress webp success.");
    logger.logTime();
    print("src, path = $srcPath length = ${File(srcPath).lengthSync()}");
    print(
        "Compress webp result path: ${result.absolute.path}, size: ${result.lengthSync()}");

    provider = FileImage(result);
    setState(() {});
  }
}

Future<Uint8List> getAssetImageUint8List(String key) async {
  final byteData = await rootBundle.load(key);
  return byteData.buffer.asUint8List();
}

double calcScale({
  double srcWidth,
  double srcHeight,
  double minWidth,
  double minHeight,
}) {
  final scaleW = srcWidth / minWidth;
  final scaleH = srcHeight / minHeight;

  final scale = math.max(1.0, math.min(scaleW, scaleH));

  return scale;
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  flutter_image_compress: ^0.6.5+1

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:flutter_image_compress/flutter_image_compress.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
98
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
99
Learn more about scoring.

We analyzed this package on Apr 7, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.7.1
  • pana: 0.13.6
  • Flutter: 1.12.13+hotfix.8

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.0.0-dev.28.0 <3.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.11 1.14.12
meta 1.1.8
sky_engine 0.0.99
typed_data 1.1.6
vector_math 2.0.8