feat: image rotation (suite)

release/mobdr-v0.0.1
Frédérik Benoist 2023-03-30 08:50:16 +02:00
parent d063256dce
commit 1ce0d03198
4 changed files with 80 additions and 41 deletions

View File

@ -81,8 +81,6 @@ class _CustomCameraPreviewState extends State<CustomCameraPreview> {
shape: BoxShape.circle), shape: BoxShape.circle),
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {
/// TODO FBE delete image on local storage
fileDelete((widget.photoFiles[index]));
setState(() { setState(() {
widget.photoFiles.removeAt(index); widget.photoFiles.removeAt(index);
}); });
@ -103,16 +101,6 @@ class _CustomCameraPreviewState extends State<CustomCameraPreview> {
); );
} }
Future<void> fileDelete(File file) async {
try {
if (await file.exists()) {
await file.delete();
}
} catch (e) {
print(e);
}
}
Positioned _rejectButton() { Positioned _rejectButton() {
return Positioned( return Positioned(
bottom: 5, bottom: 5,

View File

@ -1,6 +1,7 @@
// ignore_for_file: prefer_const_constructors // ignore_for_file: prefer_const_constructors
import 'dart:ui'; import 'dart:ui';
import 'dart:io';
import 'package:mobdr/config/constant.dart'; import 'package:mobdr/config/constant.dart';
@ -16,6 +17,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:path_provider/path_provider.dart';
import 'objectbox.dart'; import 'objectbox.dart';
import 'package:wakelock/wakelock.dart'; import 'package:wakelock/wakelock.dart';
@ -59,6 +61,8 @@ class MyApp extends StatelessWidget {
// This widget is the root of your application. // This widget is the root of your application.
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
initDirectories();
// Initialize all bloc provider used on this entire application here // Initialize all bloc provider used on this entire application here
return MultiBlocProvider( return MultiBlocProvider(
providers: [ providers: [
@ -117,4 +121,23 @@ class MyApp extends StatelessWidget {
), ),
); );
} }
void initDirectories() async {
// Get the application's document directory
final Directory documentsDir = await getApplicationDocumentsDirectory();
// Create a Directory object for the "photos" directory in the documents directory
final Directory photosDir = Directory('${documentsDir.path}/photos');
// Check if the "photos" directory exists
if (await photosDir.exists()) {
print(
'The "photos" directory already exists in the documents directory.');
} else {
// Create the "photos" directory if it does not exist
await photosDir.create();
print(
'The "photos" directory has been created in the documents directory.');
}
}
} }

View File

@ -68,7 +68,7 @@ class ObjectBox {
concurrentBox.removeAll(); concurrentBox.removeAll();
visiteBox.removeAll(); visiteBox.removeAll();
visiteTagBox.removeAll(); visiteTagBox.removeAll();
photoBox.removeAll(); //photoBox.removeAll();
//photoTypologyBox.removeAll(); //photoTypologyBox.removeAll();
// Add some demo data if the box is empty. // Add some demo data if the box is empty.

View File

@ -8,6 +8,8 @@ import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:image/image.dart' as img; import 'package:image/image.dart' as img;
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
import 'package:flutter/painting.dart' show ImageProvider;
import 'package:mobdr/config/constant.dart'; import 'package:mobdr/config/constant.dart';
import 'package:mobdr/main.dart'; import 'package:mobdr/main.dart';
@ -16,6 +18,8 @@ import 'package:mobdr/ui/home/photo_camera.dart';
import 'package:mobdr/model/photo_model.dart'; import 'package:mobdr/model/photo_model.dart';
import 'package:mobdr/db/box_photo.dart'; import 'package:mobdr/db/box_photo.dart';
// TODO Il faut supprimer les possibles photos du répertoire cache !
extension FileNameExtension on File { extension FileNameExtension on File {
String getFileName() { String getFileName() {
String fileName = path.split('/').last; String fileName = path.split('/').last;
@ -138,12 +142,8 @@ class _PhotoListPageState extends State<PhotoListPage> {
children: <Widget>[ children: <Widget>[
ClipRRect( ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(10)), borderRadius: BorderRadius.all(Radius.circular(10)),
child: Image.file( child: Image.file(File(photoData.image),
File(photoData.image), fit: BoxFit.cover, height: 100, width: 150),
fit: BoxFit.cover,
height: 100,
width: 150,
),
), ),
SizedBox( SizedBox(
width: 10, width: 10,
@ -289,8 +289,10 @@ class _PhotoListPageState extends State<PhotoListPage> {
behavior: HitTestBehavior.translucent, behavior: HitTestBehavior.translucent,
onTap: () async { onTap: () async {
await rotateAndReplaceImage( await rotateAndReplaceImage(
File(_photoData[index].image)); File(_photoData[index].image), 90);
setState(() => _listKey = GlobalKey()); setState(() {
_listKey = GlobalKey();
});
Fluttertoast.showToast( Fluttertoast.showToast(
msg: 'The image has been rotated'); msg: 'The image has been rotated');
}, },
@ -310,8 +312,14 @@ class _PhotoListPageState extends State<PhotoListPage> {
), ),
GestureDetector( GestureDetector(
behavior: HitTestBehavior.translucent, behavior: HitTestBehavior.translucent,
onTap: () { onTap: () async {
showPopupDeletePhoto(index, boxImageSize); await rotateAndReplaceImage(
File(_photoData[index].image), -90);
setState(() {
_listKey = GlobalKey();
});
Fluttertoast.showToast(
msg: 'The image has been rotated');
}, },
child: Container( child: Container(
padding: EdgeInsets.fromLTRB(5, 0, 5, 0), padding: EdgeInsets.fromLTRB(5, 0, 5, 0),
@ -345,8 +353,6 @@ class _PhotoListPageState extends State<PhotoListPage> {
/// if the user has validated photos /// if the user has validated photos
if (val == true) { if (val == true) {
savePhotos(); savePhotos();
} else {
resetPhotos();
} }
}); });
}, },
@ -384,12 +390,20 @@ class _PhotoListPageState extends State<PhotoListPage> {
); );
} }
void savePhotos() { Future<bool> evictImage(String imageURL) async {
final NetworkImage provider = NetworkImage(imageURL);
return await provider.evict();
}
void savePhotos() async {
if (photoFiles.length > 0) { if (photoFiles.length > 0) {
final List<Photo> _listPhotos = []; final List<Photo> _listPhotos = [];
final List<PhotoModel> _listPhotosModel = []; final List<PhotoModel> _listPhotosModel = [];
for (var myPhoto in photoFiles) { for (var myTmpPhoto in photoFiles) {
/// move jpg file to photo directory
final myPhoto = await moveFileFromTempToPhotosDir(myTmpPhoto);
/// database /// database
_listPhotos.add(Photo( _listPhotos.add(Photo(
id_visite: 0, id_visite: 0,
@ -412,15 +426,9 @@ class _PhotoListPageState extends State<PhotoListPage> {
_photoData.insertAll(0, _listPhotosModel); _photoData.insertAll(0, _listPhotosModel);
/// refresh widget /// refresh widget
setState(() => _listKey = GlobalKey()); setState(() {
} _listKey = GlobalKey();
} });
void resetPhotos() {
/// for all photos taken
for (var photo in photoFiles) {
/// delete image on local storage
deleteFile(photo);
} }
} }
@ -447,10 +455,13 @@ class _PhotoListPageState extends State<PhotoListPage> {
onPressed: () { onPressed: () {
int removeIndex = index; int removeIndex = index;
var removedItem = _photoData.removeAt(removeIndex); var removedItem = _photoData.removeAt(removeIndex);
// delete file on database // delete file on database
objectbox.delPhoto(removedItem.image_name); objectbox.delPhoto(removedItem.image_name);
// delete file on locale storages
// delete file on local storage
deleteFile(new File(removedItem.image)); deleteFile(new File(removedItem.image));
// This builder is just so that the animation has something // This builder is just so that the animation has something
// to work with before it disappears from view since the original // to work with before it disappears from view since the original
// has already been deleted. // has already been deleted.
@ -494,7 +505,22 @@ class _PhotoListPageState extends State<PhotoListPage> {
); );
} }
Future<void> rotateAndReplaceImage(File imageFile) async { Future<File> moveFileFromTempToPhotosDir(File tempFile) async {
// Get the application's document directory
final Directory documentsDir = await getApplicationDocumentsDirectory();
// Set the new file path with the original file name
final String newPath =
'${documentsDir.path}/photos/${tempFile.path.split('/').last}';
// Rename the file to move it to the documents directory
await tempFile.rename(newPath);
// Create and return a new File object for the new file
return File(newPath);
}
Future<void> rotateAndReplaceImage(File imageFile, int angle) async {
// Read the image file into a Uint8List // Read the image file into a Uint8List
Uint8List bytes = await imageFile.readAsBytes(); Uint8List bytes = await imageFile.readAsBytes();
@ -502,13 +528,15 @@ class _PhotoListPageState extends State<PhotoListPage> {
img.Image? image = img.decodeImage(bytes); img.Image? image = img.decodeImage(bytes);
// Rotate the image clockwise by 90 degrees // Rotate the image clockwise by 90 degrees
img.Image? rotatedImage = img.copyRotate(image!, angle: 10); img.Image? rotatedImage = img.copyRotate(image!, angle: angle);
// Encode the rotated image back into a Uint8List // Encode the rotated image back into a Uint8List
Uint8List rotatedBytes = img.encodePng(rotatedImage); Uint8List rotatedBytes = img.encodePng(rotatedImage);
// Overwrite the original file with the rotated image // Overwrite the original file with the rotated image
deleteFile(imageFile); await imageFile.writeAsBytes(rotatedBytes);
await imageFile.writeAsBytes(rotatedBytes, flush: true);
// remove the flutter cache image
FileImage(imageFile).evict();
} }
} }