Open In App

Flutter - Build an Image Compressor App

Last Updated : 04 May, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

Many applications accept images of small size, to reduce image size we use different online applications to compress images while maintaining their quality. In this article, we will create an image compressor app in Flutter that compresses images with the help of available libraries in Flutter.

Build-an--Image--Compressor--App


Step-by-Step Implementation

Step 1: Create a new Flutter Application

Create a new Flutter application using the command Prompt. To create a new app, write the following command and run it.

flutter create app_name

To know more about it refer this article: Creating a Simple Application in Flutter

Step 2: Adding the Dependency

To add the dependency to the pubspec.yaml file, add  image_picker, flutter_image_compress, and path_provider as dependencies in the dependencies part of the pubspec.yaml file, as shown below:

Dart
dependencies:
     flutter:
       sdk: flutter
     image_picker: ^1.1.2
     flutter_image_compress: ^2.3.0
     path_provider: ^2.1.5

Now run the below command in the terminal.

flutter pub get

Or

Run the below command in the terminal.

flutter pub add flutter_image_compress image_picker path_provider


Step 3 : Import dependencies

To use libraries, import all of them in the main.dart file,

import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';


Step 4 : Working With main.dart

Add the boilerplate code below in main.dart to create a basic structure with an AppBar and body using a Scaffold.

Dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ImageCompressorApp(),
      debugShowCheckedModeBanner: false,
    );
  }
}

// This is the main widget of the app.
class ImageCompressorApp extends StatefulWidget {
  @override
  _ImageCompressorAppState createState() => _ImageCompressorAppState();
}

// This is the state class for the main widget.
class _ImageCompressorAppState extends State<ImageCompressorApp> {
  
  // This function builds the user interface.
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('GFG Image Compression'),
        // Set app bar color.
        backgroundColor: Colors.green,
        // Set text color
        foregroundColor: Colors.white,
      ),
      body: SingleChildScrollView(
        // Code Here
      ),
    );
  }
}


Step 5 : Initialize variables

Intialize required variables.

Dart
// Stores the original image.
File? _selectedImage;
// Stores the compressed image.
File? _compressedImage;
// Checks if compression is in progress.
bool _isCompressing = false;
// Stores error messages.
String? _error;


Step 6 : Create methods

Create methods to select an image from the gallery and compress it using the image_picker and flutter_image_compress packages.

Dart
// Function to pick an image from the gallery.
Future<void> pickImage() async {
    try {
          final picker = ImagePicker();
          final pickedFile = await picker.pickImage(source: ImageSource.gallery);
        
          if (pickedFile != null) {
            setState(() {
                  // Store the selected image.
                  _selectedImage = File(pickedFile.path);
                  // Reset previous compressed image.
                  _compressedImage = null;
                  // Clear any previous error.
                  _error = null;
                  // Show that compression is starting.
                  _isCompressing = true;
            });
            // Compress the selected image.
            await compressImage(_selectedImage!);
          }
    } catch (e) {
      setState(() {
            // Store error message.
            _error = 'Error picking image: $e';
            // Stop compressing state.
            _isCompressing = false;
      });
    }
}

// Function to compress the selected image.
Future<void> compressImage(File image) async {
    try {
          // Get a temporary directory to store the compressed image.
          final tempDir = await getTemporaryDirectory();
          final targetPath = '${tempDir.path}/compressed_image.jpg';
        
          // Compress the image and store it in the new location.
          var result = await FlutterImageCompress.compressAndGetFile(
            image.absolute.path,
            targetPath,
            // Set image quality (lower value = more compression).
            quality: 85,
          );
        
          if (result != null) {
            final compressedFile = File(result.path);
            if (await compressedFile.exists()) {
                  final originalSize = await image.length();
                  final compressedSize = await compressedFile.length();
            
                  setState(() {
                        // Save the compressed image.
                        _compressedImage = compressedFile;
                        // Compression is done.
                        _isCompressing = false;
                        // Clear any error messages.
                        _error = null;
                  });
            
                  // Print the sizes of original and compressed images.
                  print('Original size: ${(originalSize / 1024).toStringAsFixed(2)} KB');
                  print('Compressed size: ${(compressedSize / 1024).toStringAsFixed(2)} KB');
            } else {
                  throw Exception('Compressed file not found at path: $targetPath');
            }
          } else {
                  throw Exception('Compression returned null path');
          }
    } catch (e) {
          setState(() {
                // Store error message.
                _error = 'Error compressing image: $e';
                // Stop compressing state.
                _isCompressing = false;
          });
          print('Error compressing image: $e');
    }
}


Step 7 : Develop UI

- ElevatedButton : Used to select images from the gallery.

Dart
 // Button to pick and compress an image.
ElevatedButton(
      style: ElevatedButton.styleFrom(
                  backgroundColor: Colors.green, 
                  foregroundColor: Colors.white
          ),
      onPressed: _isCompressing
          ? null
          : pickImage, // Disable when compressing.
      child: Text(_isCompressing
          ? 'Compressing...' // Show progress message.
          : 'Pick and Compress Image'),
),


- Error Container : Display the error wrapped in a container for the user.

Dart
if (_error != null) ...[
  // Show error message if any.
  const SizedBox(height: 16),
  Container(
        padding: const EdgeInsets.all(8),
        // Background color for error message.
        color: Colors.red.shade100,
        child: Text(
              _error!,
              // Red text color.
              style: const TextStyle(color: Colors.red),
        ),
  ),
],


- Original & Compressed Image : Display both the original and compressed image files using the Image.file() widget.

Dart
if (_selectedImage != null) ...[
  // Show original image.
  const SizedBox(height: 16),
  const Text(
    'Original Image',
     style:TextStyle(
             fontSize: 16,
             fontWeight: FontWeight.bold
            ),
  ),
  const SizedBox(height: 8),
  Image.file(_selectedImage!),
],
if (_compressedImage != null) ...[
  // Show compressed image.
  const SizedBox(height: 16),
  const Text(
    'Compressed Image',
     style: TextStyle(
             fontSize: 16,
             fontWeight: FontWeight.bold
            ),
  ),
  const SizedBox(height: 8),
  Image.file(_compressedImage!),
]


- Show values : Display the original image size, compressed image size, and the percentage reduction using the code below.

Dart
  // Show original and compressed image sizes.
FutureBuilder<List<int>>(
    future: Future.wait([
          _selectedImage!.length(),
          _compressedImage!.length(),
    ]),
    builder: (context, snapshot) {
          if (snapshot.hasData) {
                final originalSize = snapshot.data![0] / 1024;
                final compressedSize = snapshot.data![1] / 1024;
                final savings =((originalSize - compressedSize) / originalSize * 100);
            
                return Padding(
                  padding: const EdgeInsets.only(top: 8),
                  child: Text(
                        'Original: ${originalSize.toStringAsFixed(2)} KB\n'
                        'Compressed: ${compressedSize.toStringAsFixed(2)} KB\n'
                        'Reduced by: ${savings.toStringAsFixed(1)}%',
                        style: const TextStyle(fontSize: 14),
                  ),
                );
          }
          // Return empty widget if no data.
          return const SizedBox();
    },
),


Complete Source Code

main.dart:

Dart
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ImageCompressorApp(),
      debugShowCheckedModeBanner: false,
    );
  }
}

// This is the main widget of the app.
class ImageCompressorApp extends StatefulWidget {
  @override
  _ImageCompressorAppState createState() => _ImageCompressorAppState();
}

// This is the state class for the main widget.
class _ImageCompressorAppState extends State<ImageCompressorApp> {
  // Stores the original image.
  File? _selectedImage;
  // Stores the compressed image.
  File? _compressedImage;
  // Checks if compression is in progress.
  bool _isCompressing = false;
  // Stores error messages.
  String? _error;

  // Function to pick an image from the gallery.
  Future<void> pickImage() async {
    try {
      final picker = ImagePicker();
      final pickedFile = await picker.pickImage(source: ImageSource.gallery);

      if (pickedFile != null) {
        setState(() {
          // Store the selected image.
          _selectedImage = File(pickedFile.path);
          // Reset previous compressed image.
          _compressedImage = null;
          // Clear any previous error.
          _error = null;
          // Show that compression is starting.
          _isCompressing = true;
        });
        // Compress the selected image.
        await compressImage(_selectedImage!);
      }
    } catch (e) {
      setState(() {
        // Store error message.
        _error = 'Error picking image: $e';
        // Stop compressing state.
        _isCompressing = false;
      });
    }
  }

  // Function to compress the selected image.
  Future<void> compressImage(File image) async {
    try {
      // Get a temporary directory to store the compressed image.
      final tempDir = await getTemporaryDirectory();
      final targetPath = '${tempDir.path}/compressed_image.jpg';

      // Compress the image and store it in the new location.
      var result = await FlutterImageCompress.compressAndGetFile(
        image.absolute.path,
        targetPath,
        // Set image quality (lower value = more compression).
        quality: 85,
      );

      if (result != null) {
        final compressedFile = File(result.path);
        if (await compressedFile.exists()) {
          final originalSize = await image.length();
          final compressedSize = await compressedFile.length();

          setState(() {
            // Save the compressed image.
            _compressedImage = compressedFile;
            // Compression is done.
            _isCompressing = false;
            // Clear any error messages.
            _error = null;
          });

          // Print the sizes of original and compressed images.
          print(
              'Original size: ${(originalSize / 1024).toStringAsFixed(2)} KB');
          print(
              'Compressed size: ${(compressedSize / 1024).toStringAsFixed(2)} KB');
        } else {
          throw Exception('Compressed file not found at path: $targetPath');
        }
      } else {
        throw Exception('Compression returned null path');
      }
    } catch (e) {
      setState(() {
        // Store error message.
        _error = 'Error compressing image: $e';
        // Stop compressing state.
        _isCompressing = false;
      });
      print('Error compressing image: $e');
    }
  }

  // This function builds the user interface.
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('GFG Image Compression'),
        // Set app bar color.
        backgroundColor: Colors.green,
        // Set text color
        foregroundColor: Colors.white,
      ),
      body: SingleChildScrollView(
        // Add padding around the content.
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // Button to pick and compress an image.
            ElevatedButton(
              style: ElevatedButton.styleFrom(
                  backgroundColor: Colors.green, foregroundColor: Colors.white),
              onPressed: _isCompressing
                  ? null
                  : pickImage, // Disable when compressing.
              child: Text(_isCompressing
                  ? 'Compressing...' // Show progress message.
                  : 'Pick and Compress Image'),
            ),
            if (_error != null) ...[
              // Show error message if any.
              const SizedBox(height: 16),
              Container(
                padding: const EdgeInsets.all(8),
                // Background color for error message.
                color: Colors.red.shade100,
                child: Text(
                  _error!,
                  // Red text color.
                  style: const TextStyle(color: Colors.red),
                ),
              ),
            ],
            if (_selectedImage != null) ...[
              // Show original image.
              const SizedBox(height: 16),
              const Text(
                'Original Image',
                style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 8),
              Image.file(_selectedImage!),
            ],
            if (_compressedImage != null) ...[
              // Show compressed image.
              const SizedBox(height: 16),
              const Text(
                'Compressed Image',
                style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 8),
              Image.file(_compressedImage!),

              // Show original and compressed image sizes.
              FutureBuilder<List<int>>(
                future: Future.wait([
                  _selectedImage!.length(),
                  _compressedImage!.length(),
                ]),
                builder: (context, snapshot) {
                  if (snapshot.hasData) {
                    final originalSize = snapshot.data![0] / 1024;
                    final compressedSize = snapshot.data![1] / 1024;
                    final savings =
                        ((originalSize - compressedSize) / originalSize * 100);

                    return Padding(
                      padding: const EdgeInsets.only(top: 8),
                      child: Text(
                        'Original: ${originalSize.toStringAsFixed(2)} KB\n'
                        'Compressed: ${compressedSize.toStringAsFixed(2)} KB\n'
                        'Reduced by: ${savings.toStringAsFixed(1)}%',
                        style: const TextStyle(fontSize: 14),
                      ),
                    );
                  }
                  // Return empty widget if no data.
                  return const SizedBox();
                },
              ),
            ],
          ],
        ),
      ),
    );
  }
}

Output:



Next Article

Similar Reads