import 'dart:io'; import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:getx_scaffold/getx_scaffold.dart'; import 'package:image/image.dart' as img; import 'package:image_picker/image_picker.dart'; import 'package:ln_jq_app/common/model/base_model.dart'; import 'package:ln_jq_app/common/model/vehicle_info.dart'; import 'package:ln_jq_app/storage_service.dart'; import 'package:qr_code_scanner_plus/qr_code_scanner_plus.dart'; import 'package:zxing_lib/common.dart'; import 'package:zxing_lib/qrcode.dart'; import 'package:zxing_lib/zxing.dart'; class QrCodeController extends GetxController with BaseControllerMixin, GetSingleTickerProviderStateMixin { @override String get builderId => 'qrcode'; // --- Animation --- late final AnimationController animationController; late final Animation scanAnimation; // --- QR Scanning --- final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); QRViewController? qrViewController; final Rx result = Rx(null); final RxBool isFlashOn = false.obs; @override void onInit() { super.onInit(); requestPermission(); animationController = AnimationController( duration: const Duration(milliseconds: 2500), vsync: this, ); scanAnimation = Tween(begin: 0, end: 1).animate(animationController); animationController.repeat(reverse: false); } /// 当 QRView 创建时调用 void onQRViewCreated(QRViewController controller) { this.qrViewController = controller; // 监听扫描到的数据 controller.scannedDataStream.listen((scanData) { if (scanData.code != null && result.value?.code != scanData.code) { result.value = scanData; qrViewController?.pauseCamera(); animationController.stop(); renderResult(scanData.code!); } }); } void resumeScanner() { result.value = null; qrViewController?.resumeCamera(); animationController.repeat(reverse: false); } /// 从相册选择图片并扫描二维码 void scanFromGallery() async { try { final XFile? imageFile = await ImagePicker().pickImage(source: ImageSource.gallery); if (imageFile == null) return; // 用户取消了选择 qrViewController?.pauseCamera(); animationController.stop(); String? scanResult; try { final image = img.decodeImage(await File(imageFile.path).readAsBytes()); if (image != null) { //扫描图片 final pixels = Int32List.fromList( image.map((pixel) { return (pixel.a.toInt() << 24) | (pixel.r.toInt() << 16) | (pixel.g.toInt() << 8) | pixel.b.toInt(); }).toList(), ); final source = RGBLuminanceSource(image.width, image.height, pixels); final bitmap = BinaryBitmap(HybridBinarizer(source)); final reader = QRCodeReader(); final result = reader.decode(bitmap); scanResult = result.text; } } on NotFoundException { scanResult = null; } catch (e) { //异常 scanResult = null; } if (scanResult != null) { renderResult(scanResult); } else { showErrorToast('未识别到二维码'); resumeScanner(); } } catch (e) { showErrorToast('从相册选择失败'); resumeScanner(); } } /// 切换闪光灯 void toggleFlash() async { await qrViewController?.toggleFlash(); isFlashOn.value = (await qrViewController?.getFlashStatus()) ?? false; } /// 翻转相机 void flipCamera() async { await qrViewController?.flipCamera(); } void requestPermission() async { final List results = await Future.wait([ requestCameraPermission(), requestPhotosPermission(), ]); final isCameraGranted = results[0]; final isPhotosGranted = results[1]; if (!isCameraGranted) { showErrorToast('相机权限未被授予,请到权限管理中打开'); } if (!isPhotosGranted) { showErrorToast('相册权限未被授予,请到权限管理中打开'); } } //扫码结果处理 void renderResult(String resultStr) async { showLoading("正在获取车辆信息..."); try { var responseData = await HttpService.to.get( "appointment/truck/base-info?vin=$resultStr", ); if (responseData == null || responseData.data == null) { showToast('无法获取车辆信息,请检查网络或稍后重试'); resumeScanner(); return; } var result = BaseModel.fromJson(responseData.data); final vehicle = VehicleInfo.fromJson(result.data as Map); //保存使用 await StorageService.to.saveVehicleInfo(vehicle); Get.back(result: true); } on DioException catch (e) { showErrorToast("网络请求失败,请稍后重试"); resumeScanner(); } catch (e, stackTrace) { showErrorToast("处理失败,请稍后重试"); resumeScanner(); // 未知异常,恢复扫描 } finally { dismissLoading(); } } @override void onClose() { qrViewController?.dispose(); animationController.dispose(); super.onClose(); } }