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",
|
||||
"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",
|
||||
"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:
|
||||
/// **'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
|
||||
|
||||
@ -267,4 +267,39 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
|
||||
@override
|
||||
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
|
||||
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