ver1.00.00

update2
This commit is contained in:
spasolreisa
2026-04-21 01:28:53 +08:00
parent f5f62c828d
commit 603772bc81
11 changed files with 346 additions and 286 deletions

View File

@@ -2,8 +2,10 @@ import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:google_mlkit_barcode_scanning/google_mlkit_barcode_scanning.dart';
import 'package:provider/provider.dart';
// 新增导入
import 'package:zxing2/qrcode.dart';
import 'package:image/image.dart' as img;
import '../../providers/user_provider.dart';
@@ -22,19 +24,13 @@ class _BindAccountPageState extends State<BindAccountPage> {
String? _statusMessage;
File? _selectedImageFile; // 用户选择的原始二维码截图
// 条码扫描器
final BarcodeScanner _barcodeScanner = BarcodeScanner(
formats: [BarcodeFormat.qrCode],
);
@override
void dispose() {
_qrContentController.dispose();
_barcodeScanner.close();
super.dispose();
}
// 1. 选择图片并识别二维码
// 1. 选择图片并识别二维码 (使用 zxing2替代 ML Kit)
Future<void> _pickImageAndScan() async {
try {
final XFile? pickedFile = await _picker.pickImage(source: ImageSource.gallery);
@@ -42,33 +38,79 @@ class _BindAccountPageState extends State<BindAccountPage> {
setState(() {
_isProcessing = true;
_statusMessage = "正在识别二维码...";
_statusMessage = "正在加载图片...";
_selectedImageFile = File(pickedFile.path);
});
final inputImage = InputImage.fromFilePath(pickedFile.path);
final List<Barcode> barcodes = await _barcodeScanner.processImage(inputImage);
// 读取图片字节
final bytes = await pickedFile.readAsBytes();
// 使用 image 库解码图片为 RGB 数据
final img.Image? decodedImage = img.decodeImage(bytes);
if (decodedImage == null) {
throw Exception("无法解析图片格式");
}
setState(() {
_isProcessing = false;
_statusMessage = "正在解析二维码...";
});
if (barcodes.isNotEmpty && barcodes.first.rawValue != null) {
final String code = barcodes.first.rawValue!;
final reader = QRCodeReader();
try {
// 【修复点开始】
// 1. 获取 Uint8List (RGBA 格式)
final Uint8List rgbaBytes = decodedImage.getBytes(order: img.ChannelOrder.rgba);
// 2. 将 Uint8List 转换为 Int32List
// 注意:这需要底层字节序匹配。大多数现代移动设备是 Little Endian。
// Uint8List 的长度必须是 4 的倍数RGBA 每个像素4字节这通常成立。
final Int32List pixels = Int32List.view(rgbaBytes.buffer);
// 3. 创建 LuminanceSource
// ZXing 的 RGBLuminanceSource 期望 Int32List其中每个 int 代表一个像素 (0xAARRGGBB 或类似格式)
// 由于 image 库给出的是 RGBA (0xRR, 0xGG, 0xBB, 0xAA),直接转换可能导致颜色通道错位,
// 但 ZXing 主要关注亮度(灰度),通常 RGBA 直接转 Int32 也能被正确二值化识别,
// 如果识别失败,可能需要手动重排字节为 ARGB。
final source = RGBLuminanceSource(
decodedImage.width,
decodedImage.height,
pixels
);
// 【修复点结束】
final binarizer = HybridBinarizer(source);
final bitmap = BinaryBitmap(binarizer);
final result = reader.decode(bitmap);
if (result != null && result.text != null) {
setState(() {
_qrContentController.text = result.text!;
_statusMessage = "识别成功";
});
} else {
setState(() {
_statusMessage = "未识别到二维码,请手动输入";
});
}
} catch (e) {
// 捕获 zxing 内部的 NotFoundException 等
print("ZXing Error: $e"); // 调试用
setState(() {
_qrContentController.text = code;
_statusMessage = "识别成功";
});
} else {
setState(() {
_statusMessage = "未识别到二维码,请手动输入";
_statusMessage = "未在图中找到有效二维码";
});
}
} catch (e) {
setState(() {
_statusMessage = "识别出错: $e";
});
} finally {
setState(() {
_isProcessing = false;
_statusMessage = "识别失败: $e";
});
}
}
@@ -92,7 +134,9 @@ class _BindAccountPageState extends State<BindAccountPage> {
});
try {
// 下载背景图用于隐写
final httpClient = HttpClient();
// 建议在实际生产环境中添加 timeout
final request = await httpClient.getUrl(Uri.parse('https://union.godserver.cn/assets/jpeg/20180621142015_5FmGZ-wYXkyL4y.jpeg'));
final response = await request.close();
final bytes = <int>[];
@@ -101,6 +145,9 @@ class _BindAccountPageState extends State<BindAccountPage> {
}
imageBytes = Uint8List.fromList(bytes);
// 关闭 client 以释放资源
httpClient.close();
if (imageBytes == null || imageBytes.isEmpty) throw Exception("图片数据无效");
setState(() {
@@ -108,7 +155,6 @@ class _BindAccountPageState extends State<BindAccountPage> {
});
// 调用 Provider
// 注意:这里 segaId 参数其实传的是 QR Code 的内容
final userProvider = context.read<UserProvider>();
final result = await userProvider.uploadStegImage(imageBytes, segaId: qrContent);
@@ -122,10 +168,13 @@ class _BindAccountPageState extends State<BindAccountPage> {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("${result['msg']}"), backgroundColor: Colors.green, duration: const Duration(seconds: 5)),
);
// 绑定成功后,通常建议刷新用户信息
// 绑定成功后,刷新用户信息
await userProvider.initUser();
// 可选:清空输入或跳转
// _qrContentController.clear();
// Navigator.of(context).pop();
} else {
// 后端返回 500 或其他错误
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("${result['msg']}"), backgroundColor: Colors.red, duration: const Duration(seconds: 5)),
);
@@ -276,7 +325,7 @@ class _BindAccountPageState extends State<BindAccountPage> {
child: Text(
_statusMessage!,
style: TextStyle(
color: _statusMessage!.contains("失败") || _statusMessage!.contains("错误") ? Colors.redAccent : Colors.white70,
color: _statusMessage!.contains("失败") || _statusMessage!.contains("错误") || _statusMessage!.contains("未识别") ? Colors.redAccent : Colors.white70,
),
textAlign: TextAlign.center,
),