refactor: photo process
parent
ef3d131b4d
commit
1ce2765172
Binary file not shown.
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 147 KiB |
Binary file not shown.
|
|
@ -7,6 +7,11 @@ PODS:
|
|||
- device_info_plus (0.0.1):
|
||||
- Flutter
|
||||
- Flutter (1.0.0)
|
||||
- flutter_image_compress_common (1.0.0):
|
||||
- Flutter
|
||||
- Mantle
|
||||
- SDWebImage
|
||||
- SDWebImageWebPCoder
|
||||
- fluttertoast (0.0.2):
|
||||
- Flutter
|
||||
- Toast
|
||||
|
|
@ -17,6 +22,18 @@ PODS:
|
|||
- Flutter
|
||||
- image_picker_ios (0.0.1):
|
||||
- Flutter
|
||||
- libwebp (1.2.4):
|
||||
- libwebp/demux (= 1.2.4)
|
||||
- libwebp/mux (= 1.2.4)
|
||||
- libwebp/webp (= 1.2.4)
|
||||
- libwebp/demux (1.2.4):
|
||||
- libwebp/webp
|
||||
- libwebp/mux (1.2.4):
|
||||
- libwebp/demux
|
||||
- libwebp/webp (1.2.4)
|
||||
- Mantle (2.2.0):
|
||||
- Mantle/extobjc (= 2.2.0)
|
||||
- Mantle/extobjc (2.2.0)
|
||||
- ObjectBox (1.8.1)
|
||||
- objectbox_flutter_libs (0.0.1):
|
||||
- Flutter
|
||||
|
|
@ -26,9 +43,13 @@ PODS:
|
|||
- path_provider_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- permission_handler_apple (9.0.4):
|
||||
- Flutter
|
||||
- ReachabilitySwift (5.0.0)
|
||||
- SDWebImage (5.16.0):
|
||||
- SDWebImage/Core (= 5.16.0)
|
||||
- SDWebImage/Core (5.16.0)
|
||||
- SDWebImageWebPCoder (0.11.0):
|
||||
- libwebp (~> 1.0)
|
||||
- SDWebImage/Core (~> 5.15)
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
|
|
@ -46,13 +67,13 @@ DEPENDENCIES:
|
|||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`)
|
||||
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
|
||||
- image_gallery_saver (from `.symlinks/plugins/image_gallery_saver/ios`)
|
||||
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
||||
- objectbox_flutter_libs (from `.symlinks/plugins/objectbox_flutter_libs/ios`)
|
||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`)
|
||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`)
|
||||
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
||||
- wakelock (from `.symlinks/plugins/wakelock/ios`)
|
||||
|
|
@ -61,8 +82,12 @@ DEPENDENCIES:
|
|||
SPEC REPOS:
|
||||
trunk:
|
||||
- FMDB
|
||||
- libwebp
|
||||
- Mantle
|
||||
- ObjectBox
|
||||
- ReachabilitySwift
|
||||
- SDWebImage
|
||||
- SDWebImageWebPCoder
|
||||
- Toast
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
|
|
@ -74,6 +99,8 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
flutter_image_compress_common:
|
||||
:path: ".symlinks/plugins/flutter_image_compress_common/ios"
|
||||
fluttertoast:
|
||||
:path: ".symlinks/plugins/fluttertoast/ios"
|
||||
image_gallery_saver:
|
||||
|
|
@ -86,8 +113,6 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||
path_provider_foundation:
|
||||
:path: ".symlinks/plugins/path_provider_foundation/ios"
|
||||
permission_handler_apple:
|
||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||
shared_preferences_foundation:
|
||||
:path: ".symlinks/plugins/shared_preferences_foundation/ios"
|
||||
sqflite:
|
||||
|
|
@ -102,16 +127,20 @@ SPEC CHECKSUMS:
|
|||
connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a
|
||||
device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed
|
||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||
flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e
|
||||
fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0
|
||||
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||
image_gallery_saver: 6eb11e5a866e9ac2c8a98c74ef99a04fc62878b2
|
||||
image_picker_ios: 4a8aadfbb6dc30ad5141a2ce3832af9214a705b5
|
||||
libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef
|
||||
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
|
||||
ObjectBox: a7900d5335218cd437cbc080b7ccc38a5211f7b4
|
||||
objectbox_flutter_libs: 61d74196d924fbc773da5f5757d1e9fab7b3cc78
|
||||
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
|
||||
path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9
|
||||
permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce
|
||||
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
||||
SDWebImage: 2aea163b50bfcb569a2726b6a754c54a4506fcf6
|
||||
SDWebImageWebPCoder: 295a6573c512f54ad2dd58098e64e17dcf008499
|
||||
shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472
|
||||
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
|
||||
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ import 'dart:io';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:camera/camera.dart';
|
||||
import 'package:audioplayers/audioplayers.dart';
|
||||
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
||||
import 'package:mobdr/service/shared_prefs.dart';
|
||||
|
||||
class CustomCameraPreview extends StatefulWidget {
|
||||
final List<File> photoFiles;
|
||||
|
|
@ -16,6 +19,9 @@ class CustomCameraPreview extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _CustomCameraPreviewState extends State<CustomCameraPreview> {
|
||||
final AudioPlayer _audioPlayer = AudioPlayer();
|
||||
bool isTakingPhoto = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
|
|
@ -23,80 +29,88 @@ class _CustomCameraPreviewState extends State<CustomCameraPreview> {
|
|||
children: <Widget>[
|
||||
Positioned.fill(
|
||||
child: AspectRatio(
|
||||
aspectRatio: widget.cameraController.value.aspectRatio,
|
||||
child: CameraPreview(widget.cameraController)),
|
||||
aspectRatio: widget.cameraController.value.aspectRatio,
|
||||
child: CameraPreview(widget.cameraController),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 5,
|
||||
child: FloatingActionButton(
|
||||
heroTag: "camera",
|
||||
backgroundColor: Colors.white,
|
||||
onPressed: () async {
|
||||
//you can give limit that's user can take how many photo
|
||||
if (widget.photoFiles.length != 10) {
|
||||
//take a photo
|
||||
var videoFile = await widget.cameraController.takePicture();
|
||||
File file = File(videoFile.path);
|
||||
//add photo into files list
|
||||
widget.photoFiles.add(file);
|
||||
bottom: 5,
|
||||
child: FloatingActionButton(
|
||||
heroTag: "camera",
|
||||
backgroundColor: Colors.white,
|
||||
onPressed: () async {
|
||||
if (widget.photoFiles.length != 10 && !isTakingPhoto) {
|
||||
setState(() {
|
||||
isTakingPhoto = true;
|
||||
});
|
||||
|
||||
try {
|
||||
var dirCacheXFile =
|
||||
await widget.cameraController.takePicture();
|
||||
|
||||
/// move jpg file to photo directory
|
||||
final dirPhotoFile =
|
||||
await moveFromCacheToPhotosDir(File(dirCacheXFile.path));
|
||||
|
||||
widget.photoFiles.add(dirPhotoFile);
|
||||
|
||||
if (SharedPrefs().photoSound) {
|
||||
await _audioPlayer.setVolume((0.5));
|
||||
await _audioPlayer.play(
|
||||
AssetSource('sounds/camera-shutter-click.mp3'),
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
setState(() {
|
||||
isTakingPhoto = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.camera_alt,
|
||||
color: Colors.black,
|
||||
),
|
||||
)),
|
||||
}
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.camera_alt,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
_confirmButton(),
|
||||
_rejectButton(),
|
||||
Positioned(
|
||||
bottom: 80,
|
||||
child: SizedBox(
|
||||
height: 80,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (context, index) {
|
||||
return Container(
|
||||
bottom: 80,
|
||||
child: SizedBox(
|
||||
height: 80,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (context, index) {
|
||||
return GestureDetector(
|
||||
onVerticalDragEnd: (details) {
|
||||
if (details.primaryVelocity! < 0) {
|
||||
setState(() {
|
||||
widget.photoFiles.removeAt(index);
|
||||
});
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 2.0),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(width: 2.0, color: Colors.white)),
|
||||
border: Border.all(width: 2.0, color: Colors.white),
|
||||
),
|
||||
child: index >= widget.photoFiles.length
|
||||
? Container(color: Colors.white, width: 100)
|
||||
: Stack(
|
||||
children: [
|
||||
Image.file(
|
||||
widget.photoFiles[index],
|
||||
fit: BoxFit.cover,
|
||||
height: 100,
|
||||
width: 100,
|
||||
),
|
||||
Positioned(
|
||||
top: 2.5,
|
||||
right: 2.5,
|
||||
child: Container(
|
||||
height: 20,
|
||||
width: 20,
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
widget.photoFiles.removeAt(index);
|
||||
});
|
||||
},
|
||||
child: const Icon(Icons.close,
|
||||
size: 12, color: Colors.black),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
: Image.file(
|
||||
widget.photoFiles[index],
|
||||
fit: BoxFit.cover,
|
||||
height: 100,
|
||||
width: 100,
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: 10,
|
||||
),
|
||||
)),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: widget.photoFiles.length,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
@ -108,9 +122,11 @@ class _CustomCameraPreviewState extends State<CustomCameraPreview> {
|
|||
child: FloatingActionButton(
|
||||
heroTag: "close",
|
||||
backgroundColor: Colors.black,
|
||||
onPressed: () {
|
||||
Navigator.pop(context, false);
|
||||
},
|
||||
onPressed: !isTakingPhoto
|
||||
? () {
|
||||
Navigator.pop(context, false);
|
||||
}
|
||||
: null,
|
||||
child: const Icon(
|
||||
Icons.close,
|
||||
color: Colors.white,
|
||||
|
|
@ -121,18 +137,62 @@ class _CustomCameraPreviewState extends State<CustomCameraPreview> {
|
|||
|
||||
Positioned _confirmButton() {
|
||||
return Positioned(
|
||||
bottom: 5,
|
||||
right: 5,
|
||||
child: FloatingActionButton(
|
||||
heroTag: "confirm",
|
||||
backgroundColor: Colors.black,
|
||||
onPressed: () {
|
||||
Navigator.pop(context, true);
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.check,
|
||||
color: Colors.white,
|
||||
),
|
||||
));
|
||||
bottom: 5,
|
||||
right: 5,
|
||||
child: FloatingActionButton(
|
||||
heroTag: "confirm",
|
||||
backgroundColor: Colors.black,
|
||||
onPressed: !isTakingPhoto
|
||||
? () {
|
||||
Navigator.pop(context, true);
|
||||
}
|
||||
: null,
|
||||
child: const Icon(
|
||||
Icons.check,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Moves a temporary file to the photos directory in the app's document directory.
|
||||
///
|
||||
/// Returns a `File` object for the new file in the photos directory.
|
||||
///
|
||||
/// Parameters:
|
||||
/// * `tempFile`: The temporary `File` object to move to the photos directory.
|
||||
///
|
||||
/// Throws a `FileSystemException` if there is an error renaming the file.
|
||||
Future<File> moveFromCacheToPhotosDir(File tempFile) async {
|
||||
// Set the new file path with the original file name
|
||||
final String newPath =
|
||||
'${SharedPrefs().photosDir}/${tempFile.path.split('/').last}';
|
||||
|
||||
// Create a new File object with the new path
|
||||
final newFile = File(newPath);
|
||||
|
||||
final bool photoResizing = SharedPrefs().photoResizing;
|
||||
final int? minWidth = photoResizing ? 1024 : null;
|
||||
final int? minHeight = photoResizing ? 768 : null;
|
||||
|
||||
final fixedImageBytes = await FlutterImageCompress.compressWithFile(
|
||||
tempFile.path,
|
||||
minWidth: minWidth!,
|
||||
minHeight: minHeight!,
|
||||
rotate: 0,
|
||||
quality: 100,
|
||||
keepExif: false,
|
||||
autoCorrectionAngle: true,
|
||||
format: CompressFormat.jpeg,
|
||||
);
|
||||
|
||||
// Write the compressed image bytes to the new file
|
||||
await newFile.writeAsBytes(fixedImageBytes!);
|
||||
|
||||
// deleting the temporary file
|
||||
await tempFile.delete();
|
||||
|
||||
// Create and return a new File object for the new file
|
||||
return File(newPath);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ class SharedPrefs {
|
|||
|
||||
/// get/set photo quality
|
||||
String get photoQuality =>
|
||||
_sharedPrefs.getString('key_photo_quality') ?? "Defaut";
|
||||
_sharedPrefs.getString('key_photo_quality') ?? "veryHigh";
|
||||
|
||||
set photoQuality(String value) {
|
||||
_sharedPrefs.setString('key_photo_quality', value);
|
||||
|
|
|
|||
|
|
@ -208,7 +208,13 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||
}
|
||||
|
||||
Widget _showQualityPopup() {
|
||||
List<String> qualityOptions = ['Defaut', 'High', 'Medium', 'Low'];
|
||||
List<String> qualityOptions = [
|
||||
'medium',
|
||||
'high',
|
||||
'veryHigh',
|
||||
'ultraHigh',
|
||||
'max'
|
||||
];
|
||||
|
||||
return StatefulBuilder(
|
||||
builder: (BuildContext context, StateSetter mystate) {
|
||||
|
|
@ -239,27 +245,18 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||
itemCount: qualityOptions.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
String qualityOption = qualityOptions[index];
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTap: () {
|
||||
return RadioListTile<String>(
|
||||
dense: true, // Ajouter cette ligne
|
||||
title: Text(qualityOption),
|
||||
value: qualityOption,
|
||||
groupValue: _photoQuality,
|
||||
onChanged: (String? value) {
|
||||
setState(() {
|
||||
_photoQuality = qualityOption;
|
||||
SharedPrefs().photoQuality = qualityOption;
|
||||
_photoQuality = value!;
|
||||
SharedPrefs().photoQuality = value;
|
||||
});
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Container(
|
||||
margin: EdgeInsets.only(top: 16),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
qualityOption,
|
||||
style: GlobalStyle.courierType,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
@ -302,12 +299,17 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||
itemCount: languageOptions.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
String languageOption = languageOptions[index];
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTap: () {
|
||||
return RadioListTile<String>(
|
||||
title: Text(
|
||||
languageOption,
|
||||
style: GlobalStyle.courierType,
|
||||
),
|
||||
value: languageOption,
|
||||
groupValue: _language,
|
||||
onChanged: (String? value) {
|
||||
setState(() {
|
||||
_language = languageOption;
|
||||
switch (languageOption) {
|
||||
_language = value!;
|
||||
switch (value) {
|
||||
case 'English':
|
||||
SharedPrefs().langage = 'en';
|
||||
break;
|
||||
|
|
@ -315,25 +317,12 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||
SharedPrefs().langage = 'fr';
|
||||
break;
|
||||
default:
|
||||
// TODO a vérifier
|
||||
_language = 'French';
|
||||
break;
|
||||
}
|
||||
});
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Container(
|
||||
margin: EdgeInsets.only(top: 16),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
languageOption,
|
||||
style: GlobalStyle.courierType,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import 'package:mobdr/config/constant.dart';
|
|||
import 'package:mobdr/events.dart';
|
||||
import 'package:mobdr/service/plausible.dart';
|
||||
import 'package:mobdr/service/logger_util.dart';
|
||||
import 'package:mobdr/service/plausible.dart';
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ import 'package:flutter/material.dart';
|
|||
|
||||
import 'package:mobdr/config/constant.dart';
|
||||
|
||||
//TODO Rechercher toutes les utilisations
|
||||
|
||||
class ReusableWidget {
|
||||
PreferredSizeWidget bottomAppBar() {
|
||||
return PreferredSize(
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:camera/camera.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:mobdr/core/components/custom_camera_preview.dart';
|
||||
import 'package:mobdr/service/shared_prefs.dart';
|
||||
|
||||
class CameraPage extends StatefulWidget {
|
||||
const CameraPage({Key? key, required this.photoFiles}) : super(key: key);
|
||||
|
|
@ -53,10 +54,35 @@ class _CameraPageState extends State<CameraPage> {
|
|||
}
|
||||
|
||||
Future _initCameraController(CameraDescription cameraDescription) async {
|
||||
ResolutionPreset getResolutionPresetFromPhotoQuality(String photoQuality) {
|
||||
switch (photoQuality) {
|
||||
case 'low':
|
||||
return ResolutionPreset.low;
|
||||
case 'medium':
|
||||
return ResolutionPreset.medium;
|
||||
case 'high':
|
||||
return ResolutionPreset.high;
|
||||
case 'veryHigh':
|
||||
return ResolutionPreset.veryHigh;
|
||||
case 'ultraHigh':
|
||||
return ResolutionPreset.ultraHigh;
|
||||
case 'max':
|
||||
return ResolutionPreset.max;
|
||||
default:
|
||||
return ResolutionPreset
|
||||
.veryHigh; // Default value if quality is not recognised
|
||||
}
|
||||
}
|
||||
|
||||
ResolutionPreset resolutionPreset =
|
||||
getResolutionPresetFromPhotoQuality(SharedPrefs().photoQuality);
|
||||
|
||||
if (controller != null) {
|
||||
await controller!.dispose();
|
||||
}
|
||||
controller = CameraController(cameraDescription, ResolutionPreset.high);
|
||||
|
||||
controller = CameraController(cameraDescription, resolutionPreset,
|
||||
enableAudio: false);
|
||||
controller!.addListener(() {
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ import 'dart:io';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:image/image.dart' as img;
|
||||
import 'package:image_gallery_saver/image_gallery_saver.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
import 'package:mobdr/config/constant.dart';
|
||||
import 'package:mobdr/config/global_style.dart';
|
||||
|
|
@ -45,6 +47,10 @@ class _VisitPhotoTypologyDetailPageState
|
|||
bool _isLoading = true;
|
||||
String _errorMessage = '';
|
||||
|
||||
late String imageSize;
|
||||
late String imageResolution;
|
||||
late String imageOrientation;
|
||||
|
||||
// Typology list
|
||||
late List<PhotoTypology> _photoTypologiesList = [];
|
||||
int _photoTypologyIndex = 0;
|
||||
|
|
@ -62,6 +68,8 @@ class _VisitPhotoTypologyDetailPageState
|
|||
void initState() {
|
||||
super.initState();
|
||||
|
||||
getImageDetails();
|
||||
|
||||
// track & log page access
|
||||
final page = 'visit_photo_typology_detail';
|
||||
LoggerUtil.dblog('LOG', 'MOBDR', 'Page : ${page}', 0);
|
||||
|
|
@ -74,6 +82,36 @@ class _VisitPhotoTypologyDetailPageState
|
|||
});
|
||||
}
|
||||
|
||||
void getImageDetails() {
|
||||
File imageFile = File(widget.pp_image);
|
||||
|
||||
if (imageFile.existsSync()) {
|
||||
img.Image? image = img.decodeImage(imageFile.readAsBytesSync());
|
||||
|
||||
int imageSizeInBytes = imageFile.lengthSync();
|
||||
double imageSizeInMb = imageSizeInBytes / (1024 * 1024);
|
||||
|
||||
int imageWidth = image!.width;
|
||||
int imageHeight = image.height;
|
||||
|
||||
imageSize = '${imageSizeInMb.toStringAsFixed(2)} MB';
|
||||
imageResolution = '$imageWidth x $imageHeight';
|
||||
|
||||
if (imageWidth > imageHeight) {
|
||||
imageOrientation = 'Landscape';
|
||||
} else if (imageHeight > imageWidth) {
|
||||
imageOrientation = 'Portrait';
|
||||
} else {
|
||||
imageOrientation = 'Square';
|
||||
}
|
||||
} else {
|
||||
// The image file does not exist
|
||||
imageSize = '';
|
||||
imageResolution = '';
|
||||
imageOrientation = '';
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
|
|
@ -136,10 +174,11 @@ class _VisitPhotoTypologyDetailPageState
|
|||
child: ListView(
|
||||
children: [
|
||||
Image.file(File(widget.pp_image), fit: BoxFit.cover),
|
||||
_buildPhotoTypology(),
|
||||
_buildPhotoVisibility(),
|
||||
_buildPhotoDetail(),
|
||||
_buildPhotoTag(context),
|
||||
_buildPhotoVisibility(),
|
||||
_BuildPhotoCompetitor(),
|
||||
_buildPhotoTypology(),
|
||||
SizedBox(height: 16)
|
||||
],
|
||||
),
|
||||
|
|
@ -209,6 +248,45 @@ class _VisitPhotoTypologyDetailPageState
|
|||
));
|
||||
}
|
||||
|
||||
Widget _buildPhotoDetail() {
|
||||
return Container(
|
||||
child: Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 5), // Décalage de 10 pixels à gauche
|
||||
child: Text(
|
||||
imageSize,
|
||||
style: TextStyle(
|
||||
color: Colors.blue,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
Flexible(
|
||||
child: Text(
|
||||
imageResolution,
|
||||
style: TextStyle(
|
||||
color: Colors.blue,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
Flexible(
|
||||
child: Text(
|
||||
'($imageOrientation)',
|
||||
style: TextStyle(
|
||||
color: Colors.blue,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPhotoTypology() {
|
||||
return Container(
|
||||
margin: EdgeInsets.only(top: 12),
|
||||
|
|
@ -519,9 +597,17 @@ class _VisitPhotoTypologyDetailPageState
|
|||
}
|
||||
|
||||
Future<bool> saveImageToGallery(String imagePath) async {
|
||||
final result = await ImageGallerySaver.saveFile(imagePath);
|
||||
bool isSuccess = result["isSuccess"];
|
||||
return isSuccess;
|
||||
// Request permission to access external storage
|
||||
PermissionStatus status = await Permission.storage.request();
|
||||
|
||||
if (status.isGranted) {
|
||||
final result = await ImageGallerySaver.saveFile(imagePath);
|
||||
bool isSuccess = result["isSuccess"];
|
||||
return isSuccess;
|
||||
} else {
|
||||
// The user has refused permission
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes data when the page loads.
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:image/image.dart' as img;
|
||||
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
|
||||
import 'package:path/path.dart' as path;
|
||||
|
|
@ -413,10 +413,7 @@ class _VisitPhotoTypologyListPageState
|
|||
if (_visitPhotoFiles.length > 0) {
|
||||
final List<VisitPhoto> _listPhotos = [];
|
||||
|
||||
for (var myTmpPhoto in _visitPhotoFiles) {
|
||||
/// move jpg file to photo directory
|
||||
final myPhoto = await moveFileFromTempToPhotosDir(myTmpPhoto);
|
||||
|
||||
for (var myPhoto in _visitPhotoFiles) {
|
||||
/// to insert into database
|
||||
_listPhotos.add(VisitPhoto(
|
||||
id_visite: widget.pp_visitModel.id_visite,
|
||||
|
|
@ -543,26 +540,6 @@ class _VisitPhotoTypologyListPageState
|
|||
);
|
||||
}
|
||||
|
||||
// Moves a temporary file to the photos directory in the app's document directory.
|
||||
///
|
||||
/// Returns a `File` object for the new file in the photos directory.
|
||||
///
|
||||
/// Parameters:
|
||||
/// * `tempFile`: The temporary `File` object to move to the photos directory.
|
||||
///
|
||||
/// Throws a `FileSystemException` if there is an error renaming the file.
|
||||
Future<File> moveFileFromTempToPhotosDir(File tempFile) async {
|
||||
// Set the new file path with the original file name
|
||||
final String newPath =
|
||||
'${SharedPrefs().photosDir}/${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);
|
||||
}
|
||||
|
||||
/// Rotates the image file clockwise by the specified angle (in degrees) and overwrites the original file with the rotated image.
|
||||
///
|
||||
/// Parameters:
|
||||
|
|
@ -571,24 +548,20 @@ class _VisitPhotoTypologyListPageState
|
|||
///
|
||||
/// Throws an exception if there is an error in the rotation process.
|
||||
Future<void> rotateAndReplaceImage(File imageFile, int angle) async {
|
||||
// Read the image file into a Uint8List
|
||||
Uint8List bytes = await imageFile.readAsBytes();
|
||||
// Rotate the image using flutter_image_compress
|
||||
Uint8List? rotatedBytes = await FlutterImageCompress.compressWithFile(
|
||||
imageFile.absolute.path,
|
||||
rotate: angle,
|
||||
);
|
||||
|
||||
// Decode the bytes into an Image object using the image package
|
||||
img.Image? image = img.decodeImage(bytes);
|
||||
// Check that rotatedBytes is not null before using it
|
||||
if (rotatedBytes != null) {
|
||||
// Overwrite the original file with the rotated image
|
||||
await imageFile.writeAsBytes(rotatedBytes);
|
||||
|
||||
// Rotate the image clockwise by 90 degrees
|
||||
img.Image? rotatedImage = img.copyRotate(image!, angle: angle);
|
||||
|
||||
// Encode the rotated image back into a Uint8List
|
||||
// TODO : png ??
|
||||
Uint8List rotatedBytes = img.encodePng(rotatedImage);
|
||||
|
||||
// Overwrite the original file with the rotated image
|
||||
await imageFile.writeAsBytes(rotatedBytes);
|
||||
|
||||
// remove the flutter cache image
|
||||
FileImage(imageFile).evict();
|
||||
// remove the flutter cache image
|
||||
FileImage(imageFile).evict();
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies the 'simulator.jpg' image file from the app's assets to a randomly named file in the app's temporary directory and returns the File object.
|
||||
|
|
|
|||
|
|
@ -6,9 +6,13 @@
|
|||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <audioplayers_linux/audioplayers_linux_plugin.h>
|
||||
#include <objectbox_flutter_libs/objectbox_flutter_libs_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
|
||||
audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) objectbox_flutter_libs_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "ObjectboxFlutterLibsPlugin");
|
||||
objectbox_flutter_libs_plugin_register_with_registrar(objectbox_flutter_libs_registrar);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
audioplayers_linux
|
||||
objectbox_flutter_libs
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import audioplayers_darwin
|
||||
import connectivity_plus
|
||||
import device_info_plus
|
||||
import objectbox_flutter_libs
|
||||
|
|
@ -15,6 +16,7 @@ import sqflite
|
|||
import wakelock_macos
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
|
||||
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
|
||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||
ObjectboxFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "ObjectboxFlutterLibsPlugin"))
|
||||
|
|
|
|||
140
pubspec.lock
140
pubspec.lock
|
|
@ -21,10 +21,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
sha256: d6347d54a2d8028e0437e3c099f66fdb8ae02c4720c1e7534c9f24c10351f85d
|
||||
sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.6"
|
||||
version: "3.3.7"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -41,6 +41,62 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.10.0"
|
||||
audioplayers:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: audioplayers
|
||||
sha256: "61583554386721772f9309f509e17712865b38565a903c761f96b1115a979282"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
audioplayers_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: audioplayers_android
|
||||
sha256: dbdc9b7f2aa2440314c638aa55aadd45c7705e8340d5eddf2e3fb8da32d4ae2c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
audioplayers_darwin:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: audioplayers_darwin
|
||||
sha256: "6aea96df1d12f7ad5a71d88c6d1b22a216211a9564219920124c16768e456e9d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
audioplayers_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: audioplayers_linux
|
||||
sha256: "396b62ac62c92dd26c3bc5106583747f57a8b325ebd2b41e5576f840cfc61338"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
audioplayers_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: audioplayers_platform_interface
|
||||
sha256: f7daaed4659143094151ecf6bacd927d29ab8acffba98c110c59f0b81ae51143
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.1"
|
||||
audioplayers_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: audioplayers_web
|
||||
sha256: ec84fd46eed1577148ed4113f5998a36a18da4fce7170c37ce3e21b631393339
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
audioplayers_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: audioplayers_windows
|
||||
sha256: "1d3aaac98a192b8488167711ba1e67d8b96333e8d0572ede4e2912e5bbce69a3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
badges:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -430,6 +486,38 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.0"
|
||||
flutter_image_compress:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_image_compress
|
||||
sha256: "2babae2c69ee4849c3d3ae0f1f10c14a6b8132390e4583c5d3b1f02e8167a022"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
flutter_image_compress_common:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_image_compress_common
|
||||
sha256: "88aae2219cd4507992643f19aee7882fe8ff82375ffa8cb4c876e3bfe3e7c3b6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
flutter_image_compress_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_image_compress_platform_interface
|
||||
sha256: dae0a3eb1fb7f38cf327cc2005dc287bc891becdd83c519277de4415fd9e065d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
flutter_image_compress_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_image_compress_web
|
||||
sha256: "677f02509bdf5fd71afb560a22f0444e82c9ee887d223cd006cb44fd145fcb17"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.3"
|
||||
flutter_localizations:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
|
@ -513,10 +601,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: image
|
||||
sha256: "483a389d6ccb292b570c31b3a193779b1b0178e7eb571986d9a49904b6861227"
|
||||
sha256: a72242c9a0ffb65d03de1b7113bc4e189686fc07c7147b8b41811d0dd0e0d9bf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.15"
|
||||
version: "4.0.17"
|
||||
image_gallery_saver:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -781,6 +869,46 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
permission_handler:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: permission_handler
|
||||
sha256: "1b6b3e73f0bcbc856548bbdfb1c33084a401c4f143e220629a9055233d76c331"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.3.0"
|
||||
permission_handler_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_android
|
||||
sha256: "8f6a95ccbca13766882f95d32684d7c9bfe6c45650c32bedba948ef1c6a4ddf7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.2.3"
|
||||
permission_handler_apple:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_apple
|
||||
sha256: "08dcb6ce628ac0b257e429944b4c652c2a4e6af725bdf12b498daa2c6b2b1edb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.1.0"
|
||||
permission_handler_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_platform_interface
|
||||
sha256: de20a5c3269229c1ae2e5a6b822f6cb59578b23e8255c93fbeebfc82116e6b11
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.10.0"
|
||||
permission_handler_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_windows
|
||||
sha256: f67cab14b4328574938ecea2db3475dad7af7ead6afab6338772c5f88963e38b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.2"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -825,10 +953,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: pointycastle
|
||||
sha256: c3120a968135aead39699267f4c74bc9a08e4e909e86bc1b0af5bfd78691123c
|
||||
sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.7.2"
|
||||
version: "3.7.3"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
29
pubspec.yaml
29
pubspec.yaml
|
|
@ -66,16 +66,18 @@ dependencies:
|
|||
# https://pub.dev/packages/camera
|
||||
camera: ^0.10.5+2
|
||||
|
||||
# https://pub.dev/packages/audioplayers
|
||||
audioplayers: ^4.1.0
|
||||
|
||||
# https://pub.dev/packages/image
|
||||
# Rotations ...
|
||||
image: ^4.0.15
|
||||
image: ^4.0.17
|
||||
|
||||
# https://pub.dev/packages/flutter_image_compress
|
||||
flutter_image_compress: ^2.0.3
|
||||
|
||||
# https://pub.dev/packages/super_tag_editor
|
||||
super_tag_editor: ^0.1.1
|
||||
|
||||
# https://pub.dev/packages/flutter_cache_manager
|
||||
#flutter_cache_manager: ^3.3.0
|
||||
|
||||
|
||||
# https://pub.dev/packages/cached_network_image
|
||||
cached_network_image: 3.2.3
|
||||
|
||||
|
|
@ -116,15 +118,18 @@ dependencies:
|
|||
# https://pub.dev/packages/image_gallery_saver
|
||||
image_gallery_saver: ^2.0.1
|
||||
|
||||
# https://pub.dev/packages/permission_handler
|
||||
permission_handler: 10.3.0
|
||||
|
||||
# hhttps://pub.dev/packages/timelines
|
||||
#timelines: ^0.1.0
|
||||
|
||||
# https://pub.dev/packages/permission_handler
|
||||
#permission_handler: 10.2.0
|
||||
|
||||
|
||||
# https://pub.dev/packages/flutter_html
|
||||
#flutter_html: 3.0.0-alpha.6
|
||||
|
||||
# https://pub.dev/packages/flutter_cache_manager
|
||||
#flutter_cache_manager: ^3.3.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
|
@ -209,4 +214,6 @@ flutter:
|
|||
- assets/lang/zh.json
|
||||
- assets/lang/hi.json
|
||||
- assets/lang/th.json
|
||||
- assets/lang/tk.json
|
||||
- assets/lang/tk.json
|
||||
|
||||
- assets/sounds/camera-shutter-click.mp3
|
||||
|
|
@ -6,12 +6,18 @@
|
|||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <audioplayers_windows/audioplayers_windows_plugin.h>
|
||||
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
||||
#include <objectbox_flutter_libs/objectbox_flutter_libs_plugin.h>
|
||||
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
AudioplayersWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
|
||||
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
|
||||
ObjectboxFlutterLibsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("ObjectboxFlutterLibsPlugin"));
|
||||
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@
|
|||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
audioplayers_windows
|
||||
connectivity_plus
|
||||
objectbox_flutter_libs
|
||||
permission_handler_windows
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
|
|
|||
Loading…
Reference in New Issue