From 68cc5c0df3c3cba6d07daea1860c9e4fbaa5de2a Mon Sep 17 00:00:00 2001 From: renolation Date: Sun, 2 Nov 2025 21:52:58 +0700 Subject: [PATCH] s --- .../pages/product_detail_page.dart | 201 +++++++++++++++++- 1 file changed, 199 insertions(+), 2 deletions(-) diff --git a/lib/features/products/presentation/pages/product_detail_page.dart b/lib/features/products/presentation/pages/product_detail_page.dart index 5e8163d..d9193a6 100644 --- a/lib/features/products/presentation/pages/product_detail_page.dart +++ b/lib/features/products/presentation/pages/product_detail_page.dart @@ -1,8 +1,10 @@ import 'package:dropdown_search/dropdown_search.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mobile_scanner/mobile_scanner.dart'; import '../../../../core/di/providers.dart'; +import '../../../../core/router/app_router.dart'; import '../../../../core/services/print_service.dart'; import '../../../../core/storage/secure_storage.dart'; import '../../../../core/utils/text_utils.dart'; @@ -97,8 +99,6 @@ class _ProductDetailPageState extends ConsumerState { return; } - print(storedEmail); - // Get all warehouse users final warehouseUsers = ref.read(usersListProvider) .where((user) => user.isWareHouseUser) @@ -141,6 +141,198 @@ class _ProductDetailPageState extends ConsumerState { }); } + void _showBarcodeScanner() { + final controller = MobileScannerController( + formats: const [BarcodeFormat.code128], + facing: CameraFacing.back, + ); + + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (context) => Container( + height: MediaQuery.of(context).size.height * 0.7, + decoration: const BoxDecoration( + color: Colors.black, + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + child: Column( + children: [ + // Header + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.grey.shade900, + borderRadius: const BorderRadius.vertical( + top: Radius.circular(20), + ), + ), + child: Row( + children: [ + const Icon( + Icons.qr_code_scanner, + color: Colors.white, + ), + const SizedBox(width: 12), + const Expanded( + child: Text( + 'Quét mã vạch', + style: TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ), + IconButton( + icon: const Icon(Icons.close, color: Colors.white), + onPressed: () { + controller.dispose(); + Navigator.pop(context); + }, + ), + ], + ), + ), + // Scanner + Expanded( + child: MobileScanner( + controller: controller, + onDetect: (capture) { + final List barcodes = capture.barcodes; + if (barcodes.isNotEmpty) { + final barcode = barcodes.first.rawValue; + if (barcode != null) { + controller.dispose(); + Navigator.pop(context); + _handleScannedBarcode(barcode); + } + } + }, + ), + ), + // Instructions + Container( + padding: const EdgeInsets.all(16), + color: Colors.grey.shade900, + child: const Text( + 'Đặt mã vạch Code 128 vào khung để quét', + style: TextStyle( + color: Colors.white70, + fontSize: 14, + ), + textAlign: TextAlign.center, + ), + ), + ], + ), + ), + ).whenComplete(() => controller.dispose()); + } + + Future _handleScannedBarcode(String barcode) async { + // Parse barcode to extract productId and optional stageId + // Format 1: "123" (only productId) + // Format 2: "123-456" (productId-stageId) + + int? productId; + int? stageId; + + if (barcode.contains('-')) { + // Format: productId-stageId + final parts = barcode.split('-'); + if (parts.length == 2) { + productId = int.tryParse(parts[0]); + stageId = int.tryParse(parts[1]); + } + } else { + // Format: productId only + productId = int.tryParse(barcode); + } + + if (productId == null) { + // Invalid barcode format + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Định dạng mã vạch không hợp lệ: "$barcode"'), + backgroundColor: Colors.red, + action: SnackBarAction( + label: 'OK', + textColor: Colors.white, + onPressed: () {}, + ), + ), + ); + return; + } + + // Show loading indicator + if (!mounted) return; + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => const Center( + child: CircularProgressIndicator(), + ), + ); + + try { + // Update the provider key and load product detail + setState(() { + _providerKey = '${widget.warehouseId}_$productId'; + }); + + // Clear current selections + _clearControllers(); + + // Load product detail data from API + await ref.read(productDetailProvider(_providerKey).notifier).loadProductDetail( + widget.warehouseId, + productId, + ); + + // Dismiss loading dialog + if (mounted) Navigator.of(context).pop(); + + // If stageId is provided, auto-select that stage + if (stageId != null && mounted) { + final stages = ref.read(productDetailProvider(_providerKey)).stages; + final stageIndex = stages.indexWhere( + (stage) => stage.productStageId == stageId, + ); + if (stageIndex != -1) { + ref.read(productDetailProvider(_providerKey).notifier).selectStage(stageIndex); + } + } + + // Show success message + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Đã tải sản phẩm ID: $productId'), + backgroundColor: Colors.green, + duration: const Duration(seconds: 2), + ), + ); + } + } catch (e) { + // Dismiss loading dialog + if (mounted) Navigator.of(context).pop(); + + // Show error message + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Lỗi khi tải sản phẩm: ${e.toString()}'), + backgroundColor: Colors.red, + duration: const Duration(seconds: 3), + ), + ); + } + } + } + @override Widget build(BuildContext context) { final theme = Theme.of(context); @@ -164,6 +356,11 @@ class _ProductDetailPageState extends ConsumerState { appBar: AppBar( title: Text('${operationTitle} ${productName}'), actions: [ + IconButton( + icon: const Icon(Icons.qr_code_scanner), + onPressed: _showBarcodeScanner, + tooltip: 'Quét mã vạch', + ), IconButton( icon: const Icon(Icons.refresh), onPressed: _onRefresh,