Image picker with compression
Pick several photos from gallery or camera, downscale and recompress them at pick time, and show removable thumbnails.
FlutterUploadsflutterimagepickercompression
Code
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
class PhotoPickerController extends ChangeNotifier {
PhotoPickerController({this.max = 6, this.maxEdge = 1600, this.quality = 80, this.maxBytes = 1500000});
final int max, maxEdge, quality, maxBytes;
final _picker = ImagePicker();
final List<File> files = [];
String? message;
Future<void> fromGallery() async {
final left = max - files.length;
if (left <= 0) return _warn('You can add up to $max photos.');
// limit/maxWidth/imageQuality make the plugin resize and re-encode natively.
final picked = await _picker.pickMultiImage(
maxWidth: maxEdge.toDouble(),
maxHeight: maxEdge.toDouble(),
imageQuality: quality,
limit: left,
);
await _accept(picked);
}
Future<void> fromCamera() async {
if (files.length >= max) return _warn('You can add up to $max photos.');
final shot = await _picker.pickImage(
source: ImageSource.camera,
maxWidth: maxEdge.toDouble(),
maxHeight: maxEdge.toDouble(),
imageQuality: quality,
);
await _accept(shot == null ? [] : [shot]);
}
Future<void> _accept(List<XFile> picked) async {
message = null;
for (final x in picked) {
if (files.length >= max) break;
final f = File(x.path);
if (await f.length() > maxBytes) {
message = 'Some photos were still too large and were skipped.';
continue;
}
files.add(f);
}
notifyListeners();
}
void remove(File f) {
files.remove(f);
notifyListeners();
}
void _warn(String text) {
message = text;
notifyListeners();
}
}
class PhotoStrip extends StatelessWidget {
const PhotoStrip({super.key, required this.controller});
final PhotoPickerController controller;
@override
Widget build(BuildContext context) {
return ListenableBuilder(
listenable: controller,
builder: (context, _) => Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Row(children: [
OutlinedButton.icon(onPressed: controller.fromGallery, icon: const Icon(Icons.photo_library), label: const Text('Gallery')),
const SizedBox(width: 8),
OutlinedButton.icon(onPressed: controller.fromCamera, icon: const Icon(Icons.photo_camera), label: const Text('Camera')),
]),
if (controller.message != null) Text(controller.message!),
SizedBox(
height: 88,
child: ListView(scrollDirection: Axis.horizontal, children: [
for (final f in controller.files)
Stack(children: [
Padding(
padding: const EdgeInsets.all(4),
child: ClipRRect(borderRadius: BorderRadius.circular(8), child: Image.file(f, width: 80, height: 80, fit: BoxFit.cover, cacheWidth: 160)),
),
PositionedDirectional(end: 0, top: 0, child: InkWell(onTap: () => controller.remove(f), child: const Icon(Icons.cancel, size: 20))),
]),
]),
),
]),
);
}
}
Needs: image_picker@^1.1.0
How it works
What it solves
Phone cameras produce photos of several megabytes. Uploading them raw is slow on mobile data and wastes storage, so listing apps should shrink images before they ever leave the device.
How it works
The controller asks image_picker to do the work natively: maxWidth, maxHeight and imageQuality make the plugin downscale and re-encode the JPEG at pick time, without decoding bitmaps in Dart. The gallery call also receives limit, so users cannot choose more photos than the slots left.
After picking, a byte-size guard skips anything still larger than maxBytes and sets a message. The strip widget shows thumbnails decoded at twice the display size via cacheWidth to keep memory low, with a cancel icon placed using PositionedDirectional.
Adapting it
Tune maxEdge and quality to your needs; 1600 pixels at 80 percent is a good listing default. To upload, read files and send them as multipart parts. If you need guaranteed output size, add a re-compress loop with the flutter_image_compress package.
Pitfalls
limitis honoured by newer platform versions only; themaxcheck in_acceptstill protects older ones.- PNG input ignores
imageQuality. - Add camera and photo library usage descriptions to the iOS plist.
How to use it
Add image_picker to pubspec.yaml and the iOS camera and photo library usage strings.
final photos = PhotoPickerController(max: 6);
PhotoStrip(controller: photos)
Upload photos.files when the form is submitted.
Licence: DEV3 Snippet Licence. Free to use, change and ship in personal and commercial projects. Attribution is appreciated, not required. Please do not redistribute snippets as a collection.