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>
134 lines
3.9 KiB
Dart
134 lines
3.9 KiB
Dart
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),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|