feat: image rotation (suite)
parent
d063256dce
commit
1ce0d03198
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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.');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue