From c4b4c7f75fd5adbc47daff0b02afba66d0554bd4 Mon Sep 17 00:00:00 2001 From: WaseemAbbasi22 <50428976+WaseemAbbasi22@users.noreply.github.com> Date: Sun, 5 Apr 2026 10:15:07 +0300 Subject: [PATCH] QR scanning fixes for Huawei asset scan --- lib/views/widgets/qr/asset_scan_qr.dart | 523 ++++++++++++++++++------ 1 file changed, 388 insertions(+), 135 deletions(-) diff --git a/lib/views/widgets/qr/asset_scan_qr.dart b/lib/views/widgets/qr/asset_scan_qr.dart index bc001e95..022cc559 100644 --- a/lib/views/widgets/qr/asset_scan_qr.dart +++ b/lib/views/widgets/qr/asset_scan_qr.dart @@ -1,14 +1,232 @@ +//Todo delete after testing + +// import 'dart:async'; +// import 'dart:io'; +// +// import 'package:flutter/material.dart'; +// import 'package:fluttertoast/fluttertoast.dart'; +// import 'package:mobile_scanner/mobile_scanner.dart'; +// import 'package:provider/provider.dart'; +// import 'package:qr_code_scanner_plus/qr_code_scanner_plus.dart'; +// import 'package:test_sa/extensions/context_extension.dart'; +// import 'package:test_sa/extensions/int_extensions.dart'; +// import 'package:test_sa/extensions/string_extensions.dart'; +// import 'package:test_sa/extensions/text_extensions.dart'; +// import 'package:test_sa/extensions/widget_extensions.dart'; +// import 'package:test_sa/helper/utils.dart'; +// import 'package:test_sa/models/device/asset.dart'; +// import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; +// import 'package:test_sa/modules/tm_module/device_transfer/search_device_page.dart'; +// import 'package:test_sa/new_views/app_style/app_color.dart'; +// import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; +// +// import '../../../controllers/providers/api/devices_provider.dart'; +// import '../../../models/device/asset_search.dart'; +// import '../../../new_views/common_widgets/app_filled_button.dart'; +// +// class AssetScanQr extends StatefulWidget { +// static const String id = "/asset-scan-qr"; +// +// const AssetScanQr({Key? key, required this.title, this.multiSelection = false, this.enablePickManually = true}) : super(key: key); +// final String title; +// final bool multiSelection; +// final bool enablePickManually; +// +// @override +// _AssetScanQrState createState() => _AssetScanQrState(); +// } +// +// class _AssetScanQrState extends State { +// // Barcode result; +// // QRViewController? _controller; +// bool _scanDone = false; +// bool _scanning = false; +// final GlobalKey qrKey = GlobalKey(debugLabel: 'QR_scanner'); +// late AssetProvider _devicesProvider; +// +// MobileScannerController? controller; +// +// @override +// void initState() { +// // TODO: implement initState +// super.initState(); +// controller = MobileScannerController( +// detectionSpeed: DetectionSpeed.noDuplicates, +// formats: [], +// returnImage: false, +// torchEnabled: false, +// invertImage: false, +// autoZoom: true, +// ); +// } +// +// @override +// void dispose() { +// super.dispose(); +// controller?.dispose(); +// _devicesProvider.searchReset(); +// } +// +// _pickManually() async { +// controller?.pause(); +// await Navigator.push(context, MaterialPageRoute(builder: (context) => SearchDevicePage(multiSelection: widget.multiSelection))).then((value) => controller?.stop()); +// } +// +// _getDevice(String result, {bool isQr = false}) async { +// _devicesProvider.reset(); +// await _devicesProvider.getAssets( +// search: DeviceSearch(assetNo: result, assetSerialNumber: ""), +// isQr: isQr, +// ); +// return _devicesProvider.devices; +// } +// +// @override +// Widget build(BuildContext context) { +// _devicesProvider = Provider.of(context); +// return Scaffold( +// appBar: DefaultAppBar(title: widget.title), +// body: Column( +// children: [ +// Stack( +// children: [ +// MobileScanner( +// key: qrKey, +// tapToFocus: true, +// controller: controller, +// onDetect: (result) async { +// String? scanData = result.barcodes.first.rawValue; +// if (!_scanDone) { +// _scanDone = true; +// if (!widget.enablePickManually) { +// if (scanData != null) { +// Navigator.of(context).pop(Asset(assetNumber: scanData)); +// } else { +// _scanDone = false; +// } +// } else { +// setState(() { +// _scanning = true; +// }); +// final result = await _getDevice(scanData!, isQr: true); +// setState(() { +// _scanning = false; +// }); +// if (result.isNotEmpty) { +// if (widget.multiSelection) { +// Navigator.of(context).pop([result[0]]); +// } else { +// Navigator.of(context).pop(result[0]); +// } +// } else { +// Utils.showToast("No Item Found. Try Again", toastGravity: ToastGravity.CENTER); +// _scanDone = false; +// } +// } +// } +// }, +// ), +// +// // QRView( +// // key: qrKey, +// // onQRViewCreated: (QRViewController controller) { +// // setState(() { +// // _controller = controller; +// // }); +// // controller.scannedDataStream.listen((scanData) async { +// // if (!_scanDone) { +// // _scanDone = true; +// // if (!widget.enablePickManually) { +// // if (scanData.code != null) { +// // Navigator.of(context).pop(Asset(assetNumber: scanData.code!)); +// // } else { +// // _scanDone = false; +// // } +// // } else { +// // _controller?.pauseCamera(); +// // setState(() { +// // _scanning = true; +// // }); +// // final result = await _getDevice(scanData.code!, isQr: true); +// // setState(() { +// // _scanning = false; +// // }); +// // if (result.isNotEmpty) { +// // if (widget.multiSelection) { +// // Navigator.of(context).pop([result[0]]); +// // } else { +// // Navigator.of(context).pop(result[0]); +// // } +// // } else { +// // Utils.showToast("No Item Found. Try Again", toastGravity: ToastGravity.CENTER); +// // _controller?.resumeCamera(); +// // _scanDone = false; +// // } +// // } +// // } +// // }); +// // }, +// // ), +// Center( +// child: 'scan'.toSvgAsset( +// height: 283.toScreenHeight.toInt(), +// width: 283.toScreenWidth.toInt(), +// fit: BoxFit.fitHeight, +// ), +// ), +// if (_scanning) +// Container( +// width: 56.toScreenHeight, +// height: 56.toScreenHeight, +// padding: const EdgeInsets.all(8), +// decoration: const BoxDecoration(shape: BoxShape.circle, color: Colors.white), +// child: const CircularProgressIndicator(color: AppColor.primary10, strokeWidth: 3), +// ).center +// ], +// ).expanded, +// if (widget.enablePickManually) +// FooterActionButton.footerContainer( +// context: context, +// child: AppFilledButton( +// label: context.translation.pickManually, +// onPressed: _pickManually, +// ), +// ), +// ], +// ), +// // bottomSheet: +// // 16.height, +// +// // Container( +// // height: 82.toScreenHeight, +// // color: Colors.white, +// // width: MediaQuery.of(context).size.width, +// // child: Center( +// // child: SizedBox( +// // height: 50.toScreenHeight, +// // width: 358.toScreenWidth, +// // child: AppFilledButton( +// // label: context.translation.pickManually, +// // onPressed: _pickManually, +// // ), +// // ), +// // ), +// // ) +// ); +// } +// } + import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; +import 'package:google_api_availability/google_api_availability.dart'; import 'package:mobile_scanner/mobile_scanner.dart'; import 'package:provider/provider.dart'; import 'package:qr_code_scanner_plus/qr_code_scanner_plus.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; -import 'package:test_sa/extensions/string_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/helper/utils.dart'; @@ -25,7 +243,13 @@ import '../../../new_views/common_widgets/app_filled_button.dart'; class AssetScanQr extends StatefulWidget { static const String id = "/asset-scan-qr"; - const AssetScanQr({Key? key, required this.title, this.multiSelection = false, this.enablePickManually = true}) : super(key: key); + const AssetScanQr({ + Key? key, + required this.title, + this.multiSelection = false, + this.enablePickManually = true, + }) : super(key: key); + final String title; final bool multiSelection; final bool enablePickManually; @@ -35,42 +259,77 @@ class AssetScanQr extends StatefulWidget { } class _AssetScanQrState extends State { - // Barcode result; - // QRViewController? _controller; - bool _scanDone = false; - bool _scanning = false; final GlobalKey qrKey = GlobalKey(debugLabel: 'QR_scanner'); + late AssetProvider _devicesProvider; - MobileScannerController? controller; + MobileScannerController? mobileController; + QRViewController? qrController; + + bool _scanDone = false; + bool _scanning = false; + + bool _isHuawei = false; + bool _isLoading = true; @override void initState() { - // TODO: implement initState super.initState(); - controller = MobileScannerController( - detectionSpeed: DetectionSpeed.noDuplicates, - formats: [], - returnImage: false, - torchEnabled: false, - invertImage: false, - autoZoom: true, - ); + _initScanner(); + } + + Future _initScanner() async { + _isHuawei = Platform.isAndroid && !(await _isGoogleServicesAvailable()); + + if (!_isHuawei) { + mobileController = MobileScannerController( + detectionSpeed: DetectionSpeed.noDuplicates, + formats: [], + returnImage: false, + torchEnabled: false, + invertImage: false, + autoZoom: true, + ); + } + + if (mounted) { + setState(() { + _isLoading = false; + }); + } + } + + Future _isGoogleServicesAvailable() async { + GooglePlayServicesAvailability availability = await GoogleApiAvailability.instance.checkGooglePlayServicesAvailability(); + + String status = availability.toString().split('.').last; + return status == "success"; } @override void dispose() { - super.dispose(); - controller?.dispose(); + mobileController?.dispose(); + qrController?.dispose(); _devicesProvider.searchReset(); + super.dispose(); } - _pickManually() async { - controller?.pause(); - await Navigator.push(context, MaterialPageRoute(builder: (context) => SearchDevicePage(multiSelection: widget.multiSelection))).then((value) => controller?.stop()); + Future _pickManually() async { + mobileController?.pause(); + qrController?.pauseCamera(); + + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => SearchDevicePage(multiSelection: widget.multiSelection), + ), + ); + + mobileController?.stop(); + qrController?.resumeCamera(); } - _getDevice(String result, {bool isQr = false}) async { + Future> _getDevice(String result, {bool isQr = false}) async { _devicesProvider.reset(); await _devicesProvider.getAssets( search: DeviceSearch(assetNo: result, assetSerialNumber: ""), @@ -79,6 +338,54 @@ class _AssetScanQrState extends State { return _devicesProvider.devices; } + Future _handleScan(String scanData) async { + if (_scanDone) return; + + _scanDone = true; + + if (!widget.enablePickManually) { + Navigator.of(context).pop(Asset(assetNumber: scanData)); + return; + } + + setState(() { + _scanning = true; + }); + + final result = await _getDevice(scanData, isQr: true); + + setState(() { + _scanning = false; + }); + + if (result.isNotEmpty) { + if (widget.multiSelection) { + Navigator.of(context).pop([result[0]]); + } else { + Navigator.of(context).pop(result[0]); + } + } else { + Utils.showToast( + "No Item Found. Try Again", + toastGravity: ToastGravity.CENTER, + ); + _scanDone = false; + mobileController?.start(); + qrController?.resumeCamera(); + } + } + + void _onQrViewCreated(QRViewController controller) { + qrController = controller; + + controller.scannedDataStream.listen((scanData) async { + if (_scanDone) return; + if (scanData.code == null || scanData.code!.isEmpty) return; + + await _handleScan(scanData.code!); + }); + } + @override Widget build(BuildContext context) { _devicesProvider = Provider.of(context); @@ -86,102 +393,66 @@ class _AssetScanQrState extends State { appBar: DefaultAppBar(title: widget.title), body: Column( children: [ - Stack( - children: [ - MobileScanner( - key: qrKey, - tapToFocus: true, - controller: controller, - onDetect: (result) async { - String? scanData = result.barcodes.first.rawValue; - if (!_scanDone) { - _scanDone = true; - if (!widget.enablePickManually) { - if (scanData != null) { - Navigator.of(context).pop(Asset(assetNumber: scanData)); - } else { - _scanDone = false; - } - } else { - setState(() { - _scanning = true; - }); - final result = await _getDevice(scanData!, isQr: true); - setState(() { - _scanning = false; - }); - if (result.isNotEmpty) { - if (widget.multiSelection) { - Navigator.of(context).pop([result[0]]); - } else { - Navigator.of(context).pop(result[0]); - } - } else { - Utils.showToast("No Item Found. Try Again", toastGravity: ToastGravity.CENTER); - _scanDone = false; - } - } - } - }, - ), + _isLoading + ? const CircularProgressIndicator(color: AppColor.primary10).center + : Stack( + children: [ + _isHuawei + ? QRView( + key: qrKey, + onQRViewCreated: _onQrViewCreated, + overlay: QrScannerOverlayShape( + borderColor: Colors.white, + borderRadius: 12, + borderLength: 30, + borderWidth: 4, + cutOutSize: MediaQuery.of(context).size.width * 0.7, + ), + ) + : MobileScanner( + key: qrKey, + controller: mobileController, + tapToFocus: true, + onDetect: (capture) async { + if (_scanDone) return; - // QRView( - // key: qrKey, - // onQRViewCreated: (QRViewController controller) { - // setState(() { - // _controller = controller; - // }); - // controller.scannedDataStream.listen((scanData) async { - // if (!_scanDone) { - // _scanDone = true; - // if (!widget.enablePickManually) { - // if (scanData.code != null) { - // Navigator.of(context).pop(Asset(assetNumber: scanData.code!)); - // } else { - // _scanDone = false; - // } - // } else { - // _controller?.pauseCamera(); - // setState(() { - // _scanning = true; - // }); - // final result = await _getDevice(scanData.code!, isQr: true); - // setState(() { - // _scanning = false; - // }); - // if (result.isNotEmpty) { - // if (widget.multiSelection) { - // Navigator.of(context).pop([result[0]]); - // } else { - // Navigator.of(context).pop(result[0]); - // } - // } else { - // Utils.showToast("No Item Found. Try Again", toastGravity: ToastGravity.CENTER); - // _controller?.resumeCamera(); - // _scanDone = false; - // } - // } - // } - // }); - // }, - // ), - Center( - child: 'scan'.toSvgAsset( - height: 283.toScreenHeight.toInt(), - width: 283.toScreenWidth.toInt(), - fit: BoxFit.fitHeight, - ), - ), - if (_scanning) - Container( - width: 56.toScreenHeight, - height: 56.toScreenHeight, - padding: const EdgeInsets.all(8), - decoration: const BoxDecoration(shape: BoxShape.circle, color: Colors.white), - child: const CircularProgressIndicator(color: AppColor.primary10, strokeWidth: 3), - ).center - ], - ).expanded, + final barcodes = capture.barcodes; + if (barcodes.isEmpty) return; + + final value = barcodes.first.rawValue; + if (value == null || value.isEmpty) return; + + await _handleScan(value); + }, + ), + + /// SVG Overlay (only for non-Huawei) + if (!_isHuawei) + Center( + child: 'scan'.toSvgAsset( + height: 283.toScreenHeight.toInt(), + width: 283.toScreenWidth.toInt(), + fit: BoxFit.fitHeight, + ), + ), + + /// Loader + if (_scanning) + Container( + width: 56.toScreenHeight, + height: 56.toScreenHeight, + padding: const EdgeInsets.all(8), + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: Colors.white, + ), + child: const CircularProgressIndicator( + color: AppColor.primary10, + strokeWidth: 3, + ), + ).center, + ], + ).expanded, if (widget.enablePickManually) FooterActionButton.footerContainer( context: context, @@ -192,24 +463,6 @@ class _AssetScanQrState extends State { ), ], ), - // bottomSheet: - // 16.height, - - // Container( - // height: 82.toScreenHeight, - // color: Colors.white, - // width: MediaQuery.of(context).size.width, - // child: Center( - // child: SizedBox( - // height: 50.toScreenHeight, - // width: 358.toScreenWidth, - // child: AppFilledButton( - // label: context.translation.pickManually, - // onPressed: _pickManually, - // ), - // ), - // ), - // ) ); } }