Skip to content

Commit 1e80fe1

Browse files
authored
feat: retain last finalized image. (#61)
1 parent 1c6f8fc commit 1e80fe1

File tree

3 files changed

+98
-31
lines changed

3 files changed

+98
-31
lines changed

lib/provider/image_loader.dart

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,68 @@
1+
import 'dart:io';
12
import 'dart:typed_data';
23

34
import 'package:flutter/material.dart';
45
import 'package:image/image.dart' as img;
56
import 'package:image_cropper/image_cropper.dart';
67
import 'package:image_picker/image_picker.dart';
8+
import 'package:path_provider/path_provider.dart';
9+
import 'package:path/path.dart' as path;
710

811
class ImageLoader extends ChangeNotifier {
912
img.Image? image;
1013
final List<img.Image> processedImgs = List.empty(growable: true);
14+
bool isLoading = false;
1115

12-
void pickImage({required int width, required int height}) async {
16+
Future<bool> pickImage({required int width, required int height}) async {
1317
final ImagePicker picker = ImagePicker();
1418
final XFile? file = await picker.pickImage(source: ImageSource.gallery);
15-
if (file == null) return;
19+
if (file == null) return false;
1620

1721
final croppedFile = await ImageCropper().cropImage(
1822
sourcePath: file.path,
19-
aspectRatio:
20-
CropAspectRatio(ratioX: width.toDouble(), ratioY: height.toDouble()),
23+
aspectRatio: CropAspectRatio(
24+
ratioX: width.toDouble(),
25+
ratioY: height.toDouble(),
26+
),
2127
);
22-
if (croppedFile == null) return;
28+
if (croppedFile == null) return false;
2329

2430
processedImgs.clear();
2531
image = await img.decodeImageFile(croppedFile.path);
2632

2733
notifyListeners();
34+
return true;
35+
}
36+
37+
Future<void> saveFinalizedImageBytes(Uint8List bytes) async {
38+
final dir = await getApplicationDocumentsDirectory();
39+
final file = File(path.join(dir.path, 'last_finalized.png'));
40+
await file.writeAsBytes(bytes);
41+
}
42+
43+
Future<void> loadFinalizedImage({
44+
required int width,
45+
required int height,
46+
}) async {
47+
isLoading = true;
48+
notifyListeners();
49+
50+
try {
51+
final dir = await getApplicationDocumentsDirectory();
52+
final file = File('${dir.path}/last_finalized.png');
53+
54+
if (await file.exists()) {
55+
final bytes = await file.readAsBytes();
56+
final decoded = img.decodeImage(bytes);
57+
if (decoded != null) {
58+
final resized = img.copyResize(decoded, width: width, height: height);
59+
image = resized;
60+
}
61+
}
62+
} finally {
63+
isLoading = false;
64+
notifyListeners();
65+
}
2866
}
2967

3068
Future<void> updateImage({

lib/view/image_editor.dart

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,20 @@ class _ImageEditorState extends State<ImageEditor> {
2828
List<img.Image> _rawImages = [];
2929
List<Uint8List> _processedPngs = [];
3030

31+
@override
32+
void initState() {
33+
super.initState();
34+
Future.microtask(() {
35+
final imgLoader = context.read<ImageLoader>();
36+
if (imgLoader.image == null) {
37+
imgLoader.loadFinalizedImage(
38+
width: widget.epd.width,
39+
height: widget.epd.height,
40+
);
41+
}
42+
});
43+
}
44+
3145
void _onFilterSelected(int index) {
3246
if (_selectedFilterIndex != index) {
3347
setState(() {
@@ -127,29 +141,36 @@ class _ImageEditorState extends State<ImageEditor> {
127141
),
128142
],
129143
),
130-
body: SafeArea(
131-
child: Padding(
132-
padding: const EdgeInsets.symmetric(horizontal: 8.0),
133-
child: _processedPngs.isNotEmpty
134-
? ImageList(
135-
key: ValueKey(_processedSourceImage),
136-
processedPngs: _processedPngs,
137-
epd: widget.epd,
138-
selectedIndex: _selectedFilterIndex,
139-
flipHorizontal: flipHorizontal,
140-
flipVertical: flipVertical,
141-
onFilterSelected: _onFilterSelected,
142-
onFlipHorizontal: toggleFlipHorizontal,
143-
onFlipVertical: toggleFlipVertical,
144-
)
145-
: const Center(
146-
child: Text(
147-
"Import an image to begin",
148-
style: TextStyle(color: Colors.grey, fontSize: 16),
149-
),
150-
),
151-
),
152-
),
144+
body: imgLoader.isLoading
145+
? const Center(
146+
child: Text('Loading...',
147+
style: TextStyle(
148+
color: colorBlack,
149+
fontSize: 14,
150+
)))
151+
: SafeArea(
152+
child: Padding(
153+
padding: const EdgeInsets.symmetric(horizontal: 8.0),
154+
child: _processedPngs.isNotEmpty
155+
? ImageList(
156+
key: ValueKey(_processedSourceImage),
157+
processedPngs: _processedPngs,
158+
epd: widget.epd,
159+
selectedIndex: _selectedFilterIndex,
160+
flipHorizontal: flipHorizontal,
161+
flipVertical: flipVertical,
162+
onFilterSelected: _onFilterSelected,
163+
onFlipHorizontal: toggleFlipHorizontal,
164+
onFlipVertical: toggleFlipVertical,
165+
)
166+
: const Center(
167+
child: Text(
168+
"Import an image to begin",
169+
style: TextStyle(color: Colors.grey, fontSize: 16),
170+
),
171+
),
172+
),
173+
),
153174
bottomNavigationBar: BottomActionMenu(
154175
epd: widget.epd,
155176
imgLoader: imgLoader,
@@ -193,8 +214,14 @@ class BottomActionMenu extends StatelessWidget {
193214
context: context,
194215
icon: Icons.add_photo_alternate_outlined,
195216
label: 'Import New',
196-
onTap: () {
197-
imgLoader.pickImage(width: epd.width, height: epd.height);
217+
onTap: () async {
218+
final success = await imgLoader.pickImage(
219+
width: epd.width, height: epd.height);
220+
if (success && imgLoader.image != null) {
221+
final bytes =
222+
Uint8List.fromList(img.encodePng(imgLoader.image!));
223+
await imgLoader.saveFinalizedImageBytes(bytes);
224+
}
198225
},
199226
),
200227
_buildActionButton(
@@ -210,11 +237,12 @@ class BottomActionMenu extends StatelessWidget {
210237
),
211238
);
212239
if (canvasBytes != null) {
213-
imgLoader.updateImage(
240+
await imgLoader.updateImage(
214241
bytes: canvasBytes,
215242
width: epd.width,
216243
height: epd.height,
217244
);
245+
await imgLoader.saveFinalizedImageBytes(canvasBytes);
218246
}
219247
},
220248
),

pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ dependencies:
4242
intl: ^0.19.0
4343
path_provider: ^2.0.15
4444
get_it: ^8.0.3
45+
path: ^1.9.1
4546

4647
dev_dependencies:
4748
flutter_test:

0 commit comments

Comments
 (0)