QinGang_interested/lib/pages/home/scan_page.dart

258 lines
7.2 KiB
Dart

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/my_appbar.dart';
import 'package:image_picker/image_picker.dart';
import 'package:qhd_prevention/tools/tools.dart';
import 'package:qhd_prevention/services/SessionService.dart';
// 扫码类型
enum ScanType {
/// 入职
Onboarding,
}
class ScanPage extends StatefulWidget {
// const ScanPage({Key? key}) : super(key: key,);
const ScanPage({super.key, required this.type});
final ScanType type;
@override
State<ScanPage> createState() => _ScanPageState();
}
class _ScanPageState extends State<ScanPage> {
final MobileScannerController _controller = MobileScannerController();
bool _torchOn = false;
@override
void initState() {
super.initState();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
Future<void> _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) {
if (widget.type == ScanType.Onboarding) {
final json = jsonDecode(result);
Navigator.pop(context, json);
}
}
// 人脸识别跳转
void goToFace(Map<String, dynamic> stuInfo) async {
print('navigate to face with $stuInfo');
// final passed = await pushPage<bool>(
// FaceRecognitionPage(
// studentId: stuInfo['STUDENT_ID'],
// data: {
// 'VIDEOCOURSEWARE_ID': stuInfo['VIDEOCOURSEWARE_ID'],
// 'CURRICULUM_ID': stuInfo['CURRICULUM_ID'],
// 'CHAPTER_ID': stuInfo['CHAPTER_ID'],
// 'CLASS_ID': stuInfo['CLASS_ID'],
// 'STUDENT_ID':stuInfo['STUDENT_ID'],
// },
// mode: FaceMode.scan,
// ),
// context,
// );
// if (passed == true) {
// ToastUtil.showSuccess(context, '验证成功');
// Navigator.pop(context);
// } else {
// ToastUtil.showError(context, '验证失败');
// Navigator.pop(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),
),
),
),
);
}
}