fix: centrage de la cible image pleine ecran respectant la prise origine au niveau des coins verts/orange

This commit is contained in:
qguillaume
2026-06-17 19:42:13 +02:00
parent c4880ccb68
commit 05356aaaea
2 changed files with 55 additions and 49 deletions

View File

@@ -101,17 +101,16 @@ class _CropScreenState extends State<CropScreen> {
? const Center(child: CircularProgressIndicator(color: Color(0xFF1A73E8)))
: Column(
children: [
// Zone interactive de crop. Le cadre épouse le ratio réel de la photo
// (AspectRatio) → la photo le remplit bord à bord, sans bande noire de
// letterbox. La fenêtre de visée carrée reste centrée à l'intérieur.
// Zone interactive de crop, CARRÉE. La photo la remplit entièrement
// (BoxFit.cover) → aucun bord noir dans la zone. Le débord hors cadre
// est récupérable en déplaçant/zoomant. La sortie d'analyse reste
// carrée, donc la cible n'est pas déformée.
Expanded(
child: Padding(
padding: const EdgeInsets.all(20),
child: Center(
child: AspectRatio(
aspectRatio: (_imageSize != null && _imageSize!.height > 0)
? _imageSize!.width / _imageSize!.height
: 1.0,
aspectRatio: 1.0,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
@@ -249,21 +248,9 @@ class _CropScreenState extends State<CropScreen> {
builder: (context, constraints) {
_viewportSize = Size(constraints.maxWidth, constraints.maxHeight);
// FIX : On calcule d'abord la taille de la photo affichée avant de définir la taille du cadre vert !
final imageAspect = _imageSize != null ? _imageSize!.width / _imageSize!.height : 1.0;
final viewportAspect = _viewportSize.width / _viewportSize.height;
double displayWidth, displayHeight;
if (imageAspect > viewportAspect) {
displayWidth = _viewportSize.width;
displayHeight = _viewportSize.width / imageAspect;
} else {
displayHeight = _viewportSize.height;
displayWidth = _viewportSize.height * imageAspect;
}
// On passe de 0.95 à 0.85 pour matcher parfaitement avec l'appareil photo !
_cropSize = math.min(displayWidth, displayHeight)* 0.85;
// La photo remplit toute la zone carrée (BoxFit.cover). La fenêtre de
// visée = toute la zone visible → aucun bord noir autour du cadre.
_cropSize = math.min(_viewportSize.width, _viewportSize.height);
if (_scale == 1.0 && _offset == Offset.zero && _rotation == 0.0) {
_initializeImagePosition();
@@ -285,7 +272,7 @@ class _CropScreenState extends State<CropScreen> {
alignment: Alignment.center,
child: Image.file(
File(widget.imagePath),
fit: BoxFit.contain,
fit: BoxFit.cover,
width: _viewportSize.width,
height: _viewportSize.height,
),
@@ -385,14 +372,13 @@ class _CropScreenState extends State<CropScreen> {
Future<void> _onCropConfirm() async {
setState(() => _isLoading = true);
try {
// Facteur d'échelle affichage/source (BoxFit.contain) — identique à
// celui utilisé pour afficher l'image dans l'aperçu.
final imageAspect = _imageSize!.width / _imageSize!.height;
final viewportAspect = _viewportSize.width / _viewportSize.height;
final double displayWidth = imageAspect > viewportAspect
? _viewportSize.width
: _viewportSize.height * imageAspect;
final double displayPerSourcePx = displayWidth / _imageSize!.width;
// Facteur d'échelle affichage/source (BoxFit.cover) — identique à celui
// utilisé pour afficher l'image dans l'aperçu : l'image remplit la zone,
// le débord est rogné, donc l'échelle est le MAX des deux ratios d'axe.
final double displayPerSourcePx = math.max(
_viewportSize.width / _imageSize!.width,
_viewportSize.height / _imageSize!.height,
);
// Découpe calée sur la fenêtre de visée : le DÉPLACEMENT (pan) et la
// ROTATION sont pris en compte, le ZOOM est ignoré, et les débordements