auto-claude: subtask-2-3 - Create PhotoCaptureDialog component for photo confirmation
Add PhotoCaptureDialog widget component that: - Shows captured photo preview with proper constraints - Displays confirmation message using delivery name - Provides Cancel and Upload action buttons - Uses theme-aware styling with colorScheme - Handles image loading errors gracefully - Includes proper i18n support (EN/FR) Added localization keys: - confirmPhoto - uploadPhotoConfirmation (with name placeholder) - uploadingPhoto - photoUploadSuccess - photoUploadFailed - cameraError - uploadError - serverError - retake Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
bcc938fde1
commit
e5f267b4f7
133
lib/components/photo_capture_dialog.dart
Normal file
133
lib/components/photo_capture_dialog.dart
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:planb_logistic/l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
/// A dialog component for confirming and displaying captured photos before upload.
|
||||||
|
///
|
||||||
|
/// This dialog shows a preview of the captured photo and prompts the user
|
||||||
|
/// to confirm the upload or cancel/retake.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```dart
|
||||||
|
/// final confirmed = await PhotoCaptureDialog.show(
|
||||||
|
/// context,
|
||||||
|
/// imageFile: File(pickedFile.path),
|
||||||
|
/// deliveryName: delivery.name,
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// if (confirmed == true) {
|
||||||
|
/// // Proceed with upload
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
class PhotoCaptureDialog extends StatelessWidget {
|
||||||
|
/// The captured image file to display.
|
||||||
|
final File imageFile;
|
||||||
|
|
||||||
|
/// The name of the delivery for the confirmation message.
|
||||||
|
final String deliveryName;
|
||||||
|
|
||||||
|
const PhotoCaptureDialog({
|
||||||
|
super.key,
|
||||||
|
required this.imageFile,
|
||||||
|
required this.deliveryName,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Shows the photo capture confirmation dialog.
|
||||||
|
///
|
||||||
|
/// Returns `true` if the user confirms the upload, `false` if cancelled.
|
||||||
|
/// Returns `null` if the dialog is dismissed without selection.
|
||||||
|
static Future<bool?> show(
|
||||||
|
BuildContext context, {
|
||||||
|
required File imageFile,
|
||||||
|
required String deliveryName,
|
||||||
|
}) {
|
||||||
|
return showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext dialogContext) {
|
||||||
|
return PhotoCaptureDialog(
|
||||||
|
imageFile: imageFile,
|
||||||
|
deliveryName: deliveryName,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final l10n = AppLocalizations.of(context);
|
||||||
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
|
final textTheme = Theme.of(context).textTheme;
|
||||||
|
final screenSize = MediaQuery.of(context).size;
|
||||||
|
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text(
|
||||||
|
l10n.confirmPhoto,
|
||||||
|
style: textTheme.headlineSmall?.copyWith(
|
||||||
|
color: colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
maxHeight: screenSize.height * 0.5,
|
||||||
|
maxWidth: screenSize.width * 0.8,
|
||||||
|
),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
child: Image.file(
|
||||||
|
imageFile,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return Container(
|
||||||
|
height: 200,
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: colorScheme.errorContainer,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Icon(
|
||||||
|
Icons.broken_image,
|
||||||
|
size: 48,
|
||||||
|
color: colorScheme.error,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
l10n.uploadPhotoConfirmation(deliveryName),
|
||||||
|
style: textTheme.bodyMedium?.copyWith(
|
||||||
|
color: colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(false),
|
||||||
|
child: Text(
|
||||||
|
l10n.cancel,
|
||||||
|
style: TextStyle(color: colorScheme.error),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(true),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: colorScheme.primary,
|
||||||
|
foregroundColor: colorScheme.onPrimary,
|
||||||
|
),
|
||||||
|
child: Text(l10n.upload),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -117,5 +117,34 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"noNotesMessage": "No notes attached to this delivery",
|
"noNotesMessage": "No notes attached to this delivery",
|
||||||
"close": "Close"
|
"close": "Close",
|
||||||
|
"confirmPhoto": "Confirm Photo",
|
||||||
|
"uploadPhotoConfirmation": "Upload this photo for {name}?",
|
||||||
|
"@uploadPhotoConfirmation": {
|
||||||
|
"placeholders": {
|
||||||
|
"name": {"type": "String"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uploadingPhoto": "Uploading photo...",
|
||||||
|
"photoUploadSuccess": "Photo uploaded successfully",
|
||||||
|
"photoUploadFailed": "Upload failed: {statusCode}",
|
||||||
|
"@photoUploadFailed": {
|
||||||
|
"placeholders": {
|
||||||
|
"statusCode": {"type": "int"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cameraError": "Camera error: {message}",
|
||||||
|
"@cameraError": {
|
||||||
|
"placeholders": {
|
||||||
|
"message": {"type": "String"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uploadError": "Upload error: {message}",
|
||||||
|
"@uploadError": {
|
||||||
|
"placeholders": {
|
||||||
|
"message": {"type": "String"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"serverError": "Server error - Please contact support",
|
||||||
|
"retake": "Retake"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -117,5 +117,34 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"noNotesMessage": "Aucune note associée à cette livraison",
|
"noNotesMessage": "Aucune note associée à cette livraison",
|
||||||
"close": "Fermer"
|
"close": "Fermer",
|
||||||
|
"confirmPhoto": "Confirmer la photo",
|
||||||
|
"uploadPhotoConfirmation": "Telecharger cette photo pour {name}?",
|
||||||
|
"@uploadPhotoConfirmation": {
|
||||||
|
"placeholders": {
|
||||||
|
"name": {"type": "String"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uploadingPhoto": "Telechargement de la photo...",
|
||||||
|
"photoUploadSuccess": "Photo telechargee avec succes",
|
||||||
|
"photoUploadFailed": "Echec du telechargement: {statusCode}",
|
||||||
|
"@photoUploadFailed": {
|
||||||
|
"placeholders": {
|
||||||
|
"statusCode": {"type": "int"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cameraError": "Erreur de camera: {message}",
|
||||||
|
"@cameraError": {
|
||||||
|
"placeholders": {
|
||||||
|
"message": {"type": "String"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uploadError": "Erreur de telechargement: {message}",
|
||||||
|
"@uploadError": {
|
||||||
|
"placeholders": {
|
||||||
|
"message": {"type": "String"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"serverError": "Erreur serveur - Veuillez contacter le support",
|
||||||
|
"retake": "Reprendre"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -577,6 +577,60 @@ abstract class AppLocalizations {
|
|||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Close'**
|
/// **'Close'**
|
||||||
String get close;
|
String get close;
|
||||||
|
|
||||||
|
/// No description provided for @confirmPhoto.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Confirm Photo'**
|
||||||
|
String get confirmPhoto;
|
||||||
|
|
||||||
|
/// No description provided for @uploadPhotoConfirmation.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Upload this photo for {name}?'**
|
||||||
|
String uploadPhotoConfirmation(String name);
|
||||||
|
|
||||||
|
/// No description provided for @uploadingPhoto.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Uploading photo...'**
|
||||||
|
String get uploadingPhoto;
|
||||||
|
|
||||||
|
/// No description provided for @photoUploadSuccess.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Photo uploaded successfully'**
|
||||||
|
String get photoUploadSuccess;
|
||||||
|
|
||||||
|
/// No description provided for @photoUploadFailed.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Upload failed: {statusCode}'**
|
||||||
|
String photoUploadFailed(int statusCode);
|
||||||
|
|
||||||
|
/// No description provided for @cameraError.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Camera error: {message}'**
|
||||||
|
String cameraError(String message);
|
||||||
|
|
||||||
|
/// No description provided for @uploadError.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Upload error: {message}'**
|
||||||
|
String uploadError(String message);
|
||||||
|
|
||||||
|
/// No description provided for @serverError.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Server error - Please contact support'**
|
||||||
|
String get serverError;
|
||||||
|
|
||||||
|
/// No description provided for @retake.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Retake'**
|
||||||
|
String get retake;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AppLocalizationsDelegate
|
class _AppLocalizationsDelegate
|
||||||
|
|||||||
@ -267,4 +267,39 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get close => 'Close';
|
String get close => 'Close';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get confirmPhoto => 'Confirm Photo';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String uploadPhotoConfirmation(String name) {
|
||||||
|
return 'Upload this photo for $name?';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get uploadingPhoto => 'Uploading photo...';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get photoUploadSuccess => 'Photo uploaded successfully';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String photoUploadFailed(int statusCode) {
|
||||||
|
return 'Upload failed: $statusCode';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String cameraError(String message) {
|
||||||
|
return 'Camera error: $message';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String uploadError(String message) {
|
||||||
|
return 'Upload error: $message';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get serverError => 'Server error - Please contact support';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get retake => 'Retake';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -267,4 +267,39 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String get close => 'Fermer';
|
String get close => 'Fermer';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get confirmPhoto => 'Confirmer la photo';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String uploadPhotoConfirmation(String name) {
|
||||||
|
return 'Telecharger cette photo pour $name?';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get uploadingPhoto => 'Telechargement de la photo...';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get photoUploadSuccess => 'Photo telechargee avec succes';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String photoUploadFailed(int statusCode) {
|
||||||
|
return 'Echec du telechargement: $statusCode';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String cameraError(String message) {
|
||||||
|
return 'Erreur de camera: $message';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String uploadError(String message) {
|
||||||
|
return 'Erreur de telechargement: $message';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get serverError => 'Erreur serveur - Veuillez contacter le support';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get retake => 'Reprendre';
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user