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.
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.
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.
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
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' )),
)
],
)),
);
}
} |