Adds an image cropper to force the use to use a square image when taking picture

This commit is contained in:
2025-07-05 10:57:38 +02:00
parent 5d461d5b0f
commit 3119e5fdbf
6 changed files with 118 additions and 57 deletions

View File

@ -3,6 +3,10 @@
android:label="rtime"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name="com.yalantis.ucrop.UCropActivity"
android:screenOrientation="portrait"
android:theme="@style/Ucrop.CropTheme"/>
<activity
android:name=".MainActivity"
android:exported="true"

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Ucrop.CropTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
</style>
</resources>

View File

@ -15,4 +15,5 @@
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
<style name="Ucrop.CropTheme" parent="Theme.AppCompat.Light.NoActionBar"/> <!--add this line-->
</resources>

View File

@ -7,73 +7,97 @@ import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as path;
import 'package:uuid/uuid.dart';
import 'package:image/image.dart' as img;
import 'package:image_cropper/image_cropper.dart' as img_cropper;
import 'package:uuid/v5.dart';
class ImagesManager
{
static final ImagesManager instance = ImagesManager._internal();
ImagesManager._internal();
class ImagesManager {
static final ImagesManager instance = ImagesManager._internal();
ImagesManager._internal();
static final Logger log = Logger("ImagesManager");
static final Logger log = Logger("ImagesManager");
static Uri? _imagesDirectory;
static Uri? _imagesDirectory;
Future<Uri> get imageDirectory async
{
if(_imagesDirectory != null) return _imagesDirectory!;
await _initImagesDirectory();
return _imagesDirectory!;
Future<Uri> get imageDirectory async {
if (_imagesDirectory != null) return _imagesDirectory!;
await _initImagesDirectory();
return _imagesDirectory!;
}
Future _initImagesDirectory() async {
final directoryLoc = await getApplicationDocumentsDirectory();
final directoryName = "images";
final directoryPath = path.join(directoryLoc.path, directoryName);
final directoryUri = Uri.directory(directoryPath);
final directory = Directory.fromUri(directoryUri);
if (!await directory.exists()) {
log.info("Image directory does not yet extists. Creating it.");
}
Future _initImagesDirectory() async
{
final directoryLoc = await getApplicationDocumentsDirectory();
final directoryName = "images";
final directoryPath = path.join(directoryLoc.path, directoryName);
final directoryUri = Uri.directory(directoryPath);
final directory = Directory.fromUri(directoryUri);
if(!await directory.exists())
{
log.info("Image directory does not yet extists. Creating it.");
}
directory.create(recursive: false);
directory.create(recursive: false);
log.info("Image directory set up at '$directory'");
_imagesDirectory = directoryUri;
log.info("Image directory set up at '$directory'");
_imagesDirectory = directoryUri;
}
Future<String?> createImage(ImageSource source) async {
// Get image from camera or not
final XFile? ximage = await ImagePicker().pickImage(source: source);
if (ximage == null) return null;
// Crop image
final uuid = Uuid().v6();
final imageDir = await imageDirectory;
final finalPath = path.join(
imageDir.path,
uuid + path.extension(ximage.name),
);
final tempPath = path.join(
imageDir.path,
"${uuid}_tocrop${path.extension(ximage.name)}",
);
await ximage.saveTo(tempPath);
img_cropper.CroppedFile? cropped = await img_cropper.ImageCropper()
.cropImage(
sourcePath: tempPath,
aspectRatio: img_cropper.CropAspectRatio(ratioX: 1.0, ratioY: 1.0),
uiSettings: [
img_cropper.AndroidUiSettings(
toolbarTitle: "Crop image",
toolbarColor: Colors.black,
toolbarWidgetColor: Colors.white,
),
img_cropper.IOSUiSettings(title: "Crop image"),
],
);
if (cropped == null) return null;
await File(finalPath).writeAsBytes(await cropped.readAsBytes());
await File(tempPath).delete();
return uuid;
}
Future<Image?> loadImage(String imageUuid) async {
final imageDir = await imageDirectory;
if (!Uuid.isValidUUID(fromString: imageUuid)) {
log.warning(
"Tried to load an image with an invalid UUID : '$imageUuid'.",
);
return null;
}
Future<String?> createImage(ImageSource source) async
{
// Get image from camera or not
final XFile? ximage = await ImagePicker().pickImage(source: source);
if(ximage == null) return null;
final uuid = Uuid().v6();
final imageDir = await imageDirectory;
ximage.saveTo(path.join(imageDir.path, uuid + path.extension(ximage.name)));
return uuid;
final imagePath = path.join(imageDir.path, "$imageUuid.jpg");
final file = File(imagePath);
if (!await file.exists()) {
log.warning("Tried to load an image that does not extist: '$imagePath'.");
return null;
}
Future<Image?> loadImage(String imageUuid) async
{
final imageDir = await imageDirectory;
if(!Uuid.isValidUUID(fromString: imageUuid))
{
log.warning("Tried to load an image with an invalid UUID : '$imageUuid'.");
return null;
}
final imagePath = path.join(imageDir.path, "$imageUuid.jpg");
final file = File(imagePath);
if(!await file.exists())
{
log.warning("Tried to load an image that does not extist: '$imagePath'.");
return null;
}
return Image.file(file);
}
return Image.file(file);
}
}

View File

@ -344,6 +344,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.5.4"
image_cropper:
dependency: "direct main"
description:
name: image_cropper
sha256: "4e9c96c029eb5a23798da1b6af39787f964da6ffc78fd8447c140542a9f7c6fc"
url: "https://pub.dev"
source: hosted
version: "9.1.0"
image_cropper_for_web:
dependency: transitive
description:
name: image_cropper_for_web
sha256: fd81ebe36f636576094377aab32673c4e5d1609b32dec16fad98d2b71f1250a9
url: "https://pub.dev"
source: hosted
version: "6.1.0"
image_cropper_platform_interface:
dependency: transitive
description:
name: image_cropper_platform_interface
sha256: "6ca6b81769abff9a4dcc3bbd3d75f5dfa9de6b870ae9613c8cd237333a4283af"
url: "https://pub.dev"
source: hosted
version: "7.1.0"
image_picker:
dependency: "direct main"
description:

View File

@ -44,6 +44,7 @@ dependencies:
image_picker: ^1.1.2
uuid: ^4.5.1
json_serializable: ^6.9.5
image_cropper: ^9.1.0
dev_dependencies:
flutter_test: