Grain flutter app
1import 'dart:io';
2
3import 'package:image/image.dart' as img;
4
5enum ResizeMode { cover, contain, stretch }
6
7class ResizeResult {
8 final File file;
9 final int width;
10 final int height;
11 final int size;
12 final String mime;
13 ResizeResult({
14 required this.file,
15 required this.width,
16 required this.height,
17 required this.size,
18 this.mime = 'image/jpeg',
19 });
20}
21
22Future<ResizeResult> resizeImage({
23 required File file,
24 int targetWidth = 2000,
25 int targetHeight = 2000,
26 int maxBytes = 1000000,
27 ResizeMode mode = ResizeMode.cover,
28}) async {
29 final bytes = await file.readAsBytes();
30 img.Image? image = img.decodeImage(bytes);
31 if (image == null) throw Exception('Could not decode image');
32
33 // Calculate scale
34 double scale;
35 if (mode == ResizeMode.cover) {
36 scale = [
37 targetWidth / image.width,
38 targetHeight / image.height,
39 ].reduce((a, b) => a > b ? a : b);
40 } else if (mode == ResizeMode.contain) {
41 scale = [
42 targetWidth / image.width,
43 targetHeight / image.height,
44 ].reduce((a, b) => a < b ? a : b);
45 } else {
46 scale = 1.0;
47 }
48
49 int newWidth = (image.width * scale).round();
50 int newHeight = (image.height * scale).round();
51
52 if (mode == ResizeMode.stretch) {
53 newWidth = targetWidth;
54 newHeight = targetHeight;
55 }
56
57 img.Image resized = img.copyResize(image, width: newWidth, height: newHeight);
58
59 // Binary search for best quality under maxBytes
60 int minQ = 0, maxQ = 101;
61 List<int> bestJpg = [];
62 while (maxQ - minQ > 1) {
63 int q = ((minQ + maxQ) / 2).round();
64 final jpg = img.encodeJpg(resized, quality: q);
65 if (jpg.length < maxBytes) {
66 minQ = q;
67 bestJpg = jpg;
68 } else {
69 maxQ = q;
70 }
71 }
72 if (bestJpg.isEmpty) {
73 // fallback: lowest quality
74 bestJpg = img.encodeJpg(resized, quality: minQ);
75 }
76
77 final outFile = File(file.path.replaceFirst(RegExp(r'\.(jpg|jpeg|png) ?'), '_resized.jpg'));
78 await outFile.writeAsBytes(bestJpg);
79
80 return ResizeResult(
81 file: outFile,
82 width: resized.width,
83 height: resized.height,
84 size: bestJpg.length,
85 mime: 'image/jpeg',
86 );
87}