Ajout de la gestion des containers (création, édition, suppression, affichage des détails).
Introduction d'un système de génération de QR codes unifié et d'un mode de sélection multiple.
**Features:**
- **Gestion des Containers :**
- Nouvelle page de gestion des containers (`container_management_page.dart`) avec recherche et filtres.
- Formulaire de création/édition de containers (`container_form_page.dart`) avec génération d'ID automatique.
- Page de détails d'un container (`container_detail_page.dart`) affichant son contenu et ses caractéristiques.
- Ajout des routes et du provider (`ContainerProvider`) nécessaires.
- **Modèle de Données :**
- Ajout du `ContainerModel` pour représenter les boîtes, flight cases, etc.
- Le modèle `EquipmentModel` a été enrichi avec des caractéristiques physiques (poids, dimensions).
- **QR Codes :**
- Nouveau service unifié (`UnifiedPDFGeneratorService`) pour générer des PDFs de QR codes pour n'importe quelle entité.
- Services `PDFGeneratorService` et `ContainerPDFGeneratorService` transformés en wrappers pour maintenir la compatibilité.
- Amélioration de la performance de la génération de QR codes en masse.
- **Interface Utilisateur (UI/UX) :**
- Nouvelle page de détails pour le matériel (`equipment_detail_page.dart`).
- Ajout d'un `SelectionModeMixin` pour gérer la sélection multiple dans les pages de gestion.
- Dialogues réutilisables pour l'affichage de QR codes (`QRCodeDialog`) et la sélection de format d'impression (`QRCodeFormatSelectorDialog`).
- Ajout d'un bouton "Gérer les boîtes" sur la page de gestion du matériel.
**Refactorisation :**
- L' `IdGenerator` a été déplacé dans le répertoire `utils` et étendu pour gérer les containers.
- Mise à jour de nombreuses dépendances `pubspec.yaml` vers des versions plus récentes.
- Séparation de la logique d'affichage des containers et du matériel dans des widgets dédiés (`ContainerHeaderCard`, `EquipmentParentContainers`, etc.).
174 lines
4.7 KiB
Dart
174 lines
4.7 KiB
Dart
import 'dart:typed_data';
|
|
import 'dart:ui' as ui;
|
|
import 'package:flutter/services.dart';
|
|
import 'package:qr_flutter/qr_flutter.dart';
|
|
import 'package:em2rp/utils/colors.dart';
|
|
|
|
/// Service pour la génération de QR codes optimisée
|
|
class QRCodeService {
|
|
// Cache pour éviter de régénérer les mêmes QR codes
|
|
static final Map<String, Uint8List> _qrCache = {};
|
|
static ui.Image? _cachedLogoImage;
|
|
|
|
/// Génère un QR code simple sans logo
|
|
static Future<Uint8List> generateQRCode(
|
|
String data, {
|
|
double size = 512,
|
|
bool useCache = true,
|
|
}) async {
|
|
// Vérifier le cache
|
|
if (useCache && _qrCache.containsKey(data)) {
|
|
return _qrCache[data]!;
|
|
}
|
|
|
|
final qrValidationResult = QrValidator.validate(
|
|
data: data,
|
|
version: QrVersions.auto,
|
|
errorCorrectionLevel: QrErrorCorrectLevel.L,
|
|
);
|
|
|
|
if (qrValidationResult.status != QrValidationStatus.valid) {
|
|
throw Exception('QR code validation failed for data: $data');
|
|
}
|
|
|
|
final qrCode = qrValidationResult.qrCode!;
|
|
final painter = QrPainter.withQr(
|
|
qr: qrCode,
|
|
eyeStyle: const QrEyeStyle(
|
|
eyeShape: QrEyeShape.square,
|
|
color: AppColors.noir,
|
|
),
|
|
gapless: true,
|
|
);
|
|
|
|
final picData = await painter.toImageData(size, format: ui.ImageByteFormat.png);
|
|
final bytes = picData!.buffer.asUint8List();
|
|
|
|
// Mettre en cache
|
|
if (useCache) {
|
|
_qrCache[data] = bytes;
|
|
}
|
|
|
|
return bytes;
|
|
}
|
|
|
|
/// Génère un QR code avec logo embarqué
|
|
static Future<Uint8List> generateQRCodeWithLogo(
|
|
String data, {
|
|
double size = 512,
|
|
bool useCache = true,
|
|
}) async {
|
|
final cacheKey = '${data}_logo';
|
|
|
|
// Vérifier le cache
|
|
if (useCache && _qrCache.containsKey(cacheKey)) {
|
|
return _qrCache[cacheKey]!;
|
|
}
|
|
|
|
final qrValidationResult = QrValidator.validate(
|
|
data: data,
|
|
version: QrVersions.auto,
|
|
errorCorrectionLevel: QrErrorCorrectLevel.L,
|
|
);
|
|
|
|
if (qrValidationResult.status != QrValidationStatus.valid) {
|
|
throw Exception('QR code validation failed for data: $data');
|
|
}
|
|
|
|
final qrCode = qrValidationResult.qrCode!;
|
|
final embedded = await _loadLogoImage();
|
|
|
|
final painter = QrPainter.withQr(
|
|
qr: qrCode,
|
|
embeddedImage: embedded,
|
|
embeddedImageStyle: const QrEmbeddedImageStyle(
|
|
size: Size(80, 80),
|
|
),
|
|
gapless: true,
|
|
);
|
|
|
|
final picData = await painter.toImageData(size, format: ui.ImageByteFormat.png);
|
|
final bytes = picData!.buffer.asUint8List();
|
|
|
|
// Mettre en cache
|
|
if (useCache) {
|
|
_qrCache[cacheKey] = bytes;
|
|
}
|
|
|
|
return bytes;
|
|
}
|
|
|
|
/// Charge le logo depuis les assets (avec cache)
|
|
static Future<ui.Image> _loadLogoImage() async {
|
|
if (_cachedLogoImage != null) {
|
|
return _cachedLogoImage!;
|
|
}
|
|
|
|
final data = await rootBundle.load('assets/logos/SquareLogoBlack.png');
|
|
final codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
|
|
final frame = await codec.getNextFrame();
|
|
_cachedLogoImage = frame.image;
|
|
return _cachedLogoImage!;
|
|
}
|
|
|
|
/// Génère plusieurs QR codes en parallèle (optimisé)
|
|
static Future<List<Uint8List>> generateBulkQRCodes(
|
|
List<String> dataList, {
|
|
double size = 512,
|
|
bool withLogo = false,
|
|
bool useCache = true,
|
|
}) async {
|
|
// Si tout est en cache, retourner immédiatement
|
|
if (useCache) {
|
|
final allCached = dataList.every((data) {
|
|
final key = withLogo ? '${data}_logo' : data;
|
|
return _qrCache.containsKey(key);
|
|
});
|
|
|
|
if (allCached) {
|
|
return dataList.map((data) {
|
|
final key = withLogo ? '${data}_logo' : data;
|
|
return _qrCache[key]!;
|
|
}).toList();
|
|
}
|
|
}
|
|
|
|
// Batching adaptatif optimisé selon la taille et le nombre
|
|
int batchSize;
|
|
if (size <= 200) {
|
|
batchSize = 100; // Petits QR : lots de 100
|
|
} else if (size <= 300) {
|
|
batchSize = 50; // Moyens QR : lots de 50
|
|
} else if (size <= 500) {
|
|
batchSize = 20; // Grands QR : lots de 20
|
|
} else {
|
|
batchSize = 10; // Très grands : lots de 10
|
|
}
|
|
|
|
final List<Uint8List> results = [];
|
|
|
|
for (int i = 0; i < dataList.length; i += batchSize) {
|
|
final batch = dataList.skip(i).take(batchSize).toList();
|
|
final batchResults = await Future.wait(
|
|
batch.map((data) => withLogo
|
|
? generateQRCodeWithLogo(data, size: size, useCache: useCache)
|
|
: generateQRCode(data, size: size, useCache: useCache)),
|
|
);
|
|
results.addAll(batchResults);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
/// Vide le cache des QR codes
|
|
static void clearCache() {
|
|
_qrCache.clear();
|
|
}
|
|
|
|
/// Obtient la taille du cache
|
|
static int getCacheSize() {
|
|
return _qrCache.length;
|
|
}
|
|
}
|
|
|