fix: reeduction temps prise photo et enleve confusion photo en cours de traitement
This commit is contained in:
@@ -47,6 +47,11 @@ class _CaptureScreenState extends State<CaptureScreen>
|
|||||||
String? _selectedImagePath;
|
String? _selectedImagePath;
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
bool _isCapturing = false; // garde anti-double-capture pendant le traitement
|
bool _isCapturing = false; // garde anti-double-capture pendant le traitement
|
||||||
|
// Chemin de la photo tout juste capturée, affichée FIGÉE pendant le
|
||||||
|
// traitement (downscale). Tant qu'il est null, la capture physique est
|
||||||
|
// encore en cours → l'utilisateur doit rester immobile. Une fois rempli,
|
||||||
|
// la photo est prise et l'utilisateur peut bouger.
|
||||||
|
String? _capturedPreviewPath;
|
||||||
|
|
||||||
// Caméra live
|
// Caméra live
|
||||||
CameraController? _cameraController;
|
CameraController? _cameraController;
|
||||||
@@ -190,9 +195,15 @@ class _CaptureScreenState extends State<CaptureScreen>
|
|||||||
|
|
||||||
_cameras = await availableCameras();
|
_cameras = await availableCameras();
|
||||||
if (_cameras != null && _cameras!.isNotEmpty) {
|
if (_cameras != null && _cameras!.isNotEmpty) {
|
||||||
|
// PERF : on capture en `veryHigh` (2160p) et non en `max`. Le pipeline
|
||||||
|
// redimensionne ensuite tout à 1080 px max → la résolution `max`
|
||||||
|
// (12–48 MP) ne servait à rien et rallongeait fortement le décodage
|
||||||
|
// post-capture (« délai de quelques secondes »). `veryHigh` reste très
|
||||||
|
// au-dessus du 1080 px final : aucune perte de qualité, mais décodage
|
||||||
|
// bien plus rapide.
|
||||||
_cameraController = CameraController(
|
_cameraController = CameraController(
|
||||||
_cameras![0],
|
_cameras![0],
|
||||||
ResolutionPreset.max,
|
ResolutionPreset.veryHigh,
|
||||||
enableAudio: false,
|
enableAudio: false,
|
||||||
imageFormatGroup: ImageFormatGroup.jpeg,
|
imageFormatGroup: ImageFormatGroup.jpeg,
|
||||||
);
|
);
|
||||||
@@ -614,30 +625,19 @@ class _CaptureScreenState extends State<CaptureScreen>
|
|||||||
|
|
||||||
// 6. Overlay bloquant pendant la capture/traitement : empêche de
|
// 6. Overlay bloquant pendant la capture/traitement : empêche de
|
||||||
// bouger l'UI ou de relancer une capture tant que la photo n'est pas
|
// bouger l'UI ou de relancer une capture tant que la photo n'est pas
|
||||||
// chargée (absorbe tous les gestes + voile + indicateur).
|
// chargée (absorbe tous les gestes). Deux phases distinctes pour
|
||||||
|
// lever la confusion « puis-je bouger ? » :
|
||||||
|
// • _capturedPreviewPath == null → capture physique en cours :
|
||||||
|
// voile sur l'aperçu vivant + « Ne bougez pas ».
|
||||||
|
// • _capturedPreviewPath != null → photo prise, traitement en
|
||||||
|
// cours : on affiche l'image FIGÉE (le flux vivant disparaît)
|
||||||
|
// + « Photo prise ✓ — vous pouvez bouger ».
|
||||||
if (_isCapturing)
|
if (_isCapturing)
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: AbsorbPointer(
|
child: AbsorbPointer(
|
||||||
child: Container(
|
child: _capturedPreviewPath == null
|
||||||
color: Colors.black.withValues(alpha: 0.45),
|
? _buildCapturingOverlay()
|
||||||
child: const Center(
|
: _buildProcessingOverlay(_capturedPreviewPath!),
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
CircularProgressIndicator(color: Colors.white),
|
|
||||||
SizedBox(height: 16),
|
|
||||||
Text(
|
|
||||||
'Photo en cours…',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -645,6 +645,92 @@ class _CaptureScreenState extends State<CaptureScreen>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
// Overlay PHASE 1 : capture physique en cours → l'utilisateur doit rester
|
||||||
|
// immobile. Voile semi-transparent par-dessus l'aperçu vivant.
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
Widget _buildCapturingOverlay() {
|
||||||
|
return Container(
|
||||||
|
color: Colors.black.withValues(alpha: 0.45),
|
||||||
|
child: const Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
CircularProgressIndicator(color: Colors.white),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'NE BOUGEZ PAS — CAPTURE…',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
letterSpacing: 1.2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
// Overlay PHASE 2 : photo capturée, traitement en cours. On affiche l'image
|
||||||
|
// FIGÉE (l'aperçu vivant disparaît, plus d'ambiguïté) et on indique
|
||||||
|
// clairement que l'utilisateur peut maintenant bouger.
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
Widget _buildProcessingOverlay(String imagePath) {
|
||||||
|
return Stack(
|
||||||
|
fit: StackFit.expand,
|
||||||
|
children: [
|
||||||
|
// Image figée de la photo prise → recouvre totalement le flux caméra
|
||||||
|
Image.file(File(imagePath), fit: BoxFit.cover),
|
||||||
|
Container(
|
||||||
|
color: Colors.black.withValues(alpha: 0.55),
|
||||||
|
child: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.check_circle,
|
||||||
|
color: Color(0xFF00FF00),
|
||||||
|
size: 52,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 14),
|
||||||
|
const Text(
|
||||||
|
'PHOTO PRISE',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFF00FF00),
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
letterSpacing: 1.2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
const SizedBox(
|
||||||
|
width: 22,
|
||||||
|
height: 22,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: Colors.white,
|
||||||
|
strokeWidth: 2.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 14),
|
||||||
|
Text(
|
||||||
|
'Traitement… vous pouvez bouger',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white.withValues(alpha: 0.85),
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
// NOUVEAU : Widget affichant les angles pitch/roll en temps réel
|
// NOUVEAU : Widget affichant les angles pitch/roll en temps réel
|
||||||
// ─────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
@@ -761,6 +847,7 @@ class _CaptureScreenState extends State<CaptureScreen>
|
|||||||
setState(() {
|
setState(() {
|
||||||
_isCapturing = true;
|
_isCapturing = true;
|
||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
|
_capturedPreviewPath = null; // phase « capture en cours »
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
// On stoppe le flux d'analyse (obligatoire avant takePicture) puis on
|
// On stoppe le flux d'analyse (obligatoire avant takePicture) puis on
|
||||||
@@ -772,6 +859,14 @@ class _CaptureScreenState extends State<CaptureScreen>
|
|||||||
|
|
||||||
final XFile photo = await _cameraController!.takePicture();
|
final XFile photo = await _cameraController!.takePicture();
|
||||||
|
|
||||||
|
// La photo est PHYSIQUEMENT capturée : on gèle l'aperçu en affichant
|
||||||
|
// l'image prise (le flux caméra « vivant » disparaît) et on bascule le
|
||||||
|
// message sur « vous pouvez bouger ». Tout ce qui suit n'est que du
|
||||||
|
// traitement et n'exige plus l'immobilité de l'utilisateur.
|
||||||
|
if (mounted) {
|
||||||
|
setState(() => _capturedPreviewPath = photo.path);
|
||||||
|
}
|
||||||
|
|
||||||
// Pas de redressement perspective automatique ici : il déformait l'image
|
// Pas de redressement perspective automatique ici : il déformait l'image
|
||||||
// (écrasement en carré + rotation de l'axe de l'ellipse) et gelait le
|
// (écrasement en carré + rotation de l'axe de l'ellipse) et gelait le
|
||||||
// thread UI. Le recentrage ET la rotation se font manuellement à l'écran
|
// thread UI. Le recentrage ET la rotation se font manuellement à l'écran
|
||||||
@@ -801,6 +896,7 @@ class _CaptureScreenState extends State<CaptureScreen>
|
|||||||
setState(() {
|
setState(() {
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
_isCapturing = false;
|
_isCapturing = false;
|
||||||
|
_capturedPreviewPath = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user