Flutter – Build an Image Compressor App
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 be creating an Image compressor app in Flutter that will compress images with the help of available libraries in Flutter.
Step-by-Step Implementation
Step 1: Create a Project
From the terminal create a flutter project using the following command:
flutter create imagecompressor
Step 2: Add package
First, we need to add all dependencies required for this project in pubspec.yaml file. This can be done using the below command:
flutter pub add flutter_image_compress file_picker gallery_saver
Step 3: Import dependencies
To use libraries, import all of them in main.dart file,
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:file_picker/file_picker.dart';
import 'package:gallery_saver/gallery_saver.dart';
Step 4: Add permissions
To access device files and save images after compression in gallery, we need to allow read and write permissions to device storage. In AndroidManifest.xml file, inside <manifest> add following lines:-
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Step 5: Create Basic Widget
Now, create basic view, from which user can upload image and download compressed image.
Dart
class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false , title: 'Flutter App' , theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.green), useMaterial3: true , ), home: const MyHomePage(title: 'Image Compressor' ), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this .title}); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { @override void initState() { super.initState(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( centerTitle: true , backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text( widget.title, style: TextStyle(color: Colors.white), ), ), body: Center( child: Column( children: <Widget>[ Padding( padding: EdgeInsets.all(20), child: ElevatedButton( onPressed: () { print( "hello!" ); }, child: const Text( 'Pick a File' ), ), ) ], )), ); } } |
Output:
Step 6: Compress Image
Create function _pickFile(), using which we will select file from device.
Dart
Future<File> _pickFile() async { FilePickerResult? result = await FilePicker.platform.pickFiles(); if (result != null) { PlatformFile file = result.files.first; File pickedFile = File(file.path!); await compressImage(pickedFile); return File(compressedImage!.path); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text( "No file picked!" ))); throw Exception( 'No file picked' ); } } |
Create another function compressImage() which takes file as input, and then compress it.
Dart
Future< void > compressImage(File file) async { final filePath = file.absolute.path; final lastIndex = filePath.lastIndexOf(RegExp(r '.jp' )); final splitted = filePath.substring(0, (lastIndex)); final outPath = "${splitted}_out${filePath.substring(lastIndex)}" ; compressedImage = await FlutterImageCompress.compressAndGetFile( filePath, outPath, minWidth: 1000, minHeight: 1000, quality: 70); } |
Create _saveLocalImage() function, which will be invoked when user clicks on download button. It will save the compressed image in device gallery.
Full Source Code
Dart
import 'dart:io' ; import 'dart:ui' as ui; import 'package:flutter/material.dart' ; import 'package:flutter_image_compress/flutter_image_compress.dart' ; import 'package:file_picker/file_picker.dart' ; import 'package:gallery_saver/gallery_saver.dart' ; void main() { runApp( const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false , title: 'Flutter Demo' , theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.green), useMaterial3: true , ), home: const MyHomePage(title: 'Image Compressor' ), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this .title}); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { var key = GlobalKey(); XFile? compressedImage; File? image; Future< void > compressImage(File file) async { final filePath = file.absolute.path; final lastIndex = filePath.lastIndexOf(RegExp(r '.jp' )); final splitted = filePath.substring(0, (lastIndex)); final outPath = "${splitted}_out${filePath.substring(lastIndex)}" ; compressedImage = await FlutterImageCompress.compressAndGetFile( filePath, outPath, minWidth: 1000, minHeight: 1000, quality: 70); } Future< void > _saveLocalImage() async { try { GallerySaver.saveImage(compressedImage!.path); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text( "Image Saved to Galery!" ))); print(compressedImage!.path); } catch (e) { print( 'Error: $e' ); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text( "Error ocurred. Try again!" ))); } } Future<File> _pickFile() async { FilePickerResult? result = await FilePicker.platform.pickFiles(); if (result != null) { PlatformFile file = result.files.first; File pickedFile = File(file.path!); await compressImage(pickedFile); return File(compressedImage!.path); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text( "No file picked!" ))); throw Exception( 'No file picked' ); } } @override void initState() { super.initState(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( centerTitle: true , backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title,style: TextStyle(color: Colors.white),), ), body: Center( child: Column( children: <Widget>[ ElevatedButton( onPressed:() { _pickFile().then((compressedFile) { setState(() { image = compressedFile; }); }).catchError((error) { print( 'Error: $error' ); }); }, child: const Text( 'Pick a File' ), ), Container( height: 450, width: 300, child: image != null ? Column( children:[ Image.file(image!,width: 300,height: 400,) , ElevatedButton( onPressed:_saveLocalImage, child: const Text( "Download" ) ) ] ) : const Center(child: Text( 'No image selected' )), ) ], )), ); } } |