import 'dart:convert'; import 'dart:io'; import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:mobile_scanner/mobile_scanner.dart'; import 'package:qhd_prevention/customWidget/toast_util.dart'; import 'package:qhd_prevention/pages/home/study/face_ecognition_page.dart'; import 'package:qhd_prevention/pages/home/work/risk_list_page.dart'; import 'package:qhd_prevention/pages/my_appbar.dart'; import 'package:image_picker/image_picker.dart'; import 'package:qhd_prevention/tools/tools.dart'; class ScanPage extends StatefulWidget { // const ScanPage({Key? key}) : super(key: key,); const ScanPage({super.key, required this.totalList}); final List totalList; @override State createState() => _ScanPageState(); } class _ScanPageState extends State { final MobileScannerController _controller = MobileScannerController(); bool _torchOn = false; @override void initState() { super.initState(); } @override void dispose() { _controller.dispose(); super.dispose(); } Future _scanFromGallery() async { final picker = ImagePicker(); final XFile? image = await picker.pickImage(source: ImageSource.gallery); if (image == null) return; try { // ★ 新版:返回 BarcodeCapture? final capture = await _controller.analyzeImage(image.path); if (capture != null && capture.barcodes.isNotEmpty) { final code = capture.barcodes.first.rawValue ?? ''; _showResult(code); } else { _showResult('未识别到二维码/条码'); } } catch (e) { _showResult('扫描失败:$e'); } } void _showResult(String result) { try { if (result.contains('STUDENT_ID')) { final Map stuInfo = jsonDecode(result); print('stuInfo: $stuInfo'); // 兼容性提取:res.result.split("@")[1] String? stuId; final parts = result.split('@'); if (parts.length > 1) stuId = parts[1]; // userId = res.result.substring(0, res.result.indexOf('%_face')) String? userId; final idx = result.indexOf('%_face'); if (idx >= 0) { userId = result.substring(0, idx); } print('stuId: $stuId, userId: $userId'); // 比较登录用户 id 与解析到的 stuInfo.USER_ID if (SessionService.instance.loginUserId == stuInfo['USER_ID']) { goToFace(stuInfo); } else { ToastUtil.showNormal(context, '当前登录账号不匹配,无法扫码学习,请切换至正确的账号后再尝试人脸识别!'); return; } } else { // 不是 STUDENT_ID 的情况:按列表 id 匹配 bool found = false; final listId = result; for (final item in widget.totalList) { if (item['LISTMANAGER_ID'] == listId) { found = true; goToList(listId: item['LISTMANAGER_ID'], listName: item['NAME']); break; } } if (!found) { ToastUtil.showError(context, '无法检查该清单'); } } } catch (e, st) { // 捕获解析或运行时错误 print('handleScanResult error: $e\n$st'); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('扫码处理失败: ${e.toString()}')), ); } } // 人脸识别跳转 void goToFace(Map stuInfo) async { print('navigate to face with $stuInfo'); final passed = await pushPage( FaceRecognitionPage(studentId: stuInfo['STUDENT_ID'], mode: FaceMode.auto), context, ); if (passed == true) { ToastUtil.showSuccess(context, '验证成功'); } else { ToastUtil.showError(context, '验证失败'); } } // 跳转到清单页面 void goToList({required String listId, required String listName}) { print('navigate to list: $listId, name: $listName'); Navigator.pop(context,Animation); pushPage(RiskListPage(1, listId), context); } @override Widget build(BuildContext context) { // 中心扫描框大小 const double scanSize = 250; final Size screen = MediaQuery.of(context).size; final double left = (screen.width - scanSize) / 2; final double top = (screen.height - scanSize) / 3 - kToolbarHeight; // 因为 SafeArea + AppBar 占了高度,所以减去 toolbar 高度 const double cornerSize = 20.0; // 角标正方形区域大小 const double strokeWidth = 4.0; // 边线宽度 return Scaffold( appBar: MyAppbar( title: "二维码/条码扫描", actions: [ TextButton( onPressed: _scanFromGallery, child: const Text( "相册", style: TextStyle(color: Colors.white, fontSize: 16), ), ), ], ), body: Stack( children: [ // 1. 摄像头预览 MobileScanner( controller: _controller, onDetect: (capture) { for (final barcode in capture.barcodes) { final code = barcode.rawValue; if (code != null && mounted) { _controller.stop(); _showResult(code); break; } } }, ), // 2. 半透明遮罩 // 顶部 // 1. 顶部遮罩 Positioned( left: 0, right: 0, top: 0, height: top, // 从顶到底部到扫描框上边缘 child: Container(color: Colors.black54), ), // 2. 底部遮罩 Positioned( left: 0, right: 0, top: top + scanSize, // 从扫描框下边缘开始 bottom: 0, child: Container(color: Colors.black54), ), // 3. 左侧遮罩 Positioned( left: 0, top: top, width: left, // 从屏幕左侧到扫描框左边缘 height: scanSize, // 和扫描框一样高 child: Container(color: Colors.black54), ), // 4. 右侧遮罩 Positioned( left: left + scanSize, top: top, right: 0, height: scanSize, // 和扫描框一样高 child: Container(color: Colors.black54), ), // 3. 扫描框四个角 // 左上 Positioned( left: left, top: top, child: _corner(size: cornerSize, stroke: strokeWidth), ), // 右上 Positioned( left: left + scanSize - cornerSize, top: top, child: _corner(size: cornerSize, stroke: strokeWidth, rotation: 1), ), // 左下 Positioned( left: left, top: top + scanSize - cornerSize, child: _corner(size: cornerSize, stroke: strokeWidth, rotation: 3), ), // 右下 Positioned( left: left + scanSize - cornerSize, top: top + scanSize - cornerSize, child: _corner(size: cornerSize, stroke: strokeWidth, rotation: 2), ), // 闪光灯按钮 Positioned( left: (screen.width - 40) / 2, top: top + scanSize - 60, child: IconButton( iconSize: 32, color: Colors.white, icon: Icon(_torchOn ? Icons.flashlight_off_outlined : Icons.flashlight_on_outlined), onPressed: () { _controller.toggleTorch(); setState(() { _torchOn = !_torchOn; }); }, ), ), ], ), ); } /// 角装饰:一个 L 形的蓝色粗边 Widget _corner({ double size = 20, double stroke = 4, int rotation = 0, // 0=左上, 1=右上, 2=右下, 3=左下 }) { return Transform.rotate( angle: rotation * math.pi / 2, child: Container( width: size, height: size, decoration: BoxDecoration( border: Border( top: BorderSide(color: Colors.blue, width: stroke), left: BorderSide(color: Colors.blue, width: stroke), ), ), ), ); } }